mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-11 12:57:46 +00:00
Compare commits
113 Commits
@vercel/py
...
@vercel/bu
| 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 | ||
|
|
d5537500d8 | ||
|
|
4b1736b2f2 | ||
|
|
9da1c6fa66 | ||
|
|
a6c320846e | ||
|
|
4973814978 | ||
|
|
f1289ff263 | ||
|
|
3ff93279cd | ||
|
|
58f9d649a8 | ||
|
|
cadc082ad1 |
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
|
git clone https://github.com/vercel/vercel
|
||||||
cd vercel
|
cd vercel
|
||||||
|
corepack enable
|
||||||
yarn install
|
yarn install
|
||||||
yarn bootstrap
|
yarn bootstrap
|
||||||
yarn build
|
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:
|
tags:
|
||||||
- '!*'
|
- '!*'
|
||||||
|
|
||||||
|
env:
|
||||||
|
TURBO_REMOTE_ONLY: 'true'
|
||||||
|
TURBO_TEAM: 'vercel'
|
||||||
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
name: Publish
|
name: Publish
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
- name: Check Release
|
- name: Check Release
|
||||||
id: check-release
|
id: check-release
|
||||||
run: |
|
run: |
|
||||||
@@ -32,8 +39,10 @@ jobs:
|
|||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
|
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
|
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
|
||||||
with:
|
with:
|
||||||
node-version: 14
|
node-version: 14
|
||||||
|
cache: 'yarn'
|
||||||
- name: Install
|
- name: Install
|
||||||
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
|
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
|
||||||
run: yarn install --check-files --frozen-lockfile --network-timeout 1000000
|
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:
|
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:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: CLI
|
name: CLI
|
||||||
@@ -19,28 +28,20 @@ jobs:
|
|||||||
node: [14]
|
node: [14]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Conditionally set remote env
|
- uses: actions/checkout@v3
|
||||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
with:
|
||||||
run: |
|
fetch-depth: 2
|
||||||
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
|
- uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.13.15'
|
go-version: '1.13.15'
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
|
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
- uses: actions/checkout@v3
|
cache: 'yarn'
|
||||||
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
|
|
||||||
- run: yarn install --network-timeout 1000000 --frozen-lockfile
|
- run: yarn install --network-timeout 1000000 --frozen-lockfile
|
||||||
- run: yarn run build
|
- run: yarn run build
|
||||||
- run: yarn test-integration-cli
|
- run: yarn test-integration-cli
|
||||||
env:
|
env:
|
||||||
VERCEL_TEAM_TOKEN: ${{ secrets.VERCEL_TEAM_TOKEN }}
|
VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }}
|
||||||
VERCEL_REGISTRATION_URL: ${{ secrets.VERCEL_REGISTRATION_URL }}
|
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:
|
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:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: Unit
|
name: Unit
|
||||||
@@ -19,25 +28,17 @@ jobs:
|
|||||||
node: [14]
|
node: [14]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
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
|
- uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.13.15'
|
go-version: '1.13.15'
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node }}
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 100
|
fetch-depth: 2
|
||||||
- run: git --version
|
- uses: actions/setup-node@v3
|
||||||
- run: git fetch origin main --depth=100
|
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
|
||||||
- run: git fetch origin ${{ github.ref }} --depth=100
|
with:
|
||||||
- run: git diff origin/main...HEAD --name-only
|
node-version: ${{ matrix.node }}
|
||||||
|
cache: 'yarn'
|
||||||
- run: yarn install --network-timeout 1000000 --frozen-lockfile
|
- run: yarn install --network-timeout 1000000 --frozen-lockfile
|
||||||
- run: yarn run build
|
- run: yarn run build
|
||||||
- run: yarn run lint
|
- run: yarn run lint
|
||||||
|
|||||||
26
.github/workflows/test.yml
vendored
26
.github/workflows/test.yml
vendored
@@ -10,6 +10,13 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
NODE_VERSION: '14'
|
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:
|
jobs:
|
||||||
setup:
|
setup:
|
||||||
@@ -20,9 +27,13 @@ jobs:
|
|||||||
dplUrl: ${{ steps.waitForTarball.outputs.url }}
|
dplUrl: ${{ steps.waitForTarball.outputs.url }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- run: git --version
|
with:
|
||||||
- run: git fetch origin main
|
fetch-depth: 2
|
||||||
|
- uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: '1.13.15'
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
|
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
@@ -52,12 +63,6 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
include: ${{ fromJson(needs.setup.outputs['tests']) }}
|
include: ${{ fromJson(needs.setup.outputs['tests']) }}
|
||||||
steps:
|
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
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
@@ -65,6 +70,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: '1.13.15'
|
go-version: '1.13.15'
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
|
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
@@ -84,8 +90,8 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
VERCEL_CLI_VERSION: ${{ needs.setup.outputs.dplUrl }}/tarballs/vercel.tgz
|
VERCEL_CLI_VERSION: ${{ needs.setup.outputs.dplUrl }}/tarballs/vercel.tgz
|
||||||
VERCEL_TEAM_TOKEN: ${{ secrets.VERCEL_TEAM_TOKEN }}
|
VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }}
|
||||||
VERCEL_REGISTRATION_URL: ${{ secrets.VERCEL_REGISTRATION_URL }}
|
VERCEL_TEST_REGISTRATION_URL: ${{ secrets.VERCEL_TEST_REGISTRATION_URL }}
|
||||||
FORCE_COLOR: '1'
|
FORCE_COLOR: '1'
|
||||||
|
|
||||||
conclusion:
|
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:
|
This is an abstract enumeration type that is implemented by one of the following possible `String` values:
|
||||||
|
|
||||||
|
- `nodejs16.x`
|
||||||
- `nodejs14.x`
|
- `nodejs14.x`
|
||||||
- `nodejs12.x`
|
|
||||||
- `go1.x`
|
- `go1.x`
|
||||||
- `java11`
|
- `java11`
|
||||||
- `python3.9`
|
- `python3.9`
|
||||||
|
|||||||
@@ -19,11 +19,9 @@
|
|||||||
|
|
||||||
## Vercel
|
## 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 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.
|
||||||
|
|
||||||
We make it easy for frontend teams to **develop, preview, and ship** delightful user experiences, where performance is the default.
|
|
||||||
|
|
||||||
## Deploy
|
## Deploy
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
import { join, dirname } from 'path';
|
import { join, dirname } from 'path';
|
||||||
import execa from 'execa';
|
|
||||||
import { getExampleList } from '../examples/example-list';
|
import { getExampleList } from '../examples/example-list';
|
||||||
import { mapOldToNew } from '../examples/map-old-to-new';
|
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.rm(pubDir, { recursive: true, force: true });
|
||||||
await fs.mkdir(pubDir);
|
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 examples = await getExampleList();
|
||||||
const pathListAll = join(pubDir, 'list-all.json');
|
const pathListAll = join(pubDir, 'list-all.json');
|
||||||
await fs.writeFile(pathListAll, JSON.stringify(examples));
|
await fs.writeFile(pathListAll, JSON.stringify(examples));
|
||||||
@@ -41,10 +52,6 @@ async function main() {
|
|||||||
JSON.stringify([...existingExamples, ...oldExamples])
|
JSON.stringify([...existingExamples, ...oldExamples])
|
||||||
);
|
);
|
||||||
|
|
||||||
const { stdout: sha } = await execa('git', ['rev-parse', '--short', 'HEAD'], {
|
|
||||||
cwd: repoRoot,
|
|
||||||
});
|
|
||||||
|
|
||||||
const tarballsDir = join(pubDir, 'tarballs');
|
const tarballsDir = join(pubDir, 'tarballs');
|
||||||
const packagesDir = join(repoRoot, 'packages');
|
const packagesDir = join(repoRoot, 'packages');
|
||||||
const packages = await fs.readdir(packagesDir);
|
const packages = await fs.readdir(packagesDir);
|
||||||
@@ -55,12 +62,21 @@ async function main() {
|
|||||||
'utf-8'
|
'utf-8'
|
||||||
);
|
);
|
||||||
const packageJson = JSON.parse(packageJsonRaw);
|
const packageJson = JSON.parse(packageJsonRaw);
|
||||||
const tarballName = `${packageJson.name
|
const files = await fs.readdir(fullDir);
|
||||||
.replace('@', '')
|
const tarballName = files.find(f => /^vercel-.+\.tgz$/.test(f));
|
||||||
.replace('/', '-')}-v${packageJson.version}-${sha.trim()}.tgz`;
|
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`);
|
const destTarballPath = join(tarballsDir, `${packageJson.name}.tgz`);
|
||||||
await fs.mkdir(dirname(destTarballPath), { recursive: true });
|
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.');
|
console.log('Completed building static frontend.');
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ const frameworks = (_frameworks as Framework[])
|
|||||||
defaultRoutes: undefined,
|
defaultRoutes: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (framework.logo) {
|
|
||||||
framework.logo = `https://assets.vercel.com/zeit-inc/image/fetch/${framework.logo}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return framework;
|
return framework;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "12.2.5",
|
"next": "12.3.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0"
|
"react-dom": "18.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "8.22.0",
|
"eslint": "8.23.1",
|
||||||
"eslint-config-next": "12.2.5"
|
"eslint-config-next": "12.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,28 +3,28 @@
|
|||||||
|
|
||||||
|
|
||||||
"@babel/runtime-corejs3@^7.10.2":
|
"@babel/runtime-corejs3@^7.10.2":
|
||||||
version "7.18.9"
|
version "7.19.1"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.18.9.tgz#7bacecd1cb2dd694eacd32a91fcf7021c20770ae"
|
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.19.1.tgz#f0cbbe7edda7c4109cd253bb1dee99aba4594ad9"
|
||||||
integrity sha512-qZEWeccZCrHA2Au4/X05QW5CMdm4VjUDCrGq5gf1ZDcM4hRqreKrtwAn7yci9zfgAS9apvnsFXiGBHBAxZdK9A==
|
integrity sha512-j2vJGnkopRzH+ykJ8h68wrHnEUmtK//E723jjixiAl/PPf6FhqY/vYRcMVlNydRKQjQsTsYEjpx+DZMIvnGk/g==
|
||||||
dependencies:
|
dependencies:
|
||||||
core-js-pure "^3.20.2"
|
core-js-pure "^3.25.1"
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@babel/runtime@^7.10.2", "@babel/runtime@^7.18.9":
|
"@babel/runtime@^7.10.2", "@babel/runtime@^7.18.9":
|
||||||
version "7.18.9"
|
version "7.19.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259"
|
||||||
integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==
|
integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@eslint/eslintrc@^1.3.0":
|
"@eslint/eslintrc@^1.3.2":
|
||||||
version "1.3.0"
|
version "1.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
|
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.2.tgz#58b69582f3b7271d8fa67fe5251767a5b38ea356"
|
||||||
integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==
|
integrity sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv "^6.12.4"
|
ajv "^6.12.4"
|
||||||
debug "^4.3.2"
|
debug "^4.3.2"
|
||||||
espree "^9.3.2"
|
espree "^9.4.0"
|
||||||
globals "^13.15.0"
|
globals "^13.15.0"
|
||||||
ignore "^5.2.0"
|
ignore "^5.2.0"
|
||||||
import-fresh "^3.2.1"
|
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"
|
resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d"
|
||||||
integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==
|
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":
|
"@humanwhocodes/object-schema@^1.2.1":
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
|
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
|
||||||
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
|
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
|
||||||
|
|
||||||
"@next/env@12.2.5":
|
"@next/env@12.3.1":
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.2.5.tgz#d908c57b35262b94db3e431e869b72ac3e1ad3e3"
|
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.3.1.tgz#18266bd92de3b4aa4037b1927aa59e6f11879260"
|
||||||
integrity sha512-vLPLV3cpPGjUPT3PjgRj7e3nio9t6USkuew3JE/jMeon/9Mvp1WyR18v3iwnCuX7eUAm1HmAbJHHLAbcu/EJcw==
|
integrity sha512-9P9THmRFVKGKt9DYqeC2aKIxm8rlvkK38V1P1sRE7qyoPBIs8l9oo79QoSdPtOWfzkbDAVUqvbQGgTMsb8BtJg==
|
||||||
|
|
||||||
"@next/eslint-plugin-next@12.2.5":
|
"@next/eslint-plugin-next@12.3.1":
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.2.5.tgz#4f3acccd2ed4f9300fbf9fd480cc8a0b261889a8"
|
resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.3.1.tgz#b821f27b0f175954d8d18e5d323fce040ecc79a6"
|
||||||
integrity sha512-VBjVbmqEzGiOTBq4+wpeVXt/KgknnGB6ahvC/AxiIGnN93/RCSyXhFRI4uSfftM2Ba3w7ZO7076bfKasZsA0fw==
|
integrity sha512-sw+lTf6r6P0j+g/n9y4qdWWI2syPqZx+uc0+B/fRENqfR3KpSid6MIKqc9gNwGhJASazEQ5b3w8h4cAET213jw==
|
||||||
dependencies:
|
dependencies:
|
||||||
glob "7.1.7"
|
glob "7.1.7"
|
||||||
|
|
||||||
"@next/swc-android-arm-eabi@12.2.5":
|
"@next/swc-android-arm-eabi@12.3.1":
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.2.5.tgz#903a5479ab4c2705d9c08d080907475f7bacf94d"
|
resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.3.1.tgz#b15ce8ad376102a3b8c0f3c017dde050a22bb1a3"
|
||||||
integrity sha512-cPWClKxGhgn2dLWnspW+7psl3MoLQUcNqJqOHk2BhNcou9ARDtC0IjQkKe5qcn9qg7I7U83Gp1yh2aesZfZJMA==
|
integrity sha512-i+BvKA8tB//srVPPQxIQN5lvfROcfv4OB23/L1nXznP+N/TyKL8lql3l7oo2LNhnH66zWhfoemg3Q4VJZSruzQ==
|
||||||
|
|
||||||
"@next/swc-android-arm64@12.2.5":
|
"@next/swc-android-arm64@12.3.1":
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.2.5.tgz#2f9a98ec4166c7860510963b31bda1f57a77c792"
|
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.3.1.tgz#85d205f568a790a137cb3c3f720d961a2436ac9c"
|
||||||
integrity sha512-vMj0efliXmC5b7p+wfcQCX0AfU8IypjkzT64GiKJD9PgiA3IILNiGJr1fw2lyUDHkjeWx/5HMlMEpLnTsQslwg==
|
integrity sha512-CmgU2ZNyBP0rkugOOqLnjl3+eRpXBzB/I2sjwcGZ7/Z6RcUJXK5Evz+N0ucOxqE4cZ3gkTeXtSzRrMK2mGYV8Q==
|
||||||
|
|
||||||
"@next/swc-darwin-arm64@12.2.5":
|
"@next/swc-darwin-arm64@12.3.1":
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.2.5.tgz#31b1c3c659d54be546120c488a1e1bad21c24a1d"
|
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.3.1.tgz#b105457d6760a7916b27e46c97cb1a40547114ae"
|
||||||
integrity sha512-VOPWbO5EFr6snla/WcxUKtvzGVShfs302TEMOtzYyWni6f9zuOetijJvVh9CCTzInnXAZMtHyNhefijA4HMYLg==
|
integrity sha512-hT/EBGNcu0ITiuWDYU9ur57Oa4LybD5DOQp4f22T6zLfpoBMfBibPtR8XktXmOyFHrL/6FC2p9ojdLZhWhvBHg==
|
||||||
|
|
||||||
"@next/swc-darwin-x64@12.2.5":
|
"@next/swc-darwin-x64@12.3.1":
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.2.5.tgz#2e44dd82b2b7fef88238d1bc4d3bead5884cedfd"
|
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.3.1.tgz#6947b39082271378896b095b6696a7791c6e32b1"
|
||||||
integrity sha512-5o8bTCgAmtYOgauO/Xd27vW52G2/m3i5PX7MUYePquxXAnX73AAtqA3WgPXBRitEB60plSKZgOTkcpqrsh546A==
|
integrity sha512-9S6EVueCVCyGf2vuiLiGEHZCJcPAxglyckTZcEwLdJwozLqN0gtS0Eq0bQlGS3dH49Py/rQYpZ3KVWZ9BUf/WA==
|
||||||
|
|
||||||
"@next/swc-freebsd-x64@12.2.5":
|
"@next/swc-freebsd-x64@12.3.1":
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.2.5.tgz#e24e75d8c2581bfebc75e4f08f6ddbd116ce9dbd"
|
resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.3.1.tgz#2b6c36a4d84aae8b0ea0e0da9bafc696ae27085a"
|
||||||
integrity sha512-yYUbyup1JnznMtEBRkK4LT56N0lfK5qNTzr6/DEyDw5TbFVwnuy2hhLBzwCBkScFVjpFdfiC6SQAX3FrAZzuuw==
|
integrity sha512-qcuUQkaBZWqzM0F1N4AkAh88lLzzpfE6ImOcI1P6YeyJSsBmpBIV8o70zV+Wxpc26yV9vpzb+e5gCyxNjKJg5Q==
|
||||||
|
|
||||||
"@next/swc-linux-arm-gnueabihf@12.2.5":
|
"@next/swc-linux-arm-gnueabihf@12.3.1":
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.2.5.tgz#46d8c514d834d2b5f67086013f0bd5e3081e10b9"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.3.1.tgz#6e421c44285cfedac1f4631d5de330dd60b86298"
|
||||||
integrity sha512-2ZE2/G921Acks7UopJZVMgKLdm4vN4U0yuzvAMJ6KBavPzqESA2yHJlm85TV/K9gIjKhSk5BVtauIUntFRP8cg==
|
integrity sha512-diL9MSYrEI5nY2wc/h/DBewEDUzr/DqBjIgHJ3RUNtETAOB3spMNHvJk2XKUDjnQuluLmFMloet9tpEqU2TT9w==
|
||||||
|
|
||||||
"@next/swc-linux-arm64-gnu@12.2.5":
|
"@next/swc-linux-arm64-gnu@12.3.1":
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.2.5.tgz#91f725ac217d3a1f4f9f53b553615ba582fd3d9f"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.3.1.tgz#8863f08a81f422f910af126159d2cbb9552ef717"
|
||||||
integrity sha512-/I6+PWVlz2wkTdWqhlSYYJ1pWWgUVva6SgX353oqTh8njNQp1SdFQuWDqk8LnM6ulheVfSsgkDzxrDaAQZnzjQ==
|
integrity sha512-o/xB2nztoaC7jnXU3Q36vGgOolJpsGG8ETNjxM1VAPxRwM7FyGCPHOMk1XavG88QZSQf+1r+POBW0tLxQOJ9DQ==
|
||||||
|
|
||||||
"@next/swc-linux-arm64-musl@12.2.5":
|
"@next/swc-linux-arm64-musl@12.3.1":
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.2.5.tgz#e627e8c867920995810250303cd9b8e963598383"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.3.1.tgz#0038f07cf0b259d70ae0c80890d826dfc775d9f3"
|
||||||
integrity sha512-LPQRelfX6asXyVr59p5sTpx5l+0yh2Vjp/R8Wi4X9pnqcayqT4CUJLiHqCvZuLin3IsFdisJL0rKHMoaZLRfmg==
|
integrity sha512-2WEasRxJzgAmP43glFNhADpe8zB7kJofhEAVNbDJZANp+H4+wq+/cW1CdDi8DqjkShPEA6/ejJw+xnEyDID2jg==
|
||||||
|
|
||||||
"@next/swc-linux-x64-gnu@12.2.5":
|
"@next/swc-linux-x64-gnu@12.3.1":
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.2.5.tgz#83a5e224fbc4d119ef2e0f29d0d79c40cc43887e"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.3.1.tgz#c66468f5e8181ffb096c537f0dbfb589baa6a9c1"
|
||||||
integrity sha512-0szyAo8jMCClkjNK0hknjhmAngUppoRekW6OAezbEYwHXN/VNtsXbfzgYOqjKWxEx3OoAzrT3jLwAF0HdX2MEw==
|
integrity sha512-JWEaMyvNrXuM3dyy9Pp5cFPuSSvG82+yABqsWugjWlvfmnlnx9HOQZY23bFq3cNghy5V/t0iPb6cffzRWylgsA==
|
||||||
|
|
||||||
"@next/swc-linux-x64-musl@12.2.5":
|
"@next/swc-linux-x64-musl@12.3.1":
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.2.5.tgz#be700d48471baac1ec2e9539396625584a317e95"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.3.1.tgz#c6269f3e96ac0395bc722ad97ce410ea5101d305"
|
||||||
integrity sha512-zg/Y6oBar1yVnW6Il1I/08/2ukWtOG6s3acdJdEyIdsCzyQi4RLxbbhkD/EGQyhqBvd3QrC6ZXQEXighQUAZ0g==
|
integrity sha512-xoEWQQ71waWc4BZcOjmatuvPUXKTv6MbIFzpm4LFeCHsg2iwai0ILmNXf81rJR+L1Wb9ifEke2sQpZSPNz1Iyg==
|
||||||
|
|
||||||
"@next/swc-win32-arm64-msvc@12.2.5":
|
"@next/swc-win32-arm64-msvc@12.3.1":
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.2.5.tgz#a93e958133ad3310373fda33a79aa10af2a0aa97"
|
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.3.1.tgz#83c639ee969cee36ce247c3abd1d9df97b5ecade"
|
||||||
integrity sha512-3/90DRNSqeeSRMMEhj4gHHQlLhhKg5SCCoYfE3kBjGpE63EfnblYUqsszGGZ9ekpKL/R4/SGB40iCQr8tR5Jiw==
|
integrity sha512-hswVFYQYIeGHE2JYaBVtvqmBQ1CppplQbZJS/JgrVI3x2CurNhEkmds/yqvDONfwfbttTtH4+q9Dzf/WVl3Opw==
|
||||||
|
|
||||||
"@next/swc-win32-ia32-msvc@12.2.5":
|
"@next/swc-win32-ia32-msvc@12.3.1":
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.2.5.tgz#4f5f7ba0a98ff89a883625d4af0125baed8b2e19"
|
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.3.1.tgz#52995748b92aa8ad053440301bc2c0d9fbcf27c2"
|
||||||
integrity sha512-hGLc0ZRAwnaPL4ulwpp4D2RxmkHQLuI8CFOEEHdzZpS63/hMVzv81g8jzYA0UXbb9pus/iTc3VRbVbAM03SRrw==
|
integrity sha512-Kny5JBehkTbKPmqulr5i+iKntO5YMP+bVM8Hf8UAmjSMVo3wehyLVc9IZkNmcbxi+vwETnQvJaT5ynYBkJ9dWA==
|
||||||
|
|
||||||
"@next/swc-win32-x64-msvc@12.2.5":
|
"@next/swc-win32-x64-msvc@12.3.1":
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.2.5.tgz#20fed129b04a0d3f632c6d0de135345bb623b1e4"
|
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.3.1.tgz#27d71a95247a9eaee03d47adee7e3bd594514136"
|
||||||
integrity sha512-7h5/ahY7NeaO2xygqVrSG/Y8Vs4cdjxIjowTZ5W6CKoTKn7tmnuxlUc2h74x06FKmbhAd9agOjr/AOKyxYYm9Q==
|
integrity sha512-W1ijvzzg+kPEX6LAc+50EYYSEo0FVu7dmTE+t+DM4iOLqgGHoW9uYSz9wCVdkXOEEMP9xhXfGpcSxsfDucyPkA==
|
||||||
|
|
||||||
"@nodelib/fs.scandir@2.1.5":
|
"@nodelib/fs.scandir@2.1.5":
|
||||||
version "2.1.5"
|
version "2.1.5"
|
||||||
@@ -150,14 +155,14 @@
|
|||||||
fastq "^1.6.0"
|
fastq "^1.6.0"
|
||||||
|
|
||||||
"@rushstack/eslint-patch@^1.1.3":
|
"@rushstack/eslint-patch@^1.1.3":
|
||||||
version "1.1.4"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.4.tgz#0c8b74c50f29ee44f423f7416829c0bf8bb5eb27"
|
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728"
|
||||||
integrity sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==
|
integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==
|
||||||
|
|
||||||
"@swc/helpers@0.4.3":
|
"@swc/helpers@0.4.11":
|
||||||
version "0.4.3"
|
version "0.4.11"
|
||||||
resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.3.tgz#16593dfc248c53b699d4b5026040f88ddb497012"
|
resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.11.tgz#db23a376761b3d31c26502122f349a21b592c8de"
|
||||||
integrity sha512-6JrF+fdUK2zbGpJIlN7G3v966PQjyx/dPt1T9km2wj+EUBqgrxCk3uX4Kct16MIm9gGxfKRcfax2hVf5jvlTzA==
|
integrity sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw==
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib "^2.4.0"
|
tslib "^2.4.0"
|
||||||
|
|
||||||
@@ -167,47 +172,47 @@
|
|||||||
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
|
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
|
||||||
|
|
||||||
"@typescript-eslint/parser@^5.21.0":
|
"@typescript-eslint/parser@^5.21.0":
|
||||||
version "5.34.0"
|
version "5.38.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.34.0.tgz#ca710858ea85dbfd30c9b416a335dc49e82dbc07"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.38.0.tgz#5a59a1ff41a7b43aacd1bb2db54f6bf1c02b2ff8"
|
||||||
integrity sha512-SZ3NEnK4usd2CXkoV3jPa/vo1mWX1fqRyIVUQZR4As1vyp4fneknBNJj+OFtV8WAVgGf+rOHMSqQbs2Qn3nFZQ==
|
integrity sha512-/F63giJGLDr0ms1Cr8utDAxP2SPiglaD6V+pCOcG35P2jCqdfR7uuEhz1GIC3oy4hkUF8xA1XSXmd9hOh/a5EA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/scope-manager" "5.34.0"
|
"@typescript-eslint/scope-manager" "5.38.0"
|
||||||
"@typescript-eslint/types" "5.34.0"
|
"@typescript-eslint/types" "5.38.0"
|
||||||
"@typescript-eslint/typescript-estree" "5.34.0"
|
"@typescript-eslint/typescript-estree" "5.38.0"
|
||||||
debug "^4.3.4"
|
debug "^4.3.4"
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager@5.34.0":
|
"@typescript-eslint/scope-manager@5.38.0":
|
||||||
version "5.34.0"
|
version "5.38.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.34.0.tgz#14efd13dc57602937e25f188fd911f118781e527"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.38.0.tgz#8f0927024b6b24e28671352c93b393a810ab4553"
|
||||||
integrity sha512-HNvASMQlah5RsBW6L6c7IJ0vsm+8Sope/wu5sEAf7joJYWNb1LDbJipzmdhdUOnfrDFE6LR1j57x1EYVxrY4ow==
|
integrity sha512-ByhHIuNyKD9giwkkLqzezZ9y5bALW8VNY6xXcP+VxoH4JBDKjU5WNnsiD4HJdglHECdV+lyaxhvQjTUbRboiTA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "5.34.0"
|
"@typescript-eslint/types" "5.38.0"
|
||||||
"@typescript-eslint/visitor-keys" "5.34.0"
|
"@typescript-eslint/visitor-keys" "5.38.0"
|
||||||
|
|
||||||
"@typescript-eslint/types@5.34.0":
|
"@typescript-eslint/types@5.38.0":
|
||||||
version "5.34.0"
|
version "5.38.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.34.0.tgz#217bf08049e9e7b86694d982e88a2c1566330c78"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.38.0.tgz#8cd15825e4874354e31800dcac321d07548b8a5f"
|
||||||
integrity sha512-49fm3xbbUPuzBIOcy2CDpYWqy/X7VBkxVN+DC21e0zIm3+61Z0NZi6J9mqPmSW1BDVk9FIOvuCFyUPjXz93sjA==
|
integrity sha512-HHu4yMjJ7i3Cb+8NUuRCdOGu2VMkfmKyIJsOr9PfkBVYLYrtMCK/Ap50Rpov+iKpxDTfnqvDbuPLgBE5FwUNfA==
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@5.34.0":
|
"@typescript-eslint/typescript-estree@5.38.0":
|
||||||
version "5.34.0"
|
version "5.38.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.34.0.tgz#ba7b83f4bf8ccbabf074bbf1baca7a58de3ccb9a"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.38.0.tgz#89f86b2279815c6fb7f57d68cf9b813f0dc25d98"
|
||||||
integrity sha512-mXHAqapJJDVzxauEkfJI96j3D10sd567LlqroyCeJaHnu42sDbjxotGb3XFtGPYKPD9IyLjhsoULML1oI3M86A==
|
integrity sha512-6P0RuphkR+UuV7Avv7MU3hFoWaGcrgOdi8eTe1NwhMp2/GjUJoODBTRWzlHpZh6lFOaPmSvgxGlROa0Sg5Zbyg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "5.34.0"
|
"@typescript-eslint/types" "5.38.0"
|
||||||
"@typescript-eslint/visitor-keys" "5.34.0"
|
"@typescript-eslint/visitor-keys" "5.38.0"
|
||||||
debug "^4.3.4"
|
debug "^4.3.4"
|
||||||
globby "^11.1.0"
|
globby "^11.1.0"
|
||||||
is-glob "^4.0.3"
|
is-glob "^4.0.3"
|
||||||
semver "^7.3.7"
|
semver "^7.3.7"
|
||||||
tsutils "^3.21.0"
|
tsutils "^3.21.0"
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys@5.34.0":
|
"@typescript-eslint/visitor-keys@5.38.0":
|
||||||
version "5.34.0"
|
version "5.38.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.34.0.tgz#d0fb3e31033e82ddd5de048371ad39eb342b2d40"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.38.0.tgz#60591ca3bf78aa12b25002c0993d067c00887e34"
|
||||||
integrity sha512-O1moYjOSrab0a2fUvFpsJe0QHtvTC+cR+ovYpgKrAVXzqQyc74mv76TgY6z+aEtjQE2vgZux3CQVtGryqdcOAw==
|
integrity sha512-MxnrdIyArnTi+XyFLR+kt/uNAcdOnmT+879os7qDRI+EYySR4crXJq9BXPfRzzLGq0wgxkwidrCJ9WCAoacm1w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/types" "5.34.0"
|
"@typescript-eslint/types" "5.38.0"
|
||||||
eslint-visitor-keys "^3.3.0"
|
eslint-visitor-keys "^3.3.0"
|
||||||
|
|
||||||
acorn-jsx@^5.3.2:
|
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"
|
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||||
|
|
||||||
caniuse-lite@^1.0.30001332:
|
caniuse-lite@^1.0.30001406:
|
||||||
version "1.0.30001382"
|
version "1.0.30001406"
|
||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001382.tgz#4d37f0d0b6fffb826c8e5e1c0f4bf8ce592db949"
|
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001406.tgz#d0146e7919635479f873b4507517b627f66ab269"
|
||||||
integrity sha512-2rtJwDmSZ716Pxm1wCtbPvHtbDWAreTPxXbkc5RkKglow3Ig/4GNGazDI9/BVnXbG/wnv6r3B5FEbkfg9OcTGg==
|
integrity sha512-bWTlaXUy/rq0BBtYShc/jArYfBPjEV95euvZ8JVtO43oQExEN/WquoqpufFjNu4kSpi5cy5kMbNvzztWDfv1Jg==
|
||||||
|
|
||||||
chalk@^4.0.0:
|
chalk@^4.0.0:
|
||||||
version "4.1.2"
|
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"
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
||||||
|
|
||||||
core-js-pure@^3.20.2:
|
core-js-pure@^3.25.1:
|
||||||
version "3.24.1"
|
version "3.25.2"
|
||||||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.24.1.tgz#8839dde5da545521bf282feb7dc6d0b425f39fd3"
|
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.25.2.tgz#44a4fd873bdd4fecf6ca11512bcefedbe87e744a"
|
||||||
integrity sha512-r1nJk41QLLPyozHUUPmILCEMtMw24NG4oWK6RbsDdjzQgg9ZvrUsPBj1MnG0wXXp1DCDU6j+wUvEmBSrtRbLXg==
|
integrity sha512-ItD7YpW1cUB4jaqFLZXe1AXkyqIxz6GqPnsDV4uF4hVcWh/WAGIqSqw5p0/WdsILM0Xht9s3Koyw05R3K6RtiA==
|
||||||
|
|
||||||
cross-spawn@^7.0.2:
|
cross-spawn@^7.0.2:
|
||||||
version "7.0.3"
|
version "7.0.3"
|
||||||
@@ -449,15 +454,15 @@ emoji-regex@^9.2.2:
|
|||||||
integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
|
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:
|
es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5:
|
||||||
version "1.20.1"
|
version "1.20.2"
|
||||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814"
|
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.2.tgz#8495a07bc56d342a3b8ea3ab01bd986700c2ccb3"
|
||||||
integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==
|
integrity sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind "^1.0.2"
|
call-bind "^1.0.2"
|
||||||
es-to-primitive "^1.2.1"
|
es-to-primitive "^1.2.1"
|
||||||
function-bind "^1.1.1"
|
function-bind "^1.1.1"
|
||||||
function.prototype.name "^1.1.5"
|
function.prototype.name "^1.1.5"
|
||||||
get-intrinsic "^1.1.1"
|
get-intrinsic "^1.1.2"
|
||||||
get-symbol-description "^1.0.0"
|
get-symbol-description "^1.0.0"
|
||||||
has "^1.0.3"
|
has "^1.0.3"
|
||||||
has-property-descriptors "^1.0.0"
|
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-shared-array-buffer "^1.0.2"
|
||||||
is-string "^1.0.7"
|
is-string "^1.0.7"
|
||||||
is-weakref "^1.0.2"
|
is-weakref "^1.0.2"
|
||||||
object-inspect "^1.12.0"
|
object-inspect "^1.12.2"
|
||||||
object-keys "^1.1.1"
|
object-keys "^1.1.1"
|
||||||
object.assign "^4.1.2"
|
object.assign "^4.1.4"
|
||||||
regexp.prototype.flags "^1.4.3"
|
regexp.prototype.flags "^1.4.3"
|
||||||
string.prototype.trimend "^1.0.5"
|
string.prototype.trimend "^1.0.5"
|
||||||
string.prototype.trimstart "^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"
|
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
|
||||||
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
||||||
|
|
||||||
eslint-config-next@12.2.5:
|
eslint-config-next@12.3.1:
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.2.5.tgz#76ce83f18cc02f6f42ed407a127f83db54fabd3c"
|
resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.3.1.tgz#5d4eb0b7903cea81fd0d5106601d3afb0a453ff4"
|
||||||
integrity sha512-SOowilkqPzW6DxKp3a3SYlrfPi5Ajs9MIzp9gVfUDxxH9QFM5ElkR1hX5m/iICJuvCbWgQqFBiA3mCMozluniw==
|
integrity sha512-EN/xwKPU6jz1G0Qi6Bd/BqMnHLyRAL0VsaQaWA7F3KkjAgZHi4f1uL1JKGWNxdQpHTW/sdGONBd0bzxUka/DJg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@next/eslint-plugin-next" "12.2.5"
|
"@next/eslint-plugin-next" "12.3.1"
|
||||||
"@rushstack/eslint-patch" "^1.1.3"
|
"@rushstack/eslint-patch" "^1.1.3"
|
||||||
"@typescript-eslint/parser" "^5.21.0"
|
"@typescript-eslint/parser" "^5.21.0"
|
||||||
eslint-import-resolver-node "^0.3.6"
|
eslint-import-resolver-node "^0.3.6"
|
||||||
eslint-import-resolver-typescript "^2.7.1"
|
eslint-import-resolver-typescript "^2.7.1"
|
||||||
eslint-plugin-import "^2.26.0"
|
eslint-plugin-import "^2.26.0"
|
||||||
eslint-plugin-jsx-a11y "^6.5.1"
|
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-plugin-react-hooks "^4.5.0"
|
||||||
|
|
||||||
eslint-import-resolver-node@^0.3.6:
|
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"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3"
|
||||||
integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==
|
integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==
|
||||||
|
|
||||||
eslint-plugin-react@^7.29.4:
|
eslint-plugin-react@^7.31.7:
|
||||||
version "7.30.1"
|
version "7.31.8"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz#2be4ab23ce09b5949c6631413ba64b2810fd3e22"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.8.tgz#3a4f80c10be1bcbc8197be9e8b641b2a3ef219bf"
|
||||||
integrity sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg==
|
integrity sha512-5lBTZmgQmARLLSYiwI71tiGVTLUuqXantZM6vlSY39OaDSV0M7+32K5DnLkmFrwTe+Ksz0ffuLUC91RUviVZfw==
|
||||||
dependencies:
|
dependencies:
|
||||||
array-includes "^3.1.5"
|
array-includes "^3.1.5"
|
||||||
array.prototype.flatmap "^1.3.0"
|
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"
|
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
|
||||||
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
|
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
|
||||||
|
|
||||||
eslint@8.22.0:
|
eslint@8.23.1:
|
||||||
version "8.22.0"
|
version "8.23.1"
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.22.0.tgz#78fcb044196dfa7eef30a9d65944f6f980402c48"
|
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.23.1.tgz#cfd7b3f7fdd07db8d16b4ac0516a29c8d8dca5dc"
|
||||||
integrity sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==
|
integrity sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint/eslintrc" "^1.3.0"
|
"@eslint/eslintrc" "^1.3.2"
|
||||||
"@humanwhocodes/config-array" "^0.10.4"
|
"@humanwhocodes/config-array" "^0.10.4"
|
||||||
"@humanwhocodes/gitignore-to-minimatch" "^1.0.2"
|
"@humanwhocodes/gitignore-to-minimatch" "^1.0.2"
|
||||||
|
"@humanwhocodes/module-importer" "^1.0.1"
|
||||||
ajv "^6.10.0"
|
ajv "^6.10.0"
|
||||||
chalk "^4.0.0"
|
chalk "^4.0.0"
|
||||||
cross-spawn "^7.0.2"
|
cross-spawn "^7.0.2"
|
||||||
@@ -644,13 +650,12 @@ eslint@8.22.0:
|
|||||||
eslint-scope "^7.1.1"
|
eslint-scope "^7.1.1"
|
||||||
eslint-utils "^3.0.0"
|
eslint-utils "^3.0.0"
|
||||||
eslint-visitor-keys "^3.3.0"
|
eslint-visitor-keys "^3.3.0"
|
||||||
espree "^9.3.3"
|
espree "^9.4.0"
|
||||||
esquery "^1.4.0"
|
esquery "^1.4.0"
|
||||||
esutils "^2.0.2"
|
esutils "^2.0.2"
|
||||||
fast-deep-equal "^3.1.3"
|
fast-deep-equal "^3.1.3"
|
||||||
file-entry-cache "^6.0.1"
|
file-entry-cache "^6.0.1"
|
||||||
find-up "^5.0.0"
|
find-up "^5.0.0"
|
||||||
functional-red-black-tree "^1.0.1"
|
|
||||||
glob-parent "^6.0.1"
|
glob-parent "^6.0.1"
|
||||||
globals "^13.15.0"
|
globals "^13.15.0"
|
||||||
globby "^11.1.0"
|
globby "^11.1.0"
|
||||||
@@ -659,6 +664,7 @@ eslint@8.22.0:
|
|||||||
import-fresh "^3.0.0"
|
import-fresh "^3.0.0"
|
||||||
imurmurhash "^0.1.4"
|
imurmurhash "^0.1.4"
|
||||||
is-glob "^4.0.0"
|
is-glob "^4.0.0"
|
||||||
|
js-sdsl "^4.1.4"
|
||||||
js-yaml "^4.1.0"
|
js-yaml "^4.1.0"
|
||||||
json-stable-stringify-without-jsonify "^1.0.1"
|
json-stable-stringify-without-jsonify "^1.0.1"
|
||||||
levn "^0.4.1"
|
levn "^0.4.1"
|
||||||
@@ -670,12 +676,11 @@ eslint@8.22.0:
|
|||||||
strip-ansi "^6.0.1"
|
strip-ansi "^6.0.1"
|
||||||
strip-json-comments "^3.1.0"
|
strip-json-comments "^3.1.0"
|
||||||
text-table "^0.2.0"
|
text-table "^0.2.0"
|
||||||
v8-compile-cache "^2.0.3"
|
|
||||||
|
|
||||||
espree@^9.3.2, espree@^9.3.3:
|
espree@^9.4.0:
|
||||||
version "9.3.3"
|
version "9.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.3.tgz#2dd37c4162bb05f433ad3c1a52ddf8a49dc08e9d"
|
resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a"
|
||||||
integrity sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==
|
integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn "^8.8.0"
|
acorn "^8.8.0"
|
||||||
acorn-jsx "^5.3.2"
|
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==
|
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||||
|
|
||||||
fast-glob@^3.2.9:
|
fast-glob@^3.2.9:
|
||||||
version "3.2.11"
|
version "3.2.12"
|
||||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
|
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
|
||||||
integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
|
integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@nodelib/fs.stat" "^2.0.2"
|
"@nodelib/fs.stat" "^2.0.2"
|
||||||
"@nodelib/fs.walk" "^1.2.3"
|
"@nodelib/fs.walk" "^1.2.3"
|
||||||
@@ -793,20 +798,15 @@ function.prototype.name@^1.1.5:
|
|||||||
es-abstract "^1.19.0"
|
es-abstract "^1.19.0"
|
||||||
functions-have-names "^1.2.2"
|
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:
|
functions-have-names@^1.2.2:
|
||||||
version "1.2.3"
|
version "1.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
|
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
|
||||||
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
|
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
|
||||||
|
|
||||||
get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
|
get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598"
|
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385"
|
||||||
integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==
|
integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==
|
||||||
dependencies:
|
dependencies:
|
||||||
function-bind "^1.1.1"
|
function-bind "^1.1.1"
|
||||||
has "^1.0.3"
|
has "^1.0.3"
|
||||||
@@ -974,9 +974,9 @@ is-boolean-object@^1.1.0:
|
|||||||
has-tostringtag "^1.0.0"
|
has-tostringtag "^1.0.0"
|
||||||
|
|
||||||
is-callable@^1.1.4, is-callable@^1.2.4:
|
is-callable@^1.1.4, is-callable@^1.2.4:
|
||||||
version "1.2.4"
|
version "1.2.6"
|
||||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945"
|
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.6.tgz#fd6170b0b8c7e2cc73de342ef8284a2202023c44"
|
||||||
integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==
|
integrity sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q==
|
||||||
|
|
||||||
is-core-module@^2.8.1, is-core-module@^2.9.0:
|
is-core-module@^2.8.1, is-core-module@^2.9.0:
|
||||||
version "2.10.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"
|
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||||
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
|
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":
|
"js-tokens@^3.0.0 || ^4.0.0":
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
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"
|
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||||
|
|
||||||
next@12.2.5:
|
next@12.3.1:
|
||||||
version "12.2.5"
|
version "12.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/next/-/next-12.2.5.tgz#14fb5975e8841fad09553b8ef41fe1393602b717"
|
resolved "https://registry.yarnpkg.com/next/-/next-12.3.1.tgz#127b825ad2207faf869b33393ec8c75fe61e50f1"
|
||||||
integrity sha512-tBdjqX5XC/oFs/6gxrZhjmiq90YWizUYU6qOWAfat7zJwrwapJ+BYgX2PmiacunXMaRpeVT4vz5MSPSLgNkrpA==
|
integrity sha512-l7bvmSeIwX5lp07WtIiP9u2ytZMv7jIeB8iacR28PuUEFG5j0HGAPnMqyG5kbZNBG2H7tRsrQ4HCjuMOPnANZw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@next/env" "12.2.5"
|
"@next/env" "12.3.1"
|
||||||
"@swc/helpers" "0.4.3"
|
"@swc/helpers" "0.4.11"
|
||||||
caniuse-lite "^1.0.30001332"
|
caniuse-lite "^1.0.30001406"
|
||||||
postcss "8.4.14"
|
postcss "8.4.14"
|
||||||
styled-jsx "5.0.4"
|
styled-jsx "5.0.7"
|
||||||
use-sync-external-store "1.2.0"
|
use-sync-external-store "1.2.0"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
"@next/swc-android-arm-eabi" "12.2.5"
|
"@next/swc-android-arm-eabi" "12.3.1"
|
||||||
"@next/swc-android-arm64" "12.2.5"
|
"@next/swc-android-arm64" "12.3.1"
|
||||||
"@next/swc-darwin-arm64" "12.2.5"
|
"@next/swc-darwin-arm64" "12.3.1"
|
||||||
"@next/swc-darwin-x64" "12.2.5"
|
"@next/swc-darwin-x64" "12.3.1"
|
||||||
"@next/swc-freebsd-x64" "12.2.5"
|
"@next/swc-freebsd-x64" "12.3.1"
|
||||||
"@next/swc-linux-arm-gnueabihf" "12.2.5"
|
"@next/swc-linux-arm-gnueabihf" "12.3.1"
|
||||||
"@next/swc-linux-arm64-gnu" "12.2.5"
|
"@next/swc-linux-arm64-gnu" "12.3.1"
|
||||||
"@next/swc-linux-arm64-musl" "12.2.5"
|
"@next/swc-linux-arm64-musl" "12.3.1"
|
||||||
"@next/swc-linux-x64-gnu" "12.2.5"
|
"@next/swc-linux-x64-gnu" "12.3.1"
|
||||||
"@next/swc-linux-x64-musl" "12.2.5"
|
"@next/swc-linux-x64-musl" "12.3.1"
|
||||||
"@next/swc-win32-arm64-msvc" "12.2.5"
|
"@next/swc-win32-arm64-msvc" "12.3.1"
|
||||||
"@next/swc-win32-ia32-msvc" "12.2.5"
|
"@next/swc-win32-ia32-msvc" "12.3.1"
|
||||||
"@next/swc-win32-x64-msvc" "12.2.5"
|
"@next/swc-win32-x64-msvc" "12.3.1"
|
||||||
|
|
||||||
object-assign@^4.1.1:
|
object-assign@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||||
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
|
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"
|
version "1.12.2"
|
||||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
|
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
|
||||||
integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==
|
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"
|
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
||||||
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
|
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"
|
version "4.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f"
|
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f"
|
||||||
integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==
|
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"
|
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||||
|
|
||||||
styled-jsx@5.0.4:
|
styled-jsx@5.0.7:
|
||||||
version "5.0.4"
|
version "5.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.4.tgz#5b1bd0b9ab44caae3dd1361295559706e044aa53"
|
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.7.tgz#be44afc53771b983769ac654d355ca8d019dff48"
|
||||||
integrity sha512-sDFWLbg4zR+UkNzfk5lPilyIgtpddfxXEULxhujorr5jtePTUqiPDc5BC0v1NRqTr/WaFBGQQUoYToGlF4B2KQ==
|
integrity sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==
|
||||||
|
|
||||||
supports-color@^7.1.0:
|
supports-color@^7.1.0:
|
||||||
version "7.2.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"
|
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==
|
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:
|
which-boxed-primitive@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
|
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"packageManager": "yarn@1.22.19",
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
@@ -34,7 +35,7 @@
|
|||||||
"prettier": "2.6.2",
|
"prettier": "2.6.2",
|
||||||
"ts-eager": "2.0.2",
|
"ts-eager": "2.0.2",
|
||||||
"ts-jest": "28.0.5",
|
"ts-jest": "28.0.5",
|
||||||
"turbo": "1.3.2-canary.1"
|
"turbo": "1.4.7"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lerna": "lerna",
|
"lerna": "lerna",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/build-utils",
|
"name": "@vercel/build-utils",
|
||||||
"version": "5.4.1",
|
"version": "5.5.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.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 }[];
|
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'>) {
|
constructor(params: Omit<EdgeFunction, 'type'>) {
|
||||||
this.type = 'EdgeFunction';
|
this.type = 'EdgeFunction';
|
||||||
this.name = params.name;
|
this.name = params.name;
|
||||||
@@ -46,5 +49,6 @@ export class EdgeFunction {
|
|||||||
this.files = params.files;
|
this.files = params.files;
|
||||||
this.envVarsInUse = params.envVarsInUse;
|
this.envVarsInUse = params.envVarsInUse;
|
||||||
this.assets = params.assets;
|
this.assets = params.assets;
|
||||||
|
this.regions = params.regions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,14 +61,14 @@ export function getPrettyError(obj: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new NowBuildError({
|
return new NowBuildError({
|
||||||
code: 'DEV_VALIDATE_CONFIG',
|
code: 'INVALID_VERCEL_CONFIG',
|
||||||
message: message,
|
message: message,
|
||||||
link: prop ? `${docsUrl}#project/${prop.toLowerCase()}` : docsUrl,
|
link: prop ? `${docsUrl}#project/${prop.toLowerCase()}` : docsUrl,
|
||||||
action: 'View Documentation',
|
action: 'View Documentation',
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return new NowBuildError({
|
return new NowBuildError({
|
||||||
code: 'DEV_VALIDATE_CONFIG',
|
code: 'INVALID_VERCEL_CONFIG',
|
||||||
message: `Failed to validate configuration.`,
|
message: `Failed to validate configuration.`,
|
||||||
link: docsUrl,
|
link: docsUrl,
|
||||||
action: 'View Documentation',
|
action: 'View Documentation',
|
||||||
|
|||||||
@@ -5,7 +5,13 @@ import path from 'path';
|
|||||||
import Sema from 'async-sema';
|
import Sema from 'async-sema';
|
||||||
import { FileBase } from './types';
|
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 {
|
interface FileFsRefOptions {
|
||||||
mode?: number;
|
mode?: number;
|
||||||
|
|||||||
@@ -12,7 +12,13 @@ interface FileRefOptions {
|
|||||||
mutable?: boolean;
|
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 {
|
class BailableError extends Error {
|
||||||
public bail: boolean;
|
public bail: boolean;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const allOptions = [
|
|||||||
major: 12,
|
major: 12,
|
||||||
range: '12.x',
|
range: '12.x',
|
||||||
runtime: 'nodejs12.x',
|
runtime: 'nodejs12.x',
|
||||||
discontinueDate: new Date('2022-10-01'),
|
discontinueDate: new Date('2022-10-03'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
major: 10,
|
major: 10,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { NowBuildError } from '../errors';
|
|||||||
import { Meta, PackageJson, NodeVersion, Config } from '../types';
|
import { Meta, PackageJson, NodeVersion, Config } from '../types';
|
||||||
import { getSupportedNodeVersion, getLatestNodeVersion } from './node-version';
|
import { getSupportedNodeVersion, getLatestNodeVersion } from './node-version';
|
||||||
import { readConfigFile } from './read-config-file';
|
import { readConfigFile } from './read-config-file';
|
||||||
|
import { cloneEnv } from '../clone-env';
|
||||||
|
|
||||||
// Only allow one `runNpmInstall()` invocation to run concurrently
|
// Only allow one `runNpmInstall()` invocation to run concurrently
|
||||||
const runNpmInstallSema = new Sema(1);
|
const runNpmInstallSema = new Sema(1);
|
||||||
@@ -217,7 +218,7 @@ export function getSpawnOptions(
|
|||||||
nodeVersion: NodeVersion
|
nodeVersion: NodeVersion
|
||||||
): SpawnOptions {
|
): SpawnOptions {
|
||||||
const opts = {
|
const opts = {
|
||||||
env: { ...process.env },
|
env: cloneEnv(process.env),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!meta.isDev) {
|
if (!meta.isDev) {
|
||||||
@@ -250,33 +251,24 @@ export async function getNodeVersion(
|
|||||||
meta: Meta = {}
|
meta: Meta = {}
|
||||||
): Promise<NodeVersion> {
|
): Promise<NodeVersion> {
|
||||||
const latest = getLatestNodeVersion();
|
const latest = getLatestNodeVersion();
|
||||||
if (meta && meta.isDev) {
|
if (meta.isDev) {
|
||||||
// Use the system-installed version of `node` in PATH for `vercel dev`
|
// Use the system-installed version of `node` in PATH for `vercel dev`
|
||||||
return { ...latest, runtime: 'nodejs' };
|
return { ...latest, runtime: 'nodejs' };
|
||||||
}
|
}
|
||||||
const { packageJson } = await scanParentDirs(destPath, true);
|
const { packageJson } = await scanParentDirs(destPath, true);
|
||||||
let { nodeVersion } = config;
|
let { nodeVersion } = config;
|
||||||
let isAuto = true;
|
let isAuto = true;
|
||||||
if (packageJson && packageJson.engines && packageJson.engines.node) {
|
if (packageJson?.engines?.node) {
|
||||||
const { node } = packageJson.engines;
|
const { node } = packageJson.engines;
|
||||||
if (
|
if (nodeVersion && validRange(node) && !intersects(nodeVersion, node)) {
|
||||||
nodeVersion &&
|
|
||||||
validRange(node) &&
|
|
||||||
!intersects(nodeVersion, node) &&
|
|
||||||
!meta.isDev
|
|
||||||
) {
|
|
||||||
console.warn(
|
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`
|
`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(
|
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`
|
`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 (
|
} else if (validRange(node) && intersects(`${latest.major + 1}.x`, node)) {
|
||||||
validRange(node) &&
|
|
||||||
intersects(`${latest.major + 1}.x`, node) &&
|
|
||||||
!meta.isDev
|
|
||||||
) {
|
|
||||||
console.warn(
|
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`
|
`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}`);
|
debug(`Installing to ${destPath}`);
|
||||||
|
|
||||||
const opts: SpawnOptionsExtended = { cwd: destPath, ...spawnOpts };
|
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;
|
delete env.NODE_ENV;
|
||||||
opts.env = getEnvForPackageManager({
|
opts.env = getEnvForPackageManager({
|
||||||
cliType,
|
cliType,
|
||||||
@@ -458,12 +450,30 @@ export async function runNpmInstall(
|
|||||||
env,
|
env,
|
||||||
});
|
});
|
||||||
let commandArgs: string[];
|
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') {
|
if (cliType === 'npm') {
|
||||||
opts.prettyCommand = 'npm install';
|
opts.prettyCommand = 'npm install';
|
||||||
commandArgs = args
|
commandArgs = args
|
||||||
.filter(a => a !== '--prefer-offline')
|
.filter(a => a !== '--prefer-offline')
|
||||||
.concat(['install', '--no-audit', '--unsafe-perm']);
|
.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') {
|
} else if (cliType === 'pnpm') {
|
||||||
// PNPM's install command is similar to NPM's but without the audit nonsense
|
// PNPM's install command is similar to NPM's but without the audit nonsense
|
||||||
// @see options https://pnpm.io/cli/install
|
// @see options https://pnpm.io/cli/install
|
||||||
@@ -480,7 +490,26 @@ export async function runNpmInstall(
|
|||||||
commandArgs.push('--production');
|
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]`);
|
debug(`Install complete [${Date.now() - installTime}ms]`);
|
||||||
return true;
|
return true;
|
||||||
} finally {
|
} finally {
|
||||||
@@ -591,10 +620,7 @@ export async function runPackageJsonScript(
|
|||||||
cliType,
|
cliType,
|
||||||
lockfileVersion,
|
lockfileVersion,
|
||||||
nodeVersion: undefined,
|
nodeVersion: undefined,
|
||||||
env: {
|
env: cloneEnv(process.env, spawnOpts?.env),
|
||||||
...process.env,
|
|
||||||
...spawnOpts?.env,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import debug from './debug';
|
|||||||
import getIgnoreFilter from './get-ignore-filter';
|
import getIgnoreFilter from './get-ignore-filter';
|
||||||
import { getPlatformEnv } from './get-platform-env';
|
import { getPlatformEnv } from './get-platform-env';
|
||||||
import { getPrefixedEnvVars } from './get-prefixed-env-vars';
|
import { getPrefixedEnvVars } from './get-prefixed-env-vars';
|
||||||
|
import { cloneEnv } from './clone-env';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
FileBlob,
|
FileBlob,
|
||||||
@@ -84,6 +85,7 @@ export {
|
|||||||
getLambdaOptionsFromFunction,
|
getLambdaOptionsFromFunction,
|
||||||
scanParentDirs,
|
scanParentDirs,
|
||||||
getIgnoreFilter,
|
getIgnoreFilter,
|
||||||
|
cloneEnv,
|
||||||
};
|
};
|
||||||
|
|
||||||
export { EdgeFunction } from './edge-function';
|
export { EdgeFunction } from './edge-function';
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export interface Config {
|
|||||||
devCommand?: string;
|
devCommand?: string;
|
||||||
framework?: string | null;
|
framework?: string | null;
|
||||||
nodeVersion?: string;
|
nodeVersion?: string;
|
||||||
|
middleware?: boolean;
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,6 +342,7 @@ export interface BuilderV2 {
|
|||||||
version: 2;
|
version: 2;
|
||||||
build: BuildV2;
|
build: BuildV2;
|
||||||
prepareCache?: PrepareCache;
|
prepareCache?: PrepareCache;
|
||||||
|
shouldServe?: ShouldServe;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BuilderV3 {
|
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,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
|
"name": "20-npm-7",
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ const skipFixtures: string[] = [
|
|||||||
'08-zero-config-middleman',
|
'08-zero-config-middleman',
|
||||||
'21-npm-workspaces',
|
'21-npm-workspaces',
|
||||||
'23-pnpm-workspaces',
|
'23-pnpm-workspaces',
|
||||||
|
'41-nx-monorepo',
|
||||||
|
'42-npm-workspace-with-nx',
|
||||||
];
|
];
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// 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 ms from 'ms';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs, { readlink } from 'fs-extra';
|
import fs, { readlink } from 'fs-extra';
|
||||||
import retry from 'async-retry';
|
|
||||||
import { strict as assert, strictEqual } from 'assert';
|
import { strict as assert, strictEqual } from 'assert';
|
||||||
import { createZip } from '../src/lambda';
|
import { createZip } from '../src/lambda';
|
||||||
import { getSupportedNodeVersion } from '../src/fs/node-version';
|
import { getSupportedNodeVersion } from '../src/fs/node-version';
|
||||||
@@ -16,7 +15,6 @@ import {
|
|||||||
runPackageJsonScript,
|
runPackageJsonScript,
|
||||||
scanParentDirs,
|
scanParentDirs,
|
||||||
FileBlob,
|
FileBlob,
|
||||||
Meta,
|
|
||||||
} from '../src';
|
} from '../src';
|
||||||
|
|
||||||
jest.setTimeout(10 * 1000);
|
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 () => {
|
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(
|
expect(await getSupportedNodeVersion('14.x', false)).toHaveProperty(
|
||||||
'major',
|
'major',
|
||||||
14
|
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('foo', true), autoMessage);
|
||||||
await expectBuilderError(getSupportedNodeVersion('=> 10', true), autoMessage);
|
await expectBuilderError(getSupportedNodeVersion('=> 10', true), autoMessage);
|
||||||
|
|
||||||
expect(await getSupportedNodeVersion('12.x', true)).toHaveProperty(
|
|
||||||
'major',
|
|
||||||
12
|
|
||||||
);
|
|
||||||
expect(await getSupportedNodeVersion('14.x', true)).toHaveProperty(
|
expect(await getSupportedNodeVersion('14.x', true)).toHaveProperty(
|
||||||
'major',
|
'major',
|
||||||
14
|
14
|
||||||
@@ -275,21 +265,21 @@ it('should only match supported node versions, otherwise throw an error', async
|
|||||||
|
|
||||||
it('should match all semver ranges', async () => {
|
it('should match all semver ranges', async () => {
|
||||||
// See https://docs.npmjs.com/files/package.json#engines
|
// See https://docs.npmjs.com/files/package.json#engines
|
||||||
expect(await getSupportedNodeVersion('12.0.0')).toHaveProperty('major', 12);
|
expect(await getSupportedNodeVersion('14.0.0')).toHaveProperty('major', 14);
|
||||||
expect(await getSupportedNodeVersion('12.x')).toHaveProperty('major', 12);
|
expect(await getSupportedNodeVersion('14.x')).toHaveProperty('major', 14);
|
||||||
expect(await getSupportedNodeVersion('>=10')).toHaveProperty('major', 16);
|
expect(await getSupportedNodeVersion('>=10')).toHaveProperty('major', 16);
|
||||||
expect(await getSupportedNodeVersion('>=10.3.0')).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',
|
'major',
|
||||||
12
|
16
|
||||||
);
|
);
|
||||||
expect(await getSupportedNodeVersion('>=9.5.0 <=12.5.0')).toHaveProperty(
|
expect(await getSupportedNodeVersion('>=9.5.0 <=14.5.0')).toHaveProperty(
|
||||||
'major',
|
'major',
|
||||||
12
|
14
|
||||||
);
|
);
|
||||||
expect(await getSupportedNodeVersion('~12.5.0')).toHaveProperty('major', 12);
|
expect(await getSupportedNodeVersion('~14.5.0')).toHaveProperty('major', 14);
|
||||||
expect(await getSupportedNodeVersion('^12.5.0')).toHaveProperty('major', 12);
|
expect(await getSupportedNodeVersion('^14.5.0')).toHaveProperty('major', 14);
|
||||||
expect(await getSupportedNodeVersion('12.5.0 - 14.5.0')).toHaveProperty(
|
expect(await getSupportedNodeVersion('14.5.0 - 14.20.0')).toHaveProperty(
|
||||||
'major',
|
'major',
|
||||||
14
|
14
|
||||||
);
|
);
|
||||||
@@ -436,8 +426,8 @@ it('should warn for deprecated versions, soon to be discontinued', async () => {
|
|||||||
expect(warningMessages).toStrictEqual([
|
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 "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 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-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-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 Node.js Version to 16.x in your Project Settings to use Node.js 16.',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
global.Date.now = realDateNow;
|
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'));
|
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should only invoke `runNpmInstall()` once per `package.json` file (serial)', async () => {
|
it('should retry npm install when peer deps invalid and npm@8 on node@16', async () => {
|
||||||
const meta: Meta = {};
|
const nodeMajor = Number(process.versions.node.split('.')[0]);
|
||||||
const fixture = path.join(__dirname, 'fixtures', '02-zero-config-api');
|
if (nodeMajor !== 16) {
|
||||||
const apiDir = path.join(fixture, 'api');
|
console.log(`Skipping test on node@${nodeMajor}`);
|
||||||
const retryOpts = { maxRetryTime: 1000 };
|
return;
|
||||||
let run1, run2, run3;
|
}
|
||||||
await retry(async () => {
|
const fixture = path.join(__dirname, 'fixtures', '15-npm-8-legacy-peer-deps');
|
||||||
run1 = await runNpmInstall(apiDir, [], undefined, meta);
|
const nodeVersion = { major: nodeMajor } as any;
|
||||||
expect(run1).toEqual(true);
|
await runNpmInstall(fixture, [], {}, {}, nodeVersion);
|
||||||
expect(
|
expect(warningMessages).toStrictEqual([
|
||||||
(meta.runNpmInstallSet as Set<string>).has(
|
'Warning: Retrying "Install Command" with `--legacy-peer-deps` which may accept a potentially broken dependency and slow install time.',
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,11 +10,9 @@
|
|||||||
|
|
||||||
## Usage
|
## 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 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.
|
||||||
|
|
||||||
We make it easy for frontend teams to **develop, preview, and ship** delightful user experiences, where performance is the default.
|
|
||||||
|
|
||||||
To install the latest version of Vercel CLI, run this command:
|
To install the latest version of Vercel CLI, run this command:
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vercel",
|
"name": "vercel",
|
||||||
"version": "28.2.1",
|
"version": "28.4.11",
|
||||||
"preferGlobal": true,
|
"preferGlobal": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"description": "The command-line interface for Vercel",
|
"description": "The command-line interface for Vercel",
|
||||||
@@ -41,16 +41,16 @@
|
|||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "5.4.1",
|
"@vercel/build-utils": "5.5.5",
|
||||||
"@vercel/go": "2.2.4",
|
"@vercel/go": "2.2.13",
|
||||||
"@vercel/hydrogen": "0.0.17",
|
"@vercel/hydrogen": "0.0.26",
|
||||||
"@vercel/next": "3.1.23",
|
"@vercel/next": "3.2.5",
|
||||||
"@vercel/node": "2.5.12",
|
"@vercel/node": "2.5.25",
|
||||||
"@vercel/python": "3.1.13",
|
"@vercel/python": "3.1.22",
|
||||||
"@vercel/redwood": "1.0.21",
|
"@vercel/redwood": "1.0.31",
|
||||||
"@vercel/remix": "1.0.22",
|
"@vercel/remix": "1.0.32",
|
||||||
"@vercel/ruby": "1.3.30",
|
"@vercel/ruby": "1.3.39",
|
||||||
"@vercel/static-build": "1.0.21",
|
"@vercel/static-build": "1.0.32",
|
||||||
"update-notifier": "5.1.0"
|
"update-notifier": "5.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -95,9 +95,9 @@
|
|||||||
"@types/which": "1.3.2",
|
"@types/which": "1.3.2",
|
||||||
"@types/write-json-file": "2.2.1",
|
"@types/write-json-file": "2.2.1",
|
||||||
"@types/yauzl-promise": "2.1.0",
|
"@types/yauzl-promise": "2.1.0",
|
||||||
"@vercel/client": "12.2.3",
|
"@vercel/client": "12.2.13",
|
||||||
"@vercel/frameworks": "1.1.3",
|
"@vercel/frameworks": "1.1.8",
|
||||||
"@vercel/fs-detectors": "2.1.0",
|
"@vercel/fs-detectors": "3.4.5",
|
||||||
"@vercel/fun": "1.0.4",
|
"@vercel/fun": "1.0.4",
|
||||||
"@vercel/ncc": "0.24.0",
|
"@vercel/ncc": "0.24.0",
|
||||||
"@zeit/source-map-support": "0.6.2",
|
"@zeit/source-map-support": "0.6.2",
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import open from 'open';
|
|||||||
import boxen from 'boxen';
|
import boxen from 'boxen';
|
||||||
import execa from 'execa';
|
import execa from 'execa';
|
||||||
import plural from 'pluralize';
|
import plural from 'pluralize';
|
||||||
import inquirer from 'inquirer';
|
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
import chalk, { Chalk } from 'chalk';
|
import chalk, { Chalk } from 'chalk';
|
||||||
import { URLSearchParams, parse } from 'url';
|
import { URLSearchParams, parse } from 'url';
|
||||||
@@ -150,7 +149,9 @@ export default async function main(client: Client): Promise<number> {
|
|||||||
|
|
||||||
if (badDeployment) {
|
if (badDeployment) {
|
||||||
if (badDeployment instanceof Error) {
|
if (badDeployment instanceof Error) {
|
||||||
badDeployment.message += ` "${bad}"`;
|
badDeployment.message += ` when requesting bad deployment "${normalizeURL(
|
||||||
|
bad
|
||||||
|
)}"`;
|
||||||
output.prettyError(badDeployment);
|
output.prettyError(badDeployment);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -165,7 +166,9 @@ export default async function main(client: Client): Promise<number> {
|
|||||||
|
|
||||||
if (goodDeployment) {
|
if (goodDeployment) {
|
||||||
if (goodDeployment instanceof Error) {
|
if (goodDeployment instanceof Error) {
|
||||||
goodDeployment.message += ` "${good}"`;
|
goodDeployment.message += ` when requesting good deployment "${normalizeURL(
|
||||||
|
good
|
||||||
|
)}"`;
|
||||||
output.prettyError(goodDeployment);
|
output.prettyError(goodDeployment);
|
||||||
return 1;
|
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
|
// If we have the "good" deployment in this chunk, then we're done
|
||||||
for (let i = 0; i < newDeployments.length; i++) {
|
for (let i = 0; i < newDeployments.length; i++) {
|
||||||
if (newDeployments[i].url === good) {
|
if (newDeployments[i].url === good) {
|
||||||
newDeployments = newDeployments.slice(0, i + 1);
|
// grab all deployments up until the good one
|
||||||
|
newDeployments = newDeployments.slice(0, i);
|
||||||
next = undefined;
|
next = undefined;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -316,7 +320,7 @@ export default async function main(client: Client): Promise<number> {
|
|||||||
if (openEnabled) {
|
if (openEnabled) {
|
||||||
await open(testUrl);
|
await open(testUrl);
|
||||||
}
|
}
|
||||||
const answer = await inquirer.prompt({
|
const answer = await client.prompt({
|
||||||
type: 'expand',
|
type: 'expand',
|
||||||
name: 'action',
|
name: 'action',
|
||||||
message: 'Select an action:',
|
message: 'Select an action:',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import chalk from 'chalk';
|
|||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
import { join, normalize, relative, resolve } from 'path';
|
import { join, normalize, relative, resolve } from 'path';
|
||||||
import {
|
import {
|
||||||
|
getDiscontinuedNodeVersions,
|
||||||
normalizePath,
|
normalizePath,
|
||||||
Files,
|
Files,
|
||||||
FileFsRef,
|
FileFsRef,
|
||||||
@@ -25,6 +26,7 @@ import {
|
|||||||
MergeRoutesProps,
|
MergeRoutesProps,
|
||||||
Route,
|
Route,
|
||||||
} from '@vercel/routing-utils';
|
} from '@vercel/routing-utils';
|
||||||
|
import { fileNameSymbol } from '@vercel/client';
|
||||||
import type { VercelConfig } from '@vercel/client';
|
import type { VercelConfig } from '@vercel/client';
|
||||||
|
|
||||||
import pull from './pull';
|
import pull from './pull';
|
||||||
@@ -54,6 +56,7 @@ import { importBuilders } from '../util/build/import-builders';
|
|||||||
import { initCorepack, cleanupCorepack } from '../util/build/corepack';
|
import { initCorepack, cleanupCorepack } from '../util/build/corepack';
|
||||||
import { sortBuilders } from '../util/build/sort-builders';
|
import { sortBuilders } from '../util/build/sort-builders';
|
||||||
import { toEnumerableError } from '../util/error';
|
import { toEnumerableError } from '../util/error';
|
||||||
|
import { validateConfig } from '../util/validate-config';
|
||||||
|
|
||||||
type BuildResult = BuildResultV2 | BuildResultV3;
|
type BuildResult = BuildResultV2 | BuildResultV3;
|
||||||
|
|
||||||
@@ -232,7 +235,8 @@ export default async function main(client: Client): Promise<number> {
|
|||||||
process.env.VERCEL = '1';
|
process.env.VERCEL = '1';
|
||||||
process.env.NOW_BUILDER = '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) {
|
} catch (err: any) {
|
||||||
output.prettyError(err);
|
output.prettyError(err);
|
||||||
|
|
||||||
@@ -265,23 +269,36 @@ async function doBuild(
|
|||||||
buildsJson: BuildsManifest,
|
buildsJson: BuildsManifest,
|
||||||
cwd: string,
|
cwd: string,
|
||||||
outputDir: string
|
outputDir: string
|
||||||
): Promise<number> {
|
): Promise<void> {
|
||||||
const { output } = client;
|
const { output } = client;
|
||||||
const workPath = join(cwd, project.settings.rootDirectory || '.');
|
const workPath = join(cwd, project.settings.rootDirectory || '.');
|
||||||
|
|
||||||
// Load `package.json` and `vercel.json` files
|
const [pkg, vercelConfig, nowConfig] = await Promise.all([
|
||||||
const [pkg, vercelConfig] = await Promise.all([
|
|
||||||
readJSONFile<PackageJson>(join(workPath, 'package.json')),
|
readJSONFile<PackageJson>(join(workPath, 'package.json')),
|
||||||
readJSONFile<VercelConfig>(join(workPath, 'vercel.json')).then(
|
readJSONFile<VercelConfig>(join(workPath, 'vercel.json')),
|
||||||
config => config || readJSONFile<VercelConfig>(join(workPath, 'now.json'))
|
readJSONFile<VercelConfig>(join(workPath, 'now.json')),
|
||||||
),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (pkg instanceof CantParseJSONFile) throw pkg;
|
if (pkg instanceof CantParseJSONFile) throw pkg;
|
||||||
if (vercelConfig instanceof CantParseJSONFile) throw vercelConfig;
|
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 = {
|
const projectSettings = {
|
||||||
...project.settings,
|
...project.settings,
|
||||||
...pickOverrides(vercelConfig || {}),
|
...pickOverrides(localConfig),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get a list of source files
|
// Get a list of source files
|
||||||
@@ -289,12 +306,12 @@ async function doBuild(
|
|||||||
normalizePath(relative(workPath, f))
|
normalizePath(relative(workPath, f))
|
||||||
);
|
);
|
||||||
|
|
||||||
const routesResult = getTransformedRoutes(vercelConfig || {});
|
const routesResult = getTransformedRoutes(localConfig);
|
||||||
if (routesResult.error) {
|
if (routesResult.error) {
|
||||||
throw routesResult.error;
|
throw routesResult.error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vercelConfig?.builds && vercelConfig.functions) {
|
if (localConfig.builds && localConfig.functions) {
|
||||||
throw new NowBuildError({
|
throw new NowBuildError({
|
||||||
code: 'bad_request',
|
code: 'bad_request',
|
||||||
message:
|
message:
|
||||||
@@ -303,7 +320,7 @@ async function doBuild(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let builds = vercelConfig?.builds || [];
|
let builds = localConfig.builds || [];
|
||||||
let zeroConfigRoutes: Route[] = [];
|
let zeroConfigRoutes: Route[] = [];
|
||||||
let isZeroConfig = false;
|
let isZeroConfig = false;
|
||||||
|
|
||||||
@@ -318,7 +335,7 @@ async function doBuild(
|
|||||||
|
|
||||||
// Detect the Vercel Builders that will need to be invoked
|
// Detect the Vercel Builders that will need to be invoked
|
||||||
const detectedBuilders = await detectBuilders(files, pkg, {
|
const detectedBuilders = await detectBuilders(files, pkg, {
|
||||||
...vercelConfig,
|
...localConfig,
|
||||||
projectSettings,
|
projectSettings,
|
||||||
ignoreBuildScript: true,
|
ignoreBuildScript: true,
|
||||||
featHandleMiss: true,
|
featHandleMiss: true,
|
||||||
@@ -395,13 +412,10 @@ async function doBuild(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
buildsJson.builds = Array.from(buildsJsonBuilds.values());
|
buildsJson.builds = Array.from(buildsJsonBuilds.values());
|
||||||
const buildsJsonPath = join(outputDir, 'builds.json');
|
await fs.writeJSON(join(outputDir, 'builds.json'), buildsJson, {
|
||||||
const writeBuildsJsonPromise = fs.writeJSON(buildsJsonPath, buildsJson, {
|
|
||||||
spaces: 2,
|
spaces: 2,
|
||||||
});
|
});
|
||||||
|
|
||||||
ops.push(writeBuildsJsonPromise);
|
|
||||||
|
|
||||||
// The `meta` config property is re-used for each Builder
|
// The `meta` config property is re-used for each Builder
|
||||||
// invocation so that Builders can share state between
|
// invocation so that Builders can share state between
|
||||||
// subsequent entrypoint builds.
|
// subsequent entrypoint builds.
|
||||||
@@ -454,6 +468,25 @@ async function doBuild(
|
|||||||
);
|
);
|
||||||
const buildResult = await builder.build(buildOptions);
|
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
|
// Store the build result to generate the final `config.json` after
|
||||||
// all builds have completed
|
// all builds have completed
|
||||||
buildResults.set(build, buildResult);
|
buildResults.set(build, buildResult);
|
||||||
@@ -466,7 +499,7 @@ async function doBuild(
|
|||||||
build,
|
build,
|
||||||
builder,
|
builder,
|
||||||
builderPkg,
|
builderPkg,
|
||||||
vercelConfig
|
localConfig
|
||||||
).then(
|
).then(
|
||||||
override => {
|
override => {
|
||||||
if (override) overrides.push(override);
|
if (override) overrides.push(override);
|
||||||
@@ -475,26 +508,11 @@ async function doBuild(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
} catch (err: any) {
|
} 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);
|
const buildJsonBuild = buildsJsonBuilds.get(build);
|
||||||
if (buildJsonBuild) {
|
if (buildJsonBuild) {
|
||||||
buildJsonBuild.error = toEnumerableError(err);
|
buildJsonBuild.error = toEnumerableError(err);
|
||||||
|
|
||||||
await fs.writeJSON(buildsJsonPath, buildsJson, {
|
|
||||||
spaces: 2,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
throw err;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,7 +573,7 @@ async function doBuild(
|
|||||||
builds: builderRoutes,
|
builds: builderRoutes,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mergedImages = mergeImages(buildResults.values());
|
const mergedImages = mergeImages(localConfig.images, buildResults.values());
|
||||||
const mergedWildcard = mergeWildcard(buildResults.values());
|
const mergedWildcard = mergeWildcard(buildResults.values());
|
||||||
const mergedOverrides: Record<string, PathOverride> =
|
const mergedOverrides: Record<string, PathOverride> =
|
||||||
overrides.length > 0 ? Object.assign({}, ...overrides) : undefined;
|
overrides.length > 0 ? Object.assign({}, ...overrides) : undefined;
|
||||||
@@ -581,8 +599,6 @@ async function doBuild(
|
|||||||
emoji('success')
|
emoji('success')
|
||||||
)}\n`
|
)}\n`
|
||||||
);
|
);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function expandBuild(files: string[], build: Builder): Builder[] {
|
function expandBuild(files: string[], build: Builder): Builder[] {
|
||||||
@@ -624,9 +640,9 @@ function expandBuild(files: string[], build: Builder): Builder[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mergeImages(
|
function mergeImages(
|
||||||
|
images: BuildResultV2Typical['images'],
|
||||||
buildResults: Iterable<BuildResult>
|
buildResults: Iterable<BuildResult>
|
||||||
): BuildResultV2Typical['images'] {
|
): BuildResultV2Typical['images'] {
|
||||||
let images: BuildResultV2Typical['images'] = undefined;
|
|
||||||
for (const result of buildResults) {
|
for (const result of buildResults) {
|
||||||
if ('images' in result && result.images) {
|
if ('images' in result && result.images) {
|
||||||
images = Object.assign({}, images, result.images);
|
images = Object.assign({}, images, result.images);
|
||||||
|
|||||||
@@ -522,7 +522,12 @@ export default async (client: Client): Promise<number> => {
|
|||||||
quiet,
|
quiet,
|
||||||
wantsPublic: argv['--public'] || localConfig.public,
|
wantsPublic: argv['--public'] || localConfig.public,
|
||||||
type: null,
|
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,
|
regions,
|
||||||
meta,
|
meta,
|
||||||
gitMetadata,
|
gitMetadata,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { resolve, join } from 'path';
|
|||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
|
|
||||||
import DevServer from '../../util/dev/server';
|
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 { ProjectEnvVariable } from '../../types';
|
||||||
import Client from '../../util/client';
|
import Client from '../../util/client';
|
||||||
import { getLinkedProject } from '../../util/projects/link';
|
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 npa from 'npm-package-arg';
|
||||||
import { satisfies } from 'semver';
|
import { satisfies } from 'semver';
|
||||||
import { dirname, join } from 'path';
|
import { dirname, join } from 'path';
|
||||||
import { mkdirp, outputJSON, readJSON, symlink } from 'fs-extra';
|
import { mkdirp, outputJSON, readJSON, symlink } from 'fs-extra';
|
||||||
|
import { isStaticRuntime } from '@vercel/fs-detectors';
|
||||||
import {
|
import {
|
||||||
BuilderV2,
|
BuilderV2,
|
||||||
BuilderV3,
|
BuilderV3,
|
||||||
@@ -13,6 +16,9 @@ import { VERCEL_DIR } from '../projects/link';
|
|||||||
import { Output } from '../output';
|
import { Output } from '../output';
|
||||||
import readJSONFile from '../read-json-file';
|
import readJSONFile from '../read-json-file';
|
||||||
import { CantParseJSONFile } from '../errors-ts';
|
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 {
|
export interface BuilderWithPkg {
|
||||||
path: string;
|
path: string;
|
||||||
@@ -81,7 +87,7 @@ export async function resolveBuilders(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === '@vercel/static' || name === '@now/static') {
|
if (isStaticRuntime(name)) {
|
||||||
// `@vercel/static` is a special-case built-in builder
|
// `@vercel/static` is a special-case built-in builder
|
||||||
builders.set(name, {
|
builders.set(name, {
|
||||||
builder: staticBuilder,
|
builder: staticBuilder,
|
||||||
@@ -201,15 +207,54 @@ async function installBuilders(
|
|||||||
if (err.code !== 'EEXIST') throw err;
|
if (err.code !== 'EEXIST') throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
output.debug(`Installing Builders: ${Array.from(buildersToAdd).join(', ')}`);
|
output.log(
|
||||||
await spawnAsync('yarn', ['add', '@vercel/build-utils', ...buildersToAdd], {
|
`Installing ${plural('Builder', buildersToAdd.size)}: ${Array.from(
|
||||||
cwd: buildersDir,
|
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
|
// Symlink `@now/build-utils` -> `@vercel/build-utils` to support legacy Builders
|
||||||
const nowScopePath = join(buildersDir, 'node_modules/@now');
|
const nowScopePath = join(buildersDir, 'node_modules/@now');
|
||||||
await mkdirp(nowScopePath);
|
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,
|
// Cross-reference any builderSpecs from the saved `package.json` file,
|
||||||
// in case they were installed from a URL
|
// in case they were installed from a URL
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import minimatch from 'minimatch';
|
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;
|
export const version = 2;
|
||||||
|
|
||||||
@@ -39,3 +40,18 @@ export const build: BuildV2 = async ({ entrypoint, files, config }) => {
|
|||||||
|
|
||||||
return { output };
|
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';
|
} from '../types';
|
||||||
import { sharedPromise } from './promise';
|
import { sharedPromise } from './promise';
|
||||||
import { APIError } from './errors-ts';
|
import { APIError } from './errors-ts';
|
||||||
|
import { normalizeError } from './is-error';
|
||||||
|
|
||||||
const isSAMLError = (v: any): v is SAMLError => {
|
const isSAMLError = (v: any): v is SAMLError => {
|
||||||
return v && v.saml;
|
return v && v.saml;
|
||||||
@@ -146,10 +147,15 @@ export default class Client extends EventEmitter implements Stdio {
|
|||||||
const error = await responseError(res);
|
const error = await responseError(res);
|
||||||
|
|
||||||
if (isSAMLError(error)) {
|
if (isSAMLError(error)) {
|
||||||
// A SAML error means the token is expired, or is not
|
try {
|
||||||
// designated for the requested team, so the user needs
|
// A SAML error means the token is expired, or is not
|
||||||
// to re-authenticate
|
// designated for the requested team, so the user needs
|
||||||
await this.reauthenticate(error);
|
// 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) {
|
} else if (res.status >= 400 && res.status < 500) {
|
||||||
// Any other 4xx should bail without retrying
|
// Any other 4xx should bail without retrying
|
||||||
return bail(error);
|
return bail(error);
|
||||||
@@ -186,7 +192,7 @@ export default class Client extends EventEmitter implements Stdio {
|
|||||||
`Failed to re-authenticate for ${bold(error.scope)} scope`
|
`Failed to re-authenticate for ${bold(error.scope)} scope`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
process.exit(1);
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.authConfig.token = result.token;
|
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> {
|
export function isDirty(directory: string, output: Output): Promise<boolean> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
exec('git status -s', { cwd: directory }, function (err, stdout, stderr) {
|
// note: we specify the `--no-optional-locks` git flag so that `git status`
|
||||||
let debugMessage = `Failed to determine if Git repo has been modified:`;
|
// does not perform any "optional" operations such as optimizing the index
|
||||||
if (err || stderr) {
|
// in the background: https://git-scm.com/docs/git-status#_background_refresh
|
||||||
if (err) debugMessage += `\n${err}`;
|
exec(
|
||||||
if (stderr) debugMessage += `\n${stderr.trim()}`;
|
'git --no-optional-locks status -s',
|
||||||
output.debug(debugMessage);
|
{ cwd: directory },
|
||||||
return resolve(false);
|
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);
|
.reduce((a: number, b: number) => a + b, 0);
|
||||||
const totalSizeHuman = bytes.format(missingSize, { decimalPlaces: 1 });
|
const totalSizeHuman = bytes.format(missingSize, { decimalPlaces: 1 });
|
||||||
|
|
||||||
uploads.forEach((e: any) =>
|
// When stderr is not a TTY then we only want to
|
||||||
e.on('progress', () => {
|
// print upload progress in 25% increments
|
||||||
const uploadedBytes = uploads.reduce((acc: number, e: any) => {
|
let nextStep = 0;
|
||||||
return acc + e.bytesUploaded;
|
const stepSize = now._client.stderr.isTTY ? 0 : 0.25;
|
||||||
}, 0);
|
|
||||||
|
|
||||||
const bar = progress(uploadedBytes, missingSize);
|
const updateProgress = () => {
|
||||||
if (!bar || uploadedBytes === missingSize) {
|
const uploadedBytes = uploads.reduce((acc: number, e: any) => {
|
||||||
output.spinner(deployingSpinnerVal, 0);
|
return acc + e.bytesUploaded;
|
||||||
} else {
|
}, 0);
|
||||||
const uploadedHuman = bytes.format(uploadedBytes, {
|
|
||||||
decimalPlaces: 1,
|
const bar = progress(uploadedBytes, missingSize);
|
||||||
fixedDecimals: true,
|
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(
|
output.spinner(
|
||||||
`Uploading ${chalk.reset(
|
`Uploading ${chalk.reset(
|
||||||
`[${bar}] (${uploadedHuman}/${totalSizeHuman})`
|
`[${bar}] (${uploadedHuman}/${totalSizeHuman})`
|
||||||
)}`,
|
)}`,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
nextStep += stepSize;
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
);
|
};
|
||||||
|
|
||||||
|
uploads.forEach((e: any) => e.on('progress', updateProgress));
|
||||||
|
updateProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type === 'file-uploaded') {
|
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,
|
FileFsRef,
|
||||||
normalizePath,
|
normalizePath,
|
||||||
} from '@vercel/build-utils';
|
} from '@vercel/build-utils';
|
||||||
import { isOfficialRuntime } from '@vercel/fs-detectors';
|
import { isStaticRuntime } from '@vercel/fs-detectors';
|
||||||
import plural from 'pluralize';
|
import plural from 'pluralize';
|
||||||
import minimatch from 'minimatch';
|
import minimatch from 'minimatch';
|
||||||
|
|
||||||
@@ -26,7 +26,6 @@ import { relative } from '../path-helpers';
|
|||||||
import { LambdaSizeExceededError } from '../errors-ts';
|
import { LambdaSizeExceededError } from '../errors-ts';
|
||||||
|
|
||||||
import DevServer from './server';
|
import DevServer from './server';
|
||||||
import { getBuilder } from './builder-cache';
|
|
||||||
import {
|
import {
|
||||||
VercelConfig,
|
VercelConfig,
|
||||||
BuildMatch,
|
BuildMatch,
|
||||||
@@ -41,6 +40,7 @@ import {
|
|||||||
import { normalizeRoutes } from '@vercel/routing-utils';
|
import { normalizeRoutes } from '@vercel/routing-utils';
|
||||||
import getUpdateCommand from '../get-update-command';
|
import getUpdateCommand from '../get-update-command';
|
||||||
import { getTitleName } from '../pkg-name';
|
import { getTitleName } from '../pkg-name';
|
||||||
|
import { importBuilders } from '../build/import-builders';
|
||||||
|
|
||||||
interface BuildMessage {
|
interface BuildMessage {
|
||||||
type: string;
|
type: string;
|
||||||
@@ -108,18 +108,18 @@ export async function executeBuild(
|
|||||||
filesRemoved?: string[]
|
filesRemoved?: string[]
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const {
|
const {
|
||||||
builderWithPkg: { runInProcess, requirePath, builder, package: pkg },
|
builderWithPkg: { path: requirePath, builder, pkg },
|
||||||
} = match;
|
} = match;
|
||||||
const { entrypoint } = match;
|
const { entrypoint, use } = match;
|
||||||
|
const isStatic = isStaticRuntime(use);
|
||||||
const { envConfigs, cwd: workPath, devCacheDir } = devServer;
|
const { envConfigs, cwd: workPath, devCacheDir } = devServer;
|
||||||
const debug = devServer.output.isDebugEnabled();
|
const debug = devServer.output.isDebugEnabled();
|
||||||
|
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
const showBuildTimestamp =
|
const showBuildTimestamp = !isStatic && (!isInitialBuild || debug);
|
||||||
!isOfficialRuntime('static', match.use) && (!isInitialBuild || debug);
|
|
||||||
|
|
||||||
if (showBuildTimestamp) {
|
if (showBuildTimestamp) {
|
||||||
devServer.output.log(`Building ${match.use}:${entrypoint}`);
|
devServer.output.log(`Building ${use}:${entrypoint}`);
|
||||||
devServer.output.debug(
|
devServer.output.debug(
|
||||||
`Using \`${pkg.name}${pkg.version ? `@${pkg.version}` : ''}\``
|
`Using \`${pkg.name}${pkg.version ? `@${pkg.version}` : ''}\``
|
||||||
);
|
);
|
||||||
@@ -130,7 +130,7 @@ export async function executeBuild(
|
|||||||
let result: BuildResult;
|
let result: BuildResult;
|
||||||
|
|
||||||
let { buildProcess } = match;
|
let { buildProcess } = match;
|
||||||
if (!runInProcess && !buildProcess) {
|
if (!isStatic && !buildProcess) {
|
||||||
buildProcess = await createBuildProcess(
|
buildProcess = await createBuildProcess(
|
||||||
match,
|
match,
|
||||||
envConfigs,
|
envConfigs,
|
||||||
@@ -158,7 +158,7 @@ export async function executeBuild(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let buildResultOrOutputs: BuilderOutputs | BuildResult | BuildResultV3;
|
let buildResultOrOutputs;
|
||||||
if (buildProcess) {
|
if (buildProcess) {
|
||||||
buildProcess.send({
|
buildProcess.send({
|
||||||
type: 'build',
|
type: 'build',
|
||||||
@@ -198,16 +198,12 @@ export async function executeBuild(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sort out build result to builder v2 shape
|
// 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)
|
// `BuilderOutputs` map was returned (Now Builder v1 behavior)
|
||||||
result = {
|
result = {
|
||||||
output: buildResultOrOutputs as BuilderOutputs,
|
output: buildResultOrOutputs as BuilderOutputs,
|
||||||
routes: [],
|
routes: [],
|
||||||
watch: [],
|
watch: [],
|
||||||
distPath:
|
|
||||||
typeof buildResultOrOutputs.distPath === 'string'
|
|
||||||
? buildResultOrOutputs.distPath
|
|
||||||
: undefined,
|
|
||||||
};
|
};
|
||||||
} else if (builder.version === 2) {
|
} else if (builder.version === 2) {
|
||||||
result = buildResultOrOutputs as BuildResult;
|
result = buildResultOrOutputs as BuildResult;
|
||||||
@@ -253,7 +249,7 @@ export async function executeBuild(
|
|||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`${getTitleName()} CLI does not support builder version ${
|
`${getTitleName()} CLI does not support builder version ${
|
||||||
builder.version
|
(builder as any).version
|
||||||
}.\nPlease run \`${await getUpdateCommand()}\` to update to the latest CLI.`
|
}.\nPlease run \`${await getUpdateCommand()}\` to update to the latest CLI.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -383,7 +379,7 @@ export async function executeBuild(
|
|||||||
if (showBuildTimestamp) {
|
if (showBuildTimestamp) {
|
||||||
const endTime = Date.now();
|
const endTime = Date.now();
|
||||||
devServer.output.log(
|
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 noMatches: Builder[] = [];
|
||||||
const builds = vercelConfig.builds || [{ src: '**', use: '@vercel/static' }];
|
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) {
|
for (const buildConfig of builds) {
|
||||||
let { src = '**', use, config = {} } = buildConfig;
|
let { src = '**', use, config = {} } = buildConfig;
|
||||||
@@ -439,6 +437,8 @@ export async function getBuildMatches(
|
|||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
src = relative(cwd, file);
|
src = relative(cwd, file);
|
||||||
|
|
||||||
|
const entrypoint = mapToEntrypoint.get(src) || src;
|
||||||
|
|
||||||
// Remove the output directory prefix
|
// Remove the output directory prefix
|
||||||
if (config.zeroConfig && config.outputDirectory) {
|
if (config.zeroConfig && config.outputDirectory) {
|
||||||
const outputMatch = 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({
|
matches.push({
|
||||||
...buildConfig,
|
...buildConfig,
|
||||||
src,
|
src,
|
||||||
entrypoint: mapToEntrypoint.get(src) || src,
|
entrypoint,
|
||||||
builderWithPkg,
|
builderWithPkg,
|
||||||
buildOutput: {},
|
buildOutput: {},
|
||||||
buildResults: new Map(),
|
buildResults: new Map(),
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import { parse } from 'url';
|
import { parse } from 'url';
|
||||||
import { ListenSpec } from './types';
|
import { ListenSpec } from './types';
|
||||||
|
|
||||||
export default function parseListen(
|
export function parseListen(str: string, defaultPort = 3000): ListenSpec {
|
||||||
str: string,
|
|
||||||
defaultPort = 3000
|
|
||||||
): ListenSpec {
|
|
||||||
let port = Number(str);
|
let port = Number(str);
|
||||||
|
|
||||||
if (!isNaN(port)) {
|
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 url, { URL } from 'url';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
@@ -32,6 +31,7 @@ import {
|
|||||||
} from '@vercel/routing-utils';
|
} from '@vercel/routing-utils';
|
||||||
import {
|
import {
|
||||||
Builder,
|
Builder,
|
||||||
|
cloneEnv,
|
||||||
Env,
|
Env,
|
||||||
StartDevServerResult,
|
StartDevServerResult,
|
||||||
FileFsRef,
|
FileFsRef,
|
||||||
@@ -57,16 +57,11 @@ import { MissingDotenvVarsError } from '../errors-ts';
|
|||||||
import cliPkg from '../pkg';
|
import cliPkg from '../pkg';
|
||||||
import { getVercelDirectory } from '../projects/link';
|
import { getVercelDirectory } from '../projects/link';
|
||||||
import { staticFiles as getFiles } from '../get-files';
|
import { staticFiles as getFiles } from '../get-files';
|
||||||
import { validateConfig } from './validate';
|
import { validateConfig } from '../validate-config';
|
||||||
import { devRouter, getRoutesTypes } from './router';
|
import { devRouter, getRoutesTypes } from './router';
|
||||||
import getMimeType from './mime-type';
|
import getMimeType from './mime-type';
|
||||||
import { executeBuild, getBuildMatches, shutdownBuilder } from './builder';
|
import { executeBuild, getBuildMatches, shutdownBuilder } from './builder';
|
||||||
import { generateErrorMessage, generateHttpStatusDescription } from './errors';
|
import { generateErrorMessage, generateHttpStatusDescription } from './errors';
|
||||||
import {
|
|
||||||
installBuilders,
|
|
||||||
updateBuilders,
|
|
||||||
builderDirPromise,
|
|
||||||
} from './builder-cache';
|
|
||||||
|
|
||||||
// HTML templates
|
// HTML templates
|
||||||
import errorTemplate from './templates/error';
|
import errorTemplate from './templates/error';
|
||||||
@@ -101,7 +96,9 @@ import {
|
|||||||
isError,
|
isError,
|
||||||
isSpawnError,
|
isSpawnError,
|
||||||
} from '../is-error';
|
} from '../is-error';
|
||||||
|
import isURL from './is-url';
|
||||||
import { pickOverrides } from '../projects/project-settings';
|
import { pickOverrides } from '../projects/project-settings';
|
||||||
|
import { replaceLocalhost } from './parse-listen';
|
||||||
|
|
||||||
const frontendRuntimeSet = new Set(
|
const frontendRuntimeSet = new Set(
|
||||||
frameworkList.map(f => f.useRuntime?.use || '@vercel/static-build')
|
frameworkList.map(f => f.useRuntime?.use || '@vercel/static-build')
|
||||||
@@ -134,9 +131,18 @@ export default class DevServer {
|
|||||||
public proxy: httpProxy;
|
public proxy: httpProxy;
|
||||||
public envConfigs: EnvConfigs;
|
public envConfigs: EnvConfigs;
|
||||||
public files: BuilderInputs;
|
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 currentDevCommand?: string;
|
||||||
private caseSensitive: boolean;
|
private caseSensitive: boolean;
|
||||||
private apiDir: string | null;
|
private apiDir: string | null;
|
||||||
@@ -152,7 +158,7 @@ export default class DevServer {
|
|||||||
private filter: (path: string) => boolean;
|
private filter: (path: string) => boolean;
|
||||||
private podId: string;
|
private podId: string;
|
||||||
private devProcess?: ChildProcess;
|
private devProcess?: ChildProcess;
|
||||||
private devProcessPort?: number;
|
private devProcessOrigin?: string;
|
||||||
private devServerPids: Set<number>;
|
private devServerPids: Set<number>;
|
||||||
private originalProjectSettings?: ProjectSettings;
|
private originalProjectSettings?: ProjectSettings;
|
||||||
private projectSettings?: ProjectSettings;
|
private projectSettings?: ProjectSettings;
|
||||||
@@ -160,8 +166,6 @@ export default class DevServer {
|
|||||||
private vercelConfigWarning: boolean;
|
private vercelConfigWarning: boolean;
|
||||||
private getVercelConfigPromise: Promise<VercelConfig> | null;
|
private getVercelConfigPromise: Promise<VercelConfig> | null;
|
||||||
private blockingBuildsPromise: Promise<void> | null;
|
private blockingBuildsPromise: Promise<void> | null;
|
||||||
private updateBuildersPromise: Promise<void> | null;
|
|
||||||
private updateBuildersTimeout: NodeJS.Timeout | undefined;
|
|
||||||
private startPromise: Promise<void> | null;
|
private startPromise: Promise<void> | null;
|
||||||
|
|
||||||
private systemEnvValues: string[];
|
private systemEnvValues: string[];
|
||||||
@@ -174,17 +178,22 @@ export default class DevServer {
|
|||||||
this.systemEnvValues = options.systemEnvValues || [];
|
this.systemEnvValues = options.systemEnvValues || [];
|
||||||
this.projectEnvs = options.projectEnvs || [];
|
this.projectEnvs = options.projectEnvs || [];
|
||||||
this.files = {};
|
this.files = {};
|
||||||
this.address = '';
|
|
||||||
this.originalProjectSettings = options.projectSettings;
|
this.originalProjectSettings = options.projectSettings;
|
||||||
this.projectSettings = options.projectSettings;
|
this.projectSettings = options.projectSettings;
|
||||||
this.caseSensitive = false;
|
this.caseSensitive = false;
|
||||||
this.apiDir = null;
|
this.apiDir = null;
|
||||||
this.apiExtensions = new Set();
|
this.apiExtensions = new Set();
|
||||||
|
|
||||||
this.proxy = httpProxy.createProxyServer({
|
this.proxy = httpProxy.createProxyServer({
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
ws: true,
|
ws: true,
|
||||||
xfwd: 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 = http.createServer(this.devServerHandler);
|
||||||
this.server.timeout = 0; // Disable timeout
|
this.server.timeout = 0; // Disable timeout
|
||||||
this.stopping = false;
|
this.stopping = false;
|
||||||
@@ -195,7 +204,6 @@ export default class DevServer {
|
|||||||
this.vercelConfigWarning = false;
|
this.vercelConfigWarning = false;
|
||||||
this.getVercelConfigPromise = null;
|
this.getVercelConfigPromise = null;
|
||||||
this.blockingBuildsPromise = null;
|
this.blockingBuildsPromise = null;
|
||||||
this.updateBuildersPromise = null;
|
|
||||||
this.startPromise = null;
|
this.startPromise = null;
|
||||||
|
|
||||||
this.watchAggregationId = 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> {
|
async getLocalEnv(fileName: string, base?: Env): Promise<Env> {
|
||||||
// TODO: use the file watcher to only invalidate the env `dotfile`
|
// TODO: use the file watcher to only invalidate the env `dotfile`
|
||||||
// once a change to the `fileName` occurs
|
// once a change to the `fileName` occurs
|
||||||
@@ -707,7 +688,7 @@ export default class DevServer {
|
|||||||
this.projectEnvs || [],
|
this.projectEnvs || [],
|
||||||
this.systemEnvValues || [],
|
this.systemEnvValues || [],
|
||||||
this.projectSettings?.autoExposeSystemEnvs,
|
this.projectSettings?.autoExposeSystemEnvs,
|
||||||
new URL(this.address).host
|
this.address.host
|
||||||
);
|
);
|
||||||
|
|
||||||
allEnv = { ...cloudEnv };
|
allEnv = { ...cloudEnv };
|
||||||
@@ -842,7 +823,7 @@ export default class DevServer {
|
|||||||
injectSystemValuesInDotenv(env: Env): Env {
|
injectSystemValuesInDotenv(env: Env): Env {
|
||||||
for (const name of Object.keys(env)) {
|
for (const name of Object.keys(env)) {
|
||||||
if (name === 'VERCEL_URL') {
|
if (name === 'VERCEL_URL') {
|
||||||
env['VERCEL_URL'] = new URL(this.address).host;
|
env['VERCEL_URL'] = this.address.host;
|
||||||
} else if (name === 'VERCEL_REGION') {
|
} else if (name === 'VERCEL_REGION') {
|
||||||
env['VERCEL_REGION'] = 'dev1';
|
env['VERCEL_REGION'] = 'dev1';
|
||||||
}
|
}
|
||||||
@@ -915,9 +896,7 @@ export default class DevServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.address = address
|
this._address = new URL(replaceLocalhost(address));
|
||||||
.replace('[::]', 'localhost')
|
|
||||||
.replace('127.0.0.1', 'localhost');
|
|
||||||
|
|
||||||
const vercelConfig = await this.getVercelConfig();
|
const vercelConfig = await this.getVercelConfig();
|
||||||
const devCommandPromise = this.runDevCommand();
|
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);
|
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
|
// 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
|
// executed at boot-up time in order to get the initial assets and/or routes
|
||||||
// that can be served by the builder.
|
// 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.
|
// Configure the server to forward WebSocket "upgrade" events to the proxy.
|
||||||
this.server.on('upgrade', async (req, socket, head) => {
|
this.server.on('upgrade', async (req, socket, head) => {
|
||||||
await this.startPromise;
|
await this.startPromise;
|
||||||
if (!this.devProcessPort) {
|
if (!this.devProcessOrigin) {
|
||||||
this.output.debug(
|
this.output.debug(
|
||||||
`Detected "upgrade" event, but closing socket because no frontend dev server is running`
|
`Detected "upgrade" event, but closing socket because no frontend dev server is running`
|
||||||
);
|
);
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const target = `http://127.0.0.1:${this.devProcessPort}`;
|
const target = this.devProcessOrigin;
|
||||||
this.output.debug(`Detected "upgrade" event, proxying to ${target}`);
|
this.output.debug(`Detected "upgrade" event, proxying to ${target}`);
|
||||||
this.proxy.ws(req, socket, head, { target });
|
this.proxy.ws(req, socket, head, { target });
|
||||||
});
|
});
|
||||||
|
|
||||||
await devCommandPromise;
|
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.
|
* Shuts down the `vercel dev` server, and cleans up any temporary resources.
|
||||||
*/
|
*/
|
||||||
async stop(exitCode?: number): Promise<void> {
|
async stop(exitCode?: number): Promise<void> {
|
||||||
const { devProcess } = this;
|
|
||||||
const { debug } = this.output;
|
|
||||||
if (this.stopping) return;
|
if (this.stopping) return;
|
||||||
|
|
||||||
this.stopping = true;
|
this.stopping = true;
|
||||||
|
|
||||||
if (typeof this.updateBuildersTimeout !== 'undefined') {
|
const { devProcess } = this;
|
||||||
clearTimeout(this.updateBuildersTimeout);
|
const { debug } = this.output;
|
||||||
}
|
|
||||||
|
|
||||||
const ops: Promise<any>[] = [];
|
const ops: Promise<any>[] = [];
|
||||||
|
|
||||||
for (const match of this.buildMatches.values()) {
|
for (const match of this.buildMatches.values()) {
|
||||||
@@ -1055,18 +1012,10 @@ export default class DevServer {
|
|||||||
ops.push(this.watcher.close());
|
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) {
|
for (const pid of this.devServerPids) {
|
||||||
ops.push(this.killBuilderDevServer(pid));
|
ops.push(this.killBuilderDevServer(pid));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the builders module cache is created
|
|
||||||
ops.push(builderDirPromise);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Promise.all(ops);
|
await Promise.all(ops);
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
@@ -1465,8 +1414,9 @@ export default class DevServer {
|
|||||||
// the middleware server for every HTTP request?
|
// the middleware server for every HTTP request?
|
||||||
const { envConfigs, files, devCacheDir, cwd: workPath } = this;
|
const { envConfigs, files, devCacheDir, cwd: workPath } = this;
|
||||||
try {
|
try {
|
||||||
startMiddlewareResult =
|
const { builder } = middleware.builderWithPkg;
|
||||||
await middleware.builderWithPkg.builder.startDevServer?.({
|
if (builder.version === 3) {
|
||||||
|
startMiddlewareResult = await builder.startDevServer?.({
|
||||||
files,
|
files,
|
||||||
entrypoint: middleware.entrypoint,
|
entrypoint: middleware.entrypoint,
|
||||||
workPath,
|
workPath,
|
||||||
@@ -1480,6 +1430,7 @@ export default class DevServer {
|
|||||||
buildEnv: { ...envConfigs.buildEnv },
|
buildEnv: { ...envConfigs.buildEnv },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (startMiddlewareResult) {
|
if (startMiddlewareResult) {
|
||||||
const { port, pid } = startMiddlewareResult;
|
const { port, pid } = startMiddlewareResult;
|
||||||
@@ -1560,15 +1511,34 @@ export default class DevServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rewritePath) {
|
if (rewritePath) {
|
||||||
// TODO: add validation?
|
|
||||||
debug(`Detected rewrite path from middleware: "${rewritePath}"`);
|
debug(`Detected rewrite path from middleware: "${rewritePath}"`);
|
||||||
prevUrl = rewritePath;
|
prevUrl = rewritePath;
|
||||||
|
|
||||||
// Retain orginal pathname, but override query parameters from the rewrite
|
|
||||||
const beforeRewriteUrl = req.url || '/';
|
const beforeRewriteUrl = req.url || '/';
|
||||||
const rewriteUrlParsed = url.parse(beforeRewriteUrl);
|
|
||||||
rewriteUrlParsed.search = url.parse(rewritePath).search;
|
if (isURL(rewritePath)) {
|
||||||
req.url = url.format(rewriteUrlParsed);
|
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(
|
debug(
|
||||||
`Rewrote incoming HTTP URL from "${beforeRewriteUrl}" to "${req.url}"`
|
`Rewrote incoming HTTP URL from "${beforeRewriteUrl}" to "${req.url}"`
|
||||||
);
|
);
|
||||||
@@ -1617,12 +1587,16 @@ export default class DevServer {
|
|||||||
missRoutes,
|
missRoutes,
|
||||||
phase
|
phase
|
||||||
);
|
);
|
||||||
prevUrl =
|
|
||||||
routeResult.continue && routeResult.dest
|
if (routeResult.continue) {
|
||||||
? getReqUrl(routeResult)
|
if (routeResult.dest) {
|
||||||
: req.url;
|
prevUrl = getReqUrl(routeResult);
|
||||||
prevHeaders =
|
}
|
||||||
routeResult.continue && routeResult.headers ? routeResult.headers : {};
|
|
||||||
|
if (routeResult.headers) {
|
||||||
|
prevHeaders = routeResult.headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (routeResult.isDestUrl) {
|
if (routeResult.isDestUrl) {
|
||||||
// Mix the `routes` result dest query params into the req path
|
// Mix the `routes` result dest query params into the req path
|
||||||
@@ -1795,8 +1769,8 @@ export default class DevServer {
|
|||||||
|
|
||||||
if (!match) {
|
if (!match) {
|
||||||
// If the dev command is started, then proxy to it
|
// If the dev command is started, then proxy to it
|
||||||
if (this.devProcessPort) {
|
if (this.devProcessOrigin) {
|
||||||
const upstream = `http://127.0.0.1:${this.devProcessPort}`;
|
const upstream = this.devProcessOrigin;
|
||||||
debug(`Proxying to frontend dev server: ${upstream}`);
|
debug(`Proxying to frontend dev server: ${upstream}`);
|
||||||
|
|
||||||
// Add the Vercel platform proxy request headers
|
// 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
|
// 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
|
// to. Once the proxied request is finished, vercel dev shuts down the dev
|
||||||
// server child process.
|
// server child process.
|
||||||
const { builder, package: builderPkg } = match.builderWithPkg;
|
const { builder, pkg: builderPkg } = match.builderWithPkg;
|
||||||
if (typeof builder.startDevServer === 'function') {
|
if (builder.version === 3 && typeof builder.startDevServer === 'function') {
|
||||||
let devServerResult: StartDevServerResult = null;
|
let devServerResult: StartDevServerResult = null;
|
||||||
try {
|
try {
|
||||||
const { envConfigs, files, devCacheDir, cwd: workPath } = this;
|
const { envConfigs, files, devCacheDir, cwd: workPath } = this;
|
||||||
@@ -1975,7 +1949,7 @@ export default class DevServer {
|
|||||||
// - when there is no asset
|
// - when there is no asset
|
||||||
// - when the asset is not a Lambda (the dev server must take care of all static files)
|
// - when the asset is not a Lambda (the dev server must take care of all static files)
|
||||||
if (
|
if (
|
||||||
this.devProcessPort &&
|
this.devProcessOrigin &&
|
||||||
(!foundAsset || (foundAsset && foundAsset.asset.type !== 'Lambda'))
|
(!foundAsset || (foundAsset && foundAsset.asset.type !== 'Lambda'))
|
||||||
) {
|
) {
|
||||||
debug('Proxying to frontend dev server');
|
debug('Proxying to frontend dev server');
|
||||||
@@ -1987,14 +1961,7 @@ export default class DevServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setResponseHeaders(res, requestId);
|
this.setResponseHeaders(res, requestId);
|
||||||
return proxyPass(
|
return proxyPass(req, res, this.devProcessOrigin, this, requestId, false);
|
||||||
req,
|
|
||||||
res,
|
|
||||||
`http://127.0.0.1:${this.devProcessPort}`,
|
|
||||||
this,
|
|
||||||
requestId,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!foundAsset) {
|
if (!foundAsset) {
|
||||||
@@ -2256,18 +2223,22 @@ export default class DevServer {
|
|||||||
|
|
||||||
const port = await getPort();
|
const port = await getPort();
|
||||||
|
|
||||||
const env: Env = {
|
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.
|
// Because of child process 'pipe' below, isTTY will be false.
|
||||||
FORCE_COLOR: process.stdout.isTTY ? '1' : '0',
|
// Most frameworks use `chalk`/`supports-color` so we enable it anyway.
|
||||||
// Prevent framework dev servers from automatically opening a web
|
FORCE_COLOR: process.stdout.isTTY ? '1' : '0',
|
||||||
// browser window, since it will not be the port that `vc dev`
|
// Prevent framework dev servers from automatically opening a web
|
||||||
// is listening on and thus will be missing Vercel features.
|
// browser window, since it will not be the port that `vc dev`
|
||||||
BROWSER: 'none',
|
// is listening on and thus will be missing Vercel features.
|
||||||
...process.env,
|
BROWSER: 'none',
|
||||||
...this.envConfigs.allEnv,
|
},
|
||||||
PORT: `${port}`,
|
process.env,
|
||||||
};
|
this.envConfigs.allEnv,
|
||||||
|
{
|
||||||
|
PORT: `${port}`,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// This is necesary so that the dev command in the Project
|
// This is necesary so that the dev command in the Project
|
||||||
// will work cross-platform (especially Windows).
|
// will work cross-platform (especially Windows).
|
||||||
@@ -2301,7 +2272,6 @@ export default class DevServer {
|
|||||||
|
|
||||||
this.output.debug(`Spawning dev command: ${command}`);
|
this.output.debug(`Spawning dev command: ${command}`);
|
||||||
|
|
||||||
const devPort = new URL(this.address).port;
|
|
||||||
const proxyPort = new RegExp(port.toString(), 'g');
|
const proxyPort = new RegExp(port.toString(), 'g');
|
||||||
const p = spawnCommand(command, {
|
const p = spawnCommand(command, {
|
||||||
stdio: ['inherit', 'pipe', 'pipe'],
|
stdio: ['inherit', 'pipe', 'pipe'],
|
||||||
@@ -2318,17 +2288,16 @@ export default class DevServer {
|
|||||||
p.stdout.setEncoding('utf8');
|
p.stdout.setEncoding('utf8');
|
||||||
|
|
||||||
p.stdout.on('data', (data: string) => {
|
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) => {
|
p.on('exit', (code, signal) => {
|
||||||
this.output.debug(`Dev command exited with "${signal || code}"`);
|
this.output.debug(`Dev command exited with "${signal || code}"`);
|
||||||
this.devProcessPort = undefined;
|
this.devProcessOrigin = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
await checkForPort(port, 1000 * 60 * 5);
|
const devProcessHost = await checkForPort(port, 1000 * 60 * 5);
|
||||||
|
this.devProcessOrigin = `http://${devProcessHost}:${port}`;
|
||||||
this.devProcessPort = port;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2616,15 +2585,29 @@ function needsBlockingBuild(buildMatch: BuildMatch): boolean {
|
|||||||
return typeof builder.shouldServe !== 'function';
|
return typeof builder.shouldServe !== 'function';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkForPort(port: number, timeout: number): Promise<void> {
|
async function checkForPort(port: number, timeout: number): Promise<string> {
|
||||||
const opts = { host: '127.0.0.1' };
|
let host;
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
while (!(await isPortReachable(port, opts))) {
|
while (!(host = await getReachableHostOnPort(port))) {
|
||||||
if (Date.now() - start > timeout) {
|
if (Date.now() - start > timeout) {
|
||||||
throw new Error(`Detecting port ${port} timed out after ${timeout}ms`);
|
break;
|
||||||
}
|
}
|
||||||
await sleep(100);
|
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) {
|
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,
|
FileBlob,
|
||||||
FileFsRef,
|
FileFsRef,
|
||||||
Lambda,
|
Lambda,
|
||||||
PackageJson,
|
|
||||||
} from '@vercel/build-utils';
|
} from '@vercel/build-utils';
|
||||||
import { VercelConfig } from '@vercel/client';
|
import { VercelConfig } from '@vercel/client';
|
||||||
import { HandleValue, Route } from '@vercel/routing-utils';
|
import { HandleValue, Route } from '@vercel/routing-utils';
|
||||||
import { Output } from '../output';
|
import { Output } from '../output';
|
||||||
import { ProjectEnvVariable, ProjectSettings } from '../../types';
|
import { ProjectEnvVariable, ProjectSettings } from '../../types';
|
||||||
|
import { BuilderWithPkg } from '../build/import-builders';
|
||||||
|
|
||||||
export { VercelConfig };
|
export { VercelConfig };
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ export interface EnvConfigs {
|
|||||||
export interface BuildMatch extends BuildConfig {
|
export interface BuildMatch extends BuildConfig {
|
||||||
entrypoint: string;
|
entrypoint: string;
|
||||||
src: string;
|
src: string;
|
||||||
builderWithPkg: BuilderWithPackage;
|
builderWithPkg: BuilderWithPkg;
|
||||||
buildOutput: BuilderOutputs;
|
buildOutput: BuilderOutputs;
|
||||||
buildResults: Map<string | null, BuildResult>;
|
buildResults: Map<string | null, BuildResult>;
|
||||||
buildTimestamp: number;
|
buildTimestamp: number;
|
||||||
@@ -122,13 +122,6 @@ export interface BuildResultV4 {
|
|||||||
distPath?: string;
|
distPath?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BuilderWithPackage {
|
|
||||||
runInProcess?: boolean;
|
|
||||||
requirePath: string;
|
|
||||||
builder: Readonly<Builder>;
|
|
||||||
package: Readonly<PackageJson>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HttpHeadersConfig {
|
export interface HttpHeadersConfig {
|
||||||
[name: string]: string;
|
[name: string]: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import wait, { StopSpinner } from './wait';
|
|||||||
import type { WritableTTY } from '../../types';
|
import type { WritableTTY } from '../../types';
|
||||||
import { errorToString } from '../is-error';
|
import { errorToString } from '../is-error';
|
||||||
|
|
||||||
|
const IS_TEST = process.env.NODE_ENV === 'test';
|
||||||
|
|
||||||
export interface OutputOptions {
|
export interface OutputOptions {
|
||||||
debug?: boolean;
|
debug?: boolean;
|
||||||
}
|
}
|
||||||
@@ -108,12 +110,15 @@ export class Output {
|
|||||||
};
|
};
|
||||||
|
|
||||||
spinner = (message: string, delay: number = 300): void => {
|
spinner = (message: string, delay: number = 300): void => {
|
||||||
this.spinnerMessage = message;
|
|
||||||
if (this.debugEnabled) {
|
if (this.debugEnabled) {
|
||||||
this.debug(`Spinner invoked (${message}) with a ${delay}ms delay`);
|
this.debug(`Spinner invoked (${message}) with a ${delay}ms delay`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.stream.isTTY) {
|
if (IS_TEST || !this.stream.isTTY) {
|
||||||
|
this.print(`${message}\n`);
|
||||||
|
} else {
|
||||||
|
this.spinnerMessage = message;
|
||||||
|
|
||||||
if (this._spinner) {
|
if (this._spinner) {
|
||||||
this._spinner.text = message;
|
this._spinner.text = message;
|
||||||
} else {
|
} else {
|
||||||
@@ -125,8 +130,6 @@ export class Output {
|
|||||||
delay
|
delay
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.print(`${message}\n`);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,16 @@ export async function writeProjectSettings(
|
|||||||
project: Project,
|
project: Project,
|
||||||
org: Org
|
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 = {
|
const projectLinkAndSettings: ProjectLinkAndSettings = {
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
orgId: org.id,
|
orgId: org.id,
|
||||||
@@ -41,7 +51,7 @@ export async function writeProjectSettings(
|
|||||||
rootDirectory: project.rootDirectory,
|
rootDirectory: project.rootDirectory,
|
||||||
directoryListing: project.directoryListing,
|
directoryListing: project.directoryListing,
|
||||||
nodeVersion: project.nodeVersion,
|
nodeVersion: project.nodeVersion,
|
||||||
analyticsId: project.analytics?.id,
|
analyticsId,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const path = join(cwd, VERCEL_DIR, VERCEL_DIR_PROJECT);
|
const path = join(cwd, VERCEL_DIR, VERCEL_DIR_PROJECT);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
rewritesSchema,
|
rewritesSchema,
|
||||||
trailingSlashSchema,
|
trailingSlashSchema,
|
||||||
} from '@vercel/routing-utils';
|
} from '@vercel/routing-utils';
|
||||||
import { VercelConfig } from './types';
|
import { VercelConfig } from './dev/types';
|
||||||
import {
|
import {
|
||||||
functionsSchema,
|
functionsSchema,
|
||||||
buildsSchema,
|
buildsSchema,
|
||||||
@@ -16,6 +16,83 @@ import {
|
|||||||
} from '@vercel/build-utils';
|
} from '@vercel/build-utils';
|
||||||
import { fileNameSymbol } from '@vercel/client';
|
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 = {
|
const vercelConfigSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
// These are not all possibilities because `vc dev`
|
// These are not all possibilities because `vc dev`
|
||||||
@@ -30,6 +107,7 @@ const vercelConfigSchema = {
|
|||||||
rewrites: rewritesSchema,
|
rewrites: rewritesSchema,
|
||||||
trailingSlash: trailingSlashSchema,
|
trailingSlash: trailingSlashSchema,
|
||||||
functions: functionsSchema,
|
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 => {
|
export default req => {
|
||||||
const url = new URL(req.url);
|
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 === '/') {
|
if (url.pathname === '/') {
|
||||||
// Pass-through "index.html" page
|
// Pass-through "index.html" page
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --port $PORT",
|
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"serve": "vite preview"
|
"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 fs from 'fs-extra';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import listen from 'async-listen';
|
import listen from 'async-listen';
|
||||||
|
import stripAnsi from 'strip-ansi';
|
||||||
import { createServer } from 'http';
|
import { createServer } from 'http';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -85,6 +86,7 @@ test(
|
|||||||
testFixtureStdio('edge-function', async (testPath: any) => {
|
testFixtureStdio('edge-function', async (testPath: any) => {
|
||||||
await testPath(500, '/api/edge-500-response');
|
await testPath(500, '/api/edge-500-response');
|
||||||
await testPath(200, '/api/edge-success');
|
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 () => {
|
test('[vercel dev] prints `npm install` errors', async () => {
|
||||||
const dir = fixture('runtime-not-installed');
|
const dir = fixture('runtime-not-installed');
|
||||||
const result = await exec(dir);
|
const result = await exec(dir);
|
||||||
expect(result.stderr.includes('npm ERR! 404')).toBeTruthy();
|
|
||||||
expect(
|
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();
|
).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
result.stderr.includes('https://vercel.link/npm-install-failed-dev')
|
result.stderr.includes(
|
||||||
|
'https://vercel.link/builder-dependencies-install-failed'
|
||||||
|
)
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -445,11 +445,7 @@ test(
|
|||||||
test(
|
test(
|
||||||
'[vercel dev] Middleware that has no response',
|
'[vercel dev] Middleware that has no response',
|
||||||
testFixtureStdio('middleware-no-response', async (testPath: any) => {
|
testFixtureStdio('middleware-no-response', async (testPath: any) => {
|
||||||
await testPath(
|
await testPath(200, '/api/hello', 'hello from a serverless function');
|
||||||
500,
|
|
||||||
'/api/hello',
|
|
||||||
'A server error has occurred\n\nEDGE_FUNCTION_INVOCATION_FAILED'
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -461,9 +457,42 @@ test(
|
|||||||
await testPath(200, '/another', '<h1>Another</h1>');
|
await testPath(200, '/another', '<h1>Another</h1>');
|
||||||
await testPath(200, '/another.html', '<h1>Another</h1>');
|
await testPath(200, '/another.html', '<h1>Another</h1>');
|
||||||
await testPath(200, '/foo', '<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(
|
test(
|
||||||
'[vercel dev] Middleware that rewrites with custom query params',
|
'[vercel dev] Middleware that rewrites with custom query params',
|
||||||
testFixtureStdio('middleware-rewrite-query', async (testPath: any) => {
|
testFixtureStdio('middleware-rewrite-query', async (testPath: any) => {
|
||||||
@@ -481,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(
|
test(
|
||||||
'[vercel dev] Middleware that redirects',
|
'[vercel dev] Middleware that redirects',
|
||||||
testFixtureStdio('middleware-redirect', async (testPath: any) => {
|
testFixtureStdio('middleware-redirect', async (testPath: any) => {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const {
|
|||||||
|
|
||||||
jest.setTimeout(6 * 60 * 1000);
|
jest.setTimeout(6 * 60 * 1000);
|
||||||
|
|
||||||
|
const isCI = !!process.env.CI;
|
||||||
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
const isCanary = () => getDistTag(cliVersion) === 'canary';
|
const isCanary = () => getDistTag(cliVersion) === 'canary';
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ function fetchWithRetry(url, opts = {}) {
|
|||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
retries: opts.retries || 3,
|
retries: opts.retries ?? 3,
|
||||||
factor: 1,
|
factor: 1,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -150,9 +151,9 @@ async function testPath(
|
|||||||
fetchOpts = {}
|
fetchOpts = {}
|
||||||
) {
|
) {
|
||||||
const opts = {
|
const opts = {
|
||||||
|
retries: isCI ? 5 : 0,
|
||||||
...fetchOpts,
|
...fetchOpts,
|
||||||
redirect: 'manual-dont-change',
|
redirect: 'manual-dont-change',
|
||||||
retries: 5,
|
|
||||||
status,
|
status,
|
||||||
};
|
};
|
||||||
const url = `${origin}${path}`;
|
const url = `${origin}${path}`;
|
||||||
@@ -330,7 +331,7 @@ function testFixtureStdio(
|
|||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(projectSettings),
|
body: JSON.stringify(projectSettings),
|
||||||
retries: 3,
|
retries: isCI ? 3 : 0,
|
||||||
status: 200,
|
status: 200,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
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({
|
'vercel.json': JSON.stringify({
|
||||||
functions: {
|
functions: {
|
||||||
'api/**/*.php': {
|
'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 { Deployment } from '@vercel/client';
|
||||||
import { client } from './client';
|
import { client } from './client';
|
||||||
import { Build, User } from '../../src/types';
|
import { Build, User } from '../../src/types';
|
||||||
|
import type { Request, Response } from 'express';
|
||||||
|
|
||||||
let deployments = new Map<string, Deployment>();
|
let deployments = new Map<string, Deployment>();
|
||||||
let deploymentBuilds = new Map<Deployment, Build[]>();
|
let deploymentBuilds = new Map<Deployment, Build[]>();
|
||||||
|
let alreadySetupDeplomentEndpoints = false;
|
||||||
|
|
||||||
type State = Deployment['readyState'];
|
type State = Deployment['readyState'];
|
||||||
|
|
||||||
export function useDeployment({
|
export function useDeployment({
|
||||||
creator,
|
creator,
|
||||||
state = 'READY',
|
state = 'READY',
|
||||||
|
createdAt,
|
||||||
}: {
|
}: {
|
||||||
creator: Pick<User, 'id' | 'email' | 'name' | 'username'>;
|
creator: Pick<User, 'id' | 'email' | 'name' | 'username'>;
|
||||||
state?: State;
|
state?: State;
|
||||||
|
createdAt?: number;
|
||||||
}) {
|
}) {
|
||||||
const createdAt = Date.now();
|
setupDeploymentEndpoints();
|
||||||
|
|
||||||
|
createdAt = createdAt || Date.now();
|
||||||
const url = new URL(chance().url());
|
const url = new URL(chance().url());
|
||||||
const name = chance().name();
|
const name = chance().name();
|
||||||
const id = `dpl_${chance().guid()}`;
|
const id = `dpl_${chance().guid()}`;
|
||||||
@@ -69,7 +75,7 @@ export function useDeploymentMissingProjectSettings() {
|
|||||||
framework: {
|
framework: {
|
||||||
name: 'Other',
|
name: 'Other',
|
||||||
slug: null,
|
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.',
|
description: 'No framework or an unoptimized framework.',
|
||||||
settings: {
|
settings: {
|
||||||
installCommand: {
|
installCommand: {
|
||||||
@@ -99,6 +105,15 @@ export function useDeploymentMissingProjectSettings() {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
deployments = new Map();
|
deployments = new Map();
|
||||||
deploymentBuilds = new Map();
|
deploymentBuilds = new Map();
|
||||||
|
alreadySetupDeplomentEndpoints = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
function setupDeploymentEndpoints() {
|
||||||
|
if (alreadySetupDeplomentEndpoints) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
alreadySetupDeplomentEndpoints = true;
|
||||||
|
|
||||||
client.scenario.get('/:version/deployments/:id', (req, res) => {
|
client.scenario.get('/:version/deployments/:id', (req, res) => {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
@@ -136,8 +151,21 @@ beforeEach(() => {
|
|||||||
res.json({ builds });
|
res.json({ builds });
|
||||||
});
|
});
|
||||||
|
|
||||||
client.scenario.get('/:version/now/deployments', (req, res) => {
|
function handleGetDeployments(req: Request, res: Response) {
|
||||||
const deploymentsList = Array.from(deployments.values());
|
const currentDeployments = Array.from(deployments.values()).sort(
|
||||||
res.json({ deployments: deploymentsList });
|
(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);
|
const errorBuilds = builds.builds.filter((b: any) => 'error' in b);
|
||||||
expect(errorBuilds).toHaveLength(1);
|
expect(errorBuilds).toHaveLength(1);
|
||||||
|
|
||||||
expect(errorBuilds[0].error.name).toEqual('Error');
|
expect(errorBuilds[0].error).toEqual({
|
||||||
expect(errorBuilds[0].error.message).toMatch(`TS1005`);
|
name: 'Error',
|
||||||
expect(errorBuilds[0].error.message).toMatch(`',' expected.`);
|
message: expect.stringContaining('TS1005'),
|
||||||
expect(errorBuilds[0].error.hideStackTrace).toEqual(true);
|
stack: expect.stringContaining('api/typescript.ts'),
|
||||||
expect(errorBuilds[0].error.code).toEqual('NODE_TYPESCRIPT_ERROR');
|
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`
|
// `config.json` contains `version`
|
||||||
const configJson = await fs.readJSON(join(output, 'config.json'));
|
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 () => {
|
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 cwd = fixture('project-settings-override');
|
||||||
const output = join(cwd, '.vercel/output');
|
const output = join(cwd, '.vercel/output');
|
||||||
try {
|
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 () {
|
describe('should find packages with different main/module/browser keys', function () {
|
||||||
let output: string;
|
let output: string;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
import bytes from 'bytes';
|
||||||
|
import fs from 'fs-extra';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
import { randomBytes } from 'crypto';
|
||||||
import { fileNameSymbol } from '@vercel/client';
|
import { fileNameSymbol } from '@vercel/client';
|
||||||
import { client } from '../../mocks/client';
|
import { client } from '../../mocks/client';
|
||||||
import deploy from '../../../src/commands/deploy';
|
import deploy from '../../../src/commands/deploy';
|
||||||
@@ -199,4 +202,119 @@ describe('deploy', () => {
|
|||||||
process.chdir(originalCwd);
|
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);
|
await list(client);
|
||||||
|
|
||||||
const output = await readOutputStream(client, 4);
|
const output = await readOutputStream(client, 6);
|
||||||
|
|
||||||
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
|
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[2]);
|
||||||
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
|
const header: string[] = parseSpacedTableRow(output.split('\n')[5]);
|
||||||
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
|
const data: string[] = parseSpacedTableRow(output.split('\n')[6]);
|
||||||
data.shift();
|
data.shift();
|
||||||
|
|
||||||
expect(org).toEqual(team[0].slug);
|
expect(org).toEqual(team[0].slug);
|
||||||
@@ -81,11 +81,11 @@ describe('list', () => {
|
|||||||
client.setArgv('-S', user.username);
|
client.setArgv('-S', user.username);
|
||||||
await list(client);
|
await list(client);
|
||||||
|
|
||||||
const output = await readOutputStream(client, 4);
|
const output = await readOutputStream(client, 6);
|
||||||
|
|
||||||
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
|
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[2]);
|
||||||
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
|
const header: string[] = parseSpacedTableRow(output.split('\n')[5]);
|
||||||
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
|
const data: string[] = parseSpacedTableRow(output.split('\n')[6]);
|
||||||
data.shift();
|
data.shift();
|
||||||
|
|
||||||
expect(org).toEqual(user.username);
|
expect(org).toEqual(user.username);
|
||||||
@@ -116,11 +116,11 @@ describe('list', () => {
|
|||||||
client.setArgv(deployment.name);
|
client.setArgv(deployment.name);
|
||||||
await list(client);
|
await list(client);
|
||||||
|
|
||||||
const output = await readOutputStream(client, 4);
|
const output = await readOutputStream(client, 6);
|
||||||
|
|
||||||
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
|
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[2]);
|
||||||
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
|
const header: string[] = parseSpacedTableRow(output.split('\n')[5]);
|
||||||
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
|
const data: string[] = parseSpacedTableRow(output.split('\n')[6]);
|
||||||
data.shift();
|
data.shift();
|
||||||
|
|
||||||
expect(org).toEqual(teamSlug || team[0].slug);
|
expect(org).toEqual(teamSlug || team[0].slug);
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ describe('project', () => {
|
|||||||
client.setArgv('project', 'ls');
|
client.setArgv('project', 'ls');
|
||||||
await projects(client);
|
await projects(client);
|
||||||
|
|
||||||
const output = await readOutputStream(client, 2);
|
const output = await readOutputStream(client, 3);
|
||||||
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
|
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[1]);
|
||||||
const header: string[] = parseSpacedTableRow(output.split('\n')[2]);
|
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
|
||||||
const data: string[] = parseSpacedTableRow(output.split('\n')[3]);
|
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
|
||||||
data.pop();
|
data.pop();
|
||||||
|
|
||||||
expect(org).toEqual(user.username);
|
expect(org).toEqual(user.username);
|
||||||
@@ -47,10 +47,10 @@ describe('project', () => {
|
|||||||
client.setArgv('project', 'ls');
|
client.setArgv('project', 'ls');
|
||||||
await projects(client);
|
await projects(client);
|
||||||
|
|
||||||
const output = await readOutputStream(client, 2);
|
const output = await readOutputStream(client, 3);
|
||||||
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
|
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[1]);
|
||||||
const header: string[] = parseSpacedTableRow(output.split('\n')[2]);
|
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
|
||||||
const data: string[] = parseSpacedTableRow(output.split('\n')[3]);
|
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
|
||||||
data.pop();
|
data.pop();
|
||||||
|
|
||||||
expect(org).toEqual(user.username);
|
expect(org).toEqual(user.username);
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import vercelNodePkg from '@vercel/node/package.json';
|
|||||||
|
|
||||||
jest.setTimeout(ms('30 seconds'));
|
jest.setTimeout(ms('30 seconds'));
|
||||||
|
|
||||||
|
const repoRoot = join(__dirname, '../../../../../..');
|
||||||
|
|
||||||
describe('importBuilders()', () => {
|
describe('importBuilders()', () => {
|
||||||
it('should import built-in Builders', async () => {
|
it('should import built-in Builders', async () => {
|
||||||
const specs = new Set(['@vercel/node', '@vercel/next']);
|
const specs = new Set(['@vercel/node', '@vercel/next']);
|
||||||
@@ -19,6 +21,12 @@ describe('importBuilders()', () => {
|
|||||||
expect(builders.size).toEqual(2);
|
expect(builders.size).toEqual(2);
|
||||||
expect(builders.get('@vercel/node')?.pkg).toMatchObject(vercelNodePkg);
|
expect(builders.get('@vercel/node')?.pkg).toMatchObject(vercelNodePkg);
|
||||||
expect(builders.get('@vercel/next')?.pkg).toMatchObject(vercelNextPkg);
|
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(
|
expect(typeof builders.get('@vercel/node')?.builder.build).toEqual(
|
||||||
'function'
|
'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();
|
const cwd = await getWriteableDirectory();
|
||||||
try {
|
try {
|
||||||
const spec = 'vercel-deno@2.0.1';
|
const spec = '@vercel/node@2.0.0';
|
||||||
const specs = new Set([spec]);
|
const specs = new Set([spec]);
|
||||||
const builders = await importBuilders(specs, cwd, client.output);
|
const builders = await importBuilders(specs, cwd, client.output);
|
||||||
expect(builders.size).toEqual(1);
|
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.name).toEqual('vercel-deno');
|
||||||
expect(builders.get(spec)?.pkg.version).toEqual('2.0.1');
|
expect(builders.get(spec)?.pkg.version).toEqual('2.0.1');
|
||||||
expect(builders.get(spec)?.pkgPath).toEqual(
|
expect(builders.get(spec)?.pkgPath).toEqual(
|
||||||
join(cwd, '.vercel/builders/node_modules/vercel-deno/package.json')
|
join(cwd, '.vercel/builders/node_modules/vercel-deno/package.json')
|
||||||
);
|
);
|
||||||
expect(typeof builders.get(spec)?.builder.build).toEqual('function');
|
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 {
|
} finally {
|
||||||
await remove(cwd);
|
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();
|
const cwd = await getWriteableDirectory();
|
||||||
try {
|
try {
|
||||||
const spec = '@frontity/now@1.2.0';
|
const spec = '@frontity/now@1.2.0';
|
||||||
@@ -62,6 +169,31 @@ describe('importBuilders()', () => {
|
|||||||
await remove(cwd);
|
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()', () => {
|
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';
|
const IS_WINDOWS = process.platform === 'win32';
|
||||||
|
|
||||||
@@ -69,3 +72,15 @@ describe('parseListen', () => {
|
|||||||
expect(err.message).toEqual('Unknown `--listen` scheme (protocol): bad:');
|
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', () => {
|
describe('validateConfig', () => {
|
||||||
it('should not error with empty config', async () => {
|
it('should not error with empty config', async () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/client",
|
"name": "@vercel/client",
|
||||||
"version": "12.2.3",
|
"version": "12.2.13",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"typings": "dist/index.d.ts",
|
"typings": "dist/index.d.ts",
|
||||||
"homepage": "https://vercel.com",
|
"homepage": "https://vercel.com",
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "5.4.1",
|
"@vercel/build-utils": "5.5.5",
|
||||||
"@vercel/routing-utils": "2.0.2",
|
"@vercel/routing-utils": "2.0.2",
|
||||||
"@zeit/fetch": "5.2.0",
|
"@zeit/fetch": "5.2.0",
|
||||||
"async-retry": "1.2.3",
|
"async-retry": "1.2.3",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type {
|
import type {
|
||||||
Builder,
|
Builder,
|
||||||
BuilderFunctions,
|
BuilderFunctions,
|
||||||
|
Images,
|
||||||
ProjectSettings,
|
ProjectSettings,
|
||||||
} from '@vercel/build-utils';
|
} from '@vercel/build-utils';
|
||||||
import type { Header, Route, Redirect, Rewrite } from '@vercel/routing-utils';
|
import type { Header, Route, Redirect, Rewrite } from '@vercel/routing-utils';
|
||||||
@@ -152,6 +153,7 @@ export interface VercelConfig {
|
|||||||
installCommand?: string | null;
|
installCommand?: string | null;
|
||||||
framework?: string | null;
|
framework?: string | null;
|
||||||
outputDirectory?: string | null;
|
outputDirectory?: string | null;
|
||||||
|
images?: Images;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GitMetadata {
|
export interface GitMetadata {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Agent } from 'https';
|
import http from 'http';
|
||||||
|
import https from 'https';
|
||||||
import { Readable } from 'stream';
|
import { Readable } from 'stream';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import retry from 'async-retry';
|
import retry from 'async-retry';
|
||||||
@@ -78,7 +79,9 @@ export async function* upload(
|
|||||||
debug('Building an upload list...');
|
debug('Building an upload list...');
|
||||||
|
|
||||||
const semaphore = new Sema(50, { capacity: 50 });
|
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) => {
|
shas.forEach((sha, index) => {
|
||||||
const uploadProgress = uploads[index];
|
const uploadProgress = uploads[index];
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M22.428.013c-.103.01-.431.042-.727.066C14.883.693 8.497 4.37 4.453 10.024A23.754 23.754 0 0 0 .216 20.51C.023 21.828 0 22.217 0 24.005c0 1.787.023 2.177.216 3.495 1.304 9.012 7.718 16.584 16.417 19.39 1.558.501 3.2.844 5.068 1.05.727.08 3.87.08 4.598 0 3.224-.356 5.954-1.154 8.648-2.529.412-.21.492-.267.436-.314-.038-.028-1.797-2.388-3.909-5.24l-3.838-5.184-4.809-7.117c-2.646-3.913-4.824-7.112-4.842-7.112-.019-.005-.038 3.157-.047 7.018-.014 6.76-.019 7.033-.103 7.192-.122.23-.216.324-.413.427-.15.075-.282.09-.99.09h-.812l-.216-.137a.878.878 0 0 1-.314-.342l-.099-.211.01-9.407.014-9.41.145-.184c.075-.098.235-.225.347-.286.193-.094.268-.103 1.08-.103.957 0 1.116.038 1.365.31.07.075 2.674 3.997 5.79 8.721s7.376 11.175 9.469 14.342l3.8 5.756.192-.127c1.704-1.107 3.505-2.683 4.932-4.325a23.888 23.888 0 0 0 5.65-12.268c.192-1.319.215-1.708.215-3.495 0-1.788-.023-2.177-.216-3.495-1.304-9.013-7.718-16.584-16.417-19.39C29.832.623 28.199.28 26.369.074c-.45-.047-3.551-.099-3.94-.061zm9.825 14.515a.947.947 0 0 1 .474.554c.038.122.047 2.73.038 8.608l-.014 8.436-1.488-2.28-1.492-2.28v-6.132c0-3.964.019-6.193.047-6.3a.957.957 0 0 1 .465-.592c.192-.098.262-.108 1-.108.694 0 .816.01.97.094z" fill="#fff"/></svg>
|
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><mask id="mask" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="48" height="48"><circle cx="24" cy="24" r="24" fill="#000"/></mask><g mask="url(#mask)"><circle cx="24" cy="24" r="23.2" fill="#000" stroke="#fff" stroke-width="1.6"/><path d="M39.8687 42.0055L18.4378 14.4H14.3999V33.592H17.6302V18.5023L37.333 43.9587C38.222 43.3637 39.069 42.7108 39.8687 42.0055Z" fill="url(#gradient0)"/><rect x="30.6667" y="14.4" width="3.2" height="19.2" fill="url(#gradient1)"/></g><defs><linearGradient id="gradient0" x1="29.0666" y1="31.0667" x2="38.5332" y2="42.8" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient><linearGradient id="gradient1" x1="32.2667" y1="14.4" x2="32.2132" y2="28.5001" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient></defs></svg>
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 997 B |
@@ -1 +1 @@
|
|||||||
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M22.428.013c-.103.01-.431.042-.727.066C14.883.693 8.497 4.37 4.453 10.024A23.754 23.754 0 0 0 .216 20.51C.023 21.828 0 22.217 0 24.005c0 1.787.023 2.177.216 3.495 1.304 9.012 7.718 16.584 16.417 19.39 1.558.501 3.2.844 5.068 1.05.727.08 3.87.08 4.598 0 3.224-.356 5.954-1.154 8.648-2.529.412-.21.492-.267.436-.314-.038-.028-1.797-2.388-3.909-5.24l-3.838-5.184-4.809-7.117c-2.646-3.913-4.824-7.112-4.842-7.112-.019-.005-.038 3.157-.047 7.018-.014 6.76-.019 7.033-.103 7.192-.122.23-.216.324-.413.427-.15.075-.282.09-.99.09h-.812l-.216-.137a.878.878 0 0 1-.314-.342l-.099-.211.01-9.407.014-9.41.145-.184c.075-.098.235-.225.347-.286.193-.094.268-.103 1.08-.103.957 0 1.116.038 1.365.31.07.075 2.674 3.997 5.79 8.721s7.376 11.175 9.469 14.342l3.8 5.756.192-.127c1.704-1.107 3.505-2.683 4.932-4.325a23.888 23.888 0 0 0 5.65-12.268c.192-1.319.215-1.708.215-3.495 0-1.788-.023-2.177-.216-3.495-1.304-9.013-7.718-16.584-16.417-19.39C29.832.623 28.199.28 26.369.074c-.45-.047-3.551-.099-3.94-.061zm9.825 14.515a.947.947 0 0 1 .474.554c.038.122.047 2.73.038 8.608l-.014 8.436-1.488-2.28-1.492-2.28v-6.132c0-3.964.019-6.193.047-6.3a.957.957 0 0 1 .465-.592c.192-.098.262-.108 1-.108.694 0 .816.01.97.094z" fill="#000"/></svg>
|
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><mask id="mask" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="48" height="48"><circle cx="24" cy="24" r="24" fill="#000"/></mask><g mask="url(#mask)"><circle cx="24" cy="24" r="23.2" fill="#000" stroke="#000" stroke-width="1.6"/><path d="M39.8687 42.0055L18.4378 14.4H14.3999V33.592H17.6302V18.5023L37.333 43.9587C38.222 43.3637 39.069 42.7108 39.8687 42.0055Z" fill="url(#gradient0)"/><rect x="30.6667" y="14.4" width="3.2" height="19.2" fill="url(#gradient1)"/></g><defs><linearGradient id="gradient0" x1="29.0666" y1="31.0667" x2="38.5332" y2="42.8" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient><linearGradient id="gradient1" x1="32.2667" y1="14.4" x2="32.2132" y2="28.5001" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient></defs></svg>
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 997 B |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/frameworks",
|
"name": "@vercel/frameworks",
|
||||||
"version": "1.1.3",
|
"version": "1.1.8",
|
||||||
"main": "./dist/frameworks.js",
|
"main": "./dist/frameworks.js",
|
||||||
"types": "./dist/frameworks.d.ts",
|
"types": "./dist/frameworks.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const frameworks = [
|
|||||||
name: 'Blitz.js',
|
name: 'Blitz.js',
|
||||||
slug: 'blitzjs',
|
slug: 'blitzjs',
|
||||||
demo: 'https://blitz-template.vercel.app',
|
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',
|
tagline: 'Blitz.js: The Fullstack React Framework',
|
||||||
description:
|
description:
|
||||||
'A brand new Blitz.js app - the result of running `npx blitz new`.',
|
'A brand new Blitz.js app - the result of running `npx blitz new`.',
|
||||||
@@ -58,9 +58,9 @@ export const frameworks = [
|
|||||||
name: 'Next.js',
|
name: 'Next.js',
|
||||||
slug: 'nextjs',
|
slug: 'nextjs',
|
||||||
demo: 'https://nextjs-template.vercel.app',
|
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:
|
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:
|
screenshot:
|
||||||
'https://assets.vercel.com/image/upload/v1647366075/front/import/nextjs.png',
|
'https://assets.vercel.com/image/upload/v1647366075/front/import/nextjs.png',
|
||||||
tagline:
|
tagline:
|
||||||
@@ -108,7 +108,7 @@ export const frameworks = [
|
|||||||
name: 'Gatsby.js',
|
name: 'Gatsby.js',
|
||||||
slug: 'gatsby',
|
slug: 'gatsby',
|
||||||
demo: 'https://gatsby.vercel.app',
|
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:
|
tagline:
|
||||||
'Gatsby helps developers build blazing fast websites and apps with React.',
|
'Gatsby helps developers build blazing fast websites and apps with React.',
|
||||||
description: 'A Gatsby starter app with an API Route.',
|
description: 'A Gatsby starter app with an API Route.',
|
||||||
@@ -195,7 +195,7 @@ export const frameworks = [
|
|||||||
name: 'Remix',
|
name: 'Remix',
|
||||||
slug: 'remix',
|
slug: 'remix',
|
||||||
demo: 'https://remix-run-template.vercel.app',
|
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',
|
tagline: 'Build Better Websites',
|
||||||
description: 'A new Remix app — the result of running `npx create-remix`.',
|
description: 'A new Remix app — the result of running `npx create-remix`.',
|
||||||
website: 'https://remix.run',
|
website: 'https://remix.run',
|
||||||
@@ -232,9 +232,9 @@ export const frameworks = [
|
|||||||
name: 'Astro',
|
name: 'Astro',
|
||||||
slug: 'astro',
|
slug: 'astro',
|
||||||
demo: 'https://astro-template.vercel.app',
|
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:
|
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:
|
tagline:
|
||||||
'Astro is a new kind of static site builder for the modern web. Powerful developer experience meets lightweight output.',
|
'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.',
|
description: 'An Astro site, using the basics starter kit.',
|
||||||
@@ -286,7 +286,7 @@ export const frameworks = [
|
|||||||
name: 'Hexo',
|
name: 'Hexo',
|
||||||
slug: 'hexo',
|
slug: 'hexo',
|
||||||
demo: 'https://hexo-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Hexo is a fast, simple & powerful blog framework powered by Node.js.',
|
'Hexo is a fast, simple & powerful blog framework powered by Node.js.',
|
||||||
description: 'A Hexo site, created with the Hexo CLI.',
|
description: 'A Hexo site, created with the Hexo CLI.',
|
||||||
@@ -323,7 +323,7 @@ export const frameworks = [
|
|||||||
name: 'Eleventy',
|
name: 'Eleventy',
|
||||||
slug: 'eleventy',
|
slug: 'eleventy',
|
||||||
demo: 'https://eleventy-template.vercel.app',
|
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:
|
tagline:
|
||||||
'11ty is a simpler static site generator written in JavaScript, created to be an alternative to Jekyll.',
|
'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.',
|
description: 'An Eleventy site, created with npm init.',
|
||||||
@@ -361,7 +361,7 @@ export const frameworks = [
|
|||||||
name: 'Docusaurus 2',
|
name: 'Docusaurus 2',
|
||||||
slug: 'docusaurus-2',
|
slug: 'docusaurus-2',
|
||||||
demo: 'https://docusaurus-2-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Docusaurus makes it easy to maintain Open Source documentation websites.',
|
'Docusaurus makes it easy to maintain Open Source documentation websites.',
|
||||||
description:
|
description:
|
||||||
@@ -449,7 +449,7 @@ export const frameworks = [
|
|||||||
name: 'Docusaurus 1',
|
name: 'Docusaurus 1',
|
||||||
slug: 'docusaurus',
|
slug: 'docusaurus',
|
||||||
demo: 'https://docusaurus-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Docusaurus makes it easy to maintain Open Source documentation websites.',
|
'Docusaurus makes it easy to maintain Open Source documentation websites.',
|
||||||
description:
|
description:
|
||||||
@@ -501,7 +501,7 @@ export const frameworks = [
|
|||||||
name: 'Preact',
|
name: 'Preact',
|
||||||
slug: 'preact',
|
slug: 'preact',
|
||||||
demo: 'https://preact-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Preact is a fast 3kB alternative to React with the same modern API.',
|
'Preact is a fast 3kB alternative to React with the same modern API.',
|
||||||
description: 'A Preact app, created with the Preact CLI.',
|
description: 'A Preact app, created with the Preact CLI.',
|
||||||
@@ -547,7 +547,7 @@ export const frameworks = [
|
|||||||
name: 'SolidStart',
|
name: 'SolidStart',
|
||||||
slug: 'solidstart',
|
slug: 'solidstart',
|
||||||
demo: 'https://solid-start-template.vercel.app',
|
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.',
|
tagline: 'Simple and performant reactivity for building user interfaces.',
|
||||||
description: 'A Solid app, created with SolidStart.',
|
description: 'A Solid app, created with SolidStart.',
|
||||||
website: 'https://solidjs.com',
|
website: 'https://solidjs.com',
|
||||||
@@ -587,7 +587,7 @@ export const frameworks = [
|
|||||||
name: 'Dojo',
|
name: 'Dojo',
|
||||||
slug: 'dojo',
|
slug: 'dojo',
|
||||||
demo: 'https://dojo-template.vercel.app',
|
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.',
|
tagline: 'Dojo is a modern progressive, TypeScript first framework.',
|
||||||
description:
|
description:
|
||||||
"A Dojo app, created with the Dojo CLI's cli-create-app command.",
|
"A Dojo app, created with the Dojo CLI's cli-create-app command.",
|
||||||
@@ -649,7 +649,7 @@ export const frameworks = [
|
|||||||
name: 'Ember.js',
|
name: 'Ember.js',
|
||||||
slug: 'ember',
|
slug: 'ember',
|
||||||
demo: 'https://ember-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Ember.js helps webapp developers be more productive out of the box.',
|
'Ember.js helps webapp developers be more productive out of the box.',
|
||||||
description: 'An Ember app, created with the Ember CLI.',
|
description: 'An Ember app, created with the Ember CLI.',
|
||||||
@@ -695,7 +695,7 @@ export const frameworks = [
|
|||||||
name: 'Vue.js',
|
name: 'Vue.js',
|
||||||
slug: 'vue',
|
slug: 'vue',
|
||||||
demo: 'https://vue-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Vue.js is a versatile JavaScript framework that is as approachable as it is performant.',
|
'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.',
|
description: 'A Vue.js app, created with the Vue CLI.',
|
||||||
@@ -752,7 +752,7 @@ export const frameworks = [
|
|||||||
name: 'Scully',
|
name: 'Scully',
|
||||||
slug: 'scully',
|
slug: 'scully',
|
||||||
demo: 'https://scully-template.vercel.app',
|
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.',
|
tagline: 'Scully is a static site generator for Angular.',
|
||||||
description: 'The Static Site Generator for Angular apps.',
|
description: 'The Static Site Generator for Angular apps.',
|
||||||
website: 'https://github.com/scullyio/scully',
|
website: 'https://github.com/scullyio/scully',
|
||||||
@@ -788,7 +788,7 @@ export const frameworks = [
|
|||||||
name: 'Ionic Angular',
|
name: 'Ionic Angular',
|
||||||
slug: 'ionic-angular',
|
slug: 'ionic-angular',
|
||||||
demo: 'https://ionic-angular-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Ionic Angular allows you to build mobile PWAs with Angular and the Ionic Framework.',
|
'Ionic Angular allows you to build mobile PWAs with Angular and the Ionic Framework.',
|
||||||
description: 'An Ionic Angular site, created with the Ionic CLI.',
|
description: 'An Ionic Angular site, created with the Ionic CLI.',
|
||||||
@@ -833,7 +833,7 @@ export const frameworks = [
|
|||||||
name: 'Angular',
|
name: 'Angular',
|
||||||
slug: 'angular',
|
slug: 'angular',
|
||||||
demo: 'https://angular-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Angular is a TypeScript-based cross-platform framework from Google.',
|
'Angular is a TypeScript-based cross-platform framework from Google.',
|
||||||
description: 'An Angular app, created with the Angular CLI.',
|
description: 'An Angular app, created with the Angular CLI.',
|
||||||
@@ -893,7 +893,7 @@ export const frameworks = [
|
|||||||
name: 'Polymer',
|
name: 'Polymer',
|
||||||
slug: 'polymer',
|
slug: 'polymer',
|
||||||
demo: 'https://polymer-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Polymer is an open-source webapps library from Google, for building using Web Components.',
|
'Polymer is an open-source webapps library from Google, for building using Web Components.',
|
||||||
description: 'A Polymer app, created with the Polymer CLI.',
|
description: 'A Polymer app, created with the Polymer CLI.',
|
||||||
@@ -950,7 +950,7 @@ export const frameworks = [
|
|||||||
name: 'Svelte',
|
name: 'Svelte',
|
||||||
slug: 'svelte',
|
slug: 'svelte',
|
||||||
demo: 'https://svelte.vercel.app',
|
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:
|
tagline:
|
||||||
'Svelte lets you write high performance reactive apps with significantly less boilerplate.',
|
'Svelte lets you write high performance reactive apps with significantly less boilerplate.',
|
||||||
description: 'A basic Svelte app using the default template.',
|
description: 'A basic Svelte app using the default template.',
|
||||||
@@ -1001,7 +1001,7 @@ export const frameworks = [
|
|||||||
name: 'SvelteKit',
|
name: 'SvelteKit',
|
||||||
slug: 'sveltekit',
|
slug: 'sveltekit',
|
||||||
demo: 'https://sveltekit-template.vercel.app',
|
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:
|
screenshot:
|
||||||
'https://assets.vercel.com/image/upload/v1647366075/front/import/sveltekit.png',
|
'https://assets.vercel.com/image/upload/v1647366075/front/import/sveltekit.png',
|
||||||
tagline:
|
tagline:
|
||||||
@@ -1040,7 +1040,7 @@ export const frameworks = [
|
|||||||
name: 'Ionic React',
|
name: 'Ionic React',
|
||||||
slug: 'ionic-react',
|
slug: 'ionic-react',
|
||||||
demo: 'https://ionic-react-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Ionic React allows you to build mobile PWAs with React and the Ionic Framework.',
|
'Ionic React allows you to build mobile PWAs with React and the Ionic Framework.',
|
||||||
description: 'An Ionic React site, created with the Ionic CLI.',
|
description: 'An Ionic React site, created with the Ionic CLI.',
|
||||||
@@ -1101,7 +1101,7 @@ export const frameworks = [
|
|||||||
name: 'Create React App',
|
name: 'Create React App',
|
||||||
slug: 'create-react-app',
|
slug: 'create-react-app',
|
||||||
demo: 'https://create-react-template.vercel.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.',
|
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.',
|
description: 'A client-side React app created with create-react-app.',
|
||||||
website: 'https://create-react-app.dev',
|
website: 'https://create-react-app.dev',
|
||||||
@@ -1168,7 +1168,7 @@ export const frameworks = [
|
|||||||
name: 'Gridsome',
|
name: 'Gridsome',
|
||||||
slug: 'gridsome',
|
slug: 'gridsome',
|
||||||
demo: 'https://gridsome-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Gridsome is a Vue.js-powered framework for building websites & apps that are fast by default.',
|
'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.',
|
description: 'A Gridsome app, created with the Gridsome CLI.',
|
||||||
@@ -1205,7 +1205,7 @@ export const frameworks = [
|
|||||||
name: 'UmiJS',
|
name: 'UmiJS',
|
||||||
slug: 'umijs',
|
slug: 'umijs',
|
||||||
demo: 'https://umijs-template.vercel.app',
|
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:
|
tagline:
|
||||||
'UmiJS is an extensible enterprise-level React application framework.',
|
'UmiJS is an extensible enterprise-level React application framework.',
|
||||||
description: 'An UmiJS app, created using the Umi CLI.',
|
description: 'An UmiJS app, created using the Umi CLI.',
|
||||||
@@ -1251,7 +1251,7 @@ export const frameworks = [
|
|||||||
name: 'Sapper',
|
name: 'Sapper',
|
||||||
slug: 'sapper',
|
slug: 'sapper',
|
||||||
demo: 'https://sapper-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Sapper is a framework for building high-performance universal web apps with Svelte.',
|
'Sapper is a framework for building high-performance universal web apps with Svelte.',
|
||||||
description: 'A Sapper app, using the Sapper template.',
|
description: 'A Sapper app, using the Sapper template.',
|
||||||
@@ -1288,7 +1288,7 @@ export const frameworks = [
|
|||||||
name: 'Saber',
|
name: 'Saber',
|
||||||
slug: 'saber',
|
slug: 'saber',
|
||||||
demo: 'https://saber-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Saber is a framework for building static sites in Vue.js that supports data from any source.',
|
'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.',
|
description: 'A Saber site, created with npm init.',
|
||||||
@@ -1339,7 +1339,7 @@ export const frameworks = [
|
|||||||
name: 'Stencil',
|
name: 'Stencil',
|
||||||
slug: 'stencil',
|
slug: 'stencil',
|
||||||
demo: 'https://stencil.vercel.app',
|
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:
|
tagline:
|
||||||
'Stencil is a powerful toolchain for building Progressive Web Apps and Design Systems.',
|
'Stencil is a powerful toolchain for building Progressive Web Apps and Design Systems.',
|
||||||
description: 'A Stencil site, created with the Stencil CLI.',
|
description: 'A Stencil site, created with the Stencil CLI.',
|
||||||
@@ -1400,7 +1400,7 @@ export const frameworks = [
|
|||||||
name: 'Nuxt.js',
|
name: 'Nuxt.js',
|
||||||
slug: 'nuxtjs',
|
slug: 'nuxtjs',
|
||||||
demo: 'https://nuxtjs-template.vercel.app',
|
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:
|
screenshot:
|
||||||
'https://assets.vercel.com/image/upload/v1647366075/front/import/nuxtjs.png',
|
'https://assets.vercel.com/image/upload/v1647366075/front/import/nuxtjs.png',
|
||||||
tagline:
|
tagline:
|
||||||
@@ -1460,7 +1460,7 @@ export const frameworks = [
|
|||||||
name: 'RedwoodJS',
|
name: 'RedwoodJS',
|
||||||
slug: 'redwoodjs',
|
slug: 'redwoodjs',
|
||||||
demo: 'https://redwood-template.vercel.app',
|
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.',
|
tagline: 'RedwoodJS is a full-stack framework for the Jamstack.',
|
||||||
description: 'A RedwoodJS app, bootstraped with create-redwood-app.',
|
description: 'A RedwoodJS app, bootstraped with create-redwood-app.',
|
||||||
website: 'https://redwoodjs.com',
|
website: 'https://redwoodjs.com',
|
||||||
@@ -1497,7 +1497,7 @@ export const frameworks = [
|
|||||||
name: 'Hugo',
|
name: 'Hugo',
|
||||||
slug: 'hugo',
|
slug: 'hugo',
|
||||||
demo: 'https://hugo-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Hugo is the world’s fastest framework for building websites, written in Go.',
|
'Hugo is the world’s fastest framework for building websites, written in Go.',
|
||||||
description: 'A Hugo site, created with the Hugo CLI.',
|
description: 'A Hugo site, created with the Hugo CLI.',
|
||||||
@@ -1550,7 +1550,7 @@ export const frameworks = [
|
|||||||
name: 'Jekyll',
|
name: 'Jekyll',
|
||||||
slug: 'jekyll',
|
slug: 'jekyll',
|
||||||
demo: 'https://jekyll-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Jekyll makes it super easy to transform your plain text into static websites and blogs.',
|
'Jekyll makes it super easy to transform your plain text into static websites and blogs.',
|
||||||
description: 'A Jekyll site, created with the Jekyll CLI.',
|
description: 'A Jekyll site, created with the Jekyll CLI.',
|
||||||
@@ -1591,7 +1591,7 @@ export const frameworks = [
|
|||||||
name: 'Brunch',
|
name: 'Brunch',
|
||||||
slug: 'brunch',
|
slug: 'brunch',
|
||||||
demo: 'https://brunch-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Brunch is a fast and simple webapp build tool with seamless incremental compilation for rapid development.',
|
'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.',
|
description: 'A Brunch app, created with the Brunch CLI.',
|
||||||
@@ -1625,7 +1625,7 @@ export const frameworks = [
|
|||||||
name: 'Middleman',
|
name: 'Middleman',
|
||||||
slug: 'middleman',
|
slug: 'middleman',
|
||||||
demo: 'https://middleman-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Middleman is a static site generator that uses all the shortcuts and tools in modern web development.',
|
'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.',
|
description: 'A Middleman app, created with the Middleman CLI.',
|
||||||
@@ -1660,7 +1660,7 @@ export const frameworks = [
|
|||||||
name: 'Zola',
|
name: 'Zola',
|
||||||
slug: 'zola',
|
slug: 'zola',
|
||||||
demo: 'https://zola-template.vercel.app',
|
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.',
|
tagline: 'Everything you need to make a static site engine in one binary.',
|
||||||
description: 'A Zola app, created with the "Getting Started" tutorial.',
|
description: 'A Zola app, created with the "Getting Started" tutorial.',
|
||||||
website: 'https://www.getzola.org',
|
website: 'https://www.getzola.org',
|
||||||
@@ -1694,7 +1694,7 @@ export const frameworks = [
|
|||||||
name: 'Vite',
|
name: 'Vite',
|
||||||
slug: 'vite',
|
slug: 'vite',
|
||||||
demo: 'https://vite-vue-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Vite is a new breed of frontend build tool that significantly improves the frontend development experience.',
|
'Vite is a new breed of frontend build tool that significantly improves the frontend development experience.',
|
||||||
description: 'A Vue.js app, created with Vite.',
|
description: 'A Vue.js app, created with Vite.',
|
||||||
@@ -1732,7 +1732,7 @@ export const frameworks = [
|
|||||||
name: 'Parcel',
|
name: 'Parcel',
|
||||||
slug: 'parcel',
|
slug: 'parcel',
|
||||||
demo: 'https://parcel-template.vercel.app',
|
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:
|
tagline:
|
||||||
'Parcel is a zero configuration build tool for the web that scales to projects of any size and complexity.',
|
'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.',
|
description: 'A vanilla web app built with Parcel.',
|
||||||
@@ -1779,16 +1779,28 @@ export const frameworks = [
|
|||||||
name: 'Sanity',
|
name: 'Sanity',
|
||||||
slug: 'sanity',
|
slug: 'sanity',
|
||||||
demo: 'https://sanity-studio-template.vercel.app',
|
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.',
|
tagline: 'The structured content platform.',
|
||||||
description: 'A Sanity Studio',
|
description: 'A Sanity Studio',
|
||||||
website: 'https://www.sanity.io',
|
website: 'https://www.sanity.io',
|
||||||
envPrefix: 'SANITY_STUDIO_',
|
envPrefix: 'SANITY_STUDIO_',
|
||||||
detectors: {
|
detectors: {
|
||||||
every: [
|
some: [
|
||||||
{
|
{
|
||||||
path: 'sanity.json',
|
path: 'sanity.json',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'sanity.config.js',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'sanity.config.jsx',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'sanity.config.ts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'sanity.config.tsx',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
@@ -1822,16 +1834,19 @@ export const frameworks = [
|
|||||||
name: 'Hydrogen',
|
name: 'Hydrogen',
|
||||||
slug: 'hydrogen',
|
slug: 'hydrogen',
|
||||||
demo: 'https://hydrogen-template.vercel.app',
|
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',
|
tagline: 'React framework for headless commerce',
|
||||||
description: 'React framework for headless commerce',
|
description: 'React framework for headless commerce',
|
||||||
website: 'https://hydrogen.shopify.dev',
|
website: 'https://hydrogen.shopify.dev',
|
||||||
useRuntime: { src: 'package.json', use: '@vercel/hydrogen' },
|
useRuntime: { src: 'package.json', use: '@vercel/hydrogen' },
|
||||||
detectors: {
|
detectors: {
|
||||||
every: [
|
some: [
|
||||||
{
|
{
|
||||||
path: 'hydrogen.config.js',
|
path: 'hydrogen.config.js',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'hydrogen.config.ts',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
@@ -1856,7 +1871,7 @@ export const frameworks = [
|
|||||||
{
|
{
|
||||||
name: 'Other',
|
name: 'Other',
|
||||||
slug: null,
|
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.',
|
description: 'No framework or an unoptimized framework.',
|
||||||
settings: {
|
settings: {
|
||||||
installCommand: {
|
installCommand: {
|
||||||
|
|||||||
@@ -53,12 +53,12 @@ export interface Framework {
|
|||||||
slug: string | null;
|
slug: string | null;
|
||||||
/**
|
/**
|
||||||
* A URL to the logo of the framework
|
* 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;
|
logo: string;
|
||||||
/**
|
/**
|
||||||
* An additional URL to the logo of the framework optimized for dark mode
|
* 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;
|
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
|
// bump timeout for Windows as network can be slower
|
||||||
jest.setTimeout(15 * 1000);
|
jest.setTimeout(15 * 1000);
|
||||||
|
|
||||||
|
const logoPrefix = 'https://api-frameworks.vercel.sh/framework-logos/';
|
||||||
|
|
||||||
const SchemaFrameworkDetectionItem = {
|
const SchemaFrameworkDetectionItem = {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: [
|
items: [
|
||||||
@@ -172,14 +174,33 @@ describe('frameworks', () => {
|
|||||||
expect(result).toBe(true);
|
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
|
const missing = frameworkList
|
||||||
.map(f => f.logo)
|
.map(f => f.logo)
|
||||||
.filter(url => {
|
.filter(logo => {
|
||||||
const prefix =
|
const filename = logo.slice(logoPrefix.length);
|
||||||
'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/';
|
const filepath = join(__dirname, '..', 'logos', filename);
|
||||||
const name = url.replace(prefix, '');
|
return existsSync(filepath) === false;
|
||||||
return existsSync(join(__dirname, '..', 'logos', name)) === false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(missing).toEqual([]);
|
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 |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user