mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-29 19:00:09 +00:00
Compare commits
52 Commits
@vercel/fr
...
vercel@29.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46950633f4 | ||
|
|
44b1dfe7c5 | ||
|
|
0278c7b7b9 | ||
|
|
761db597be | ||
|
|
671e63e7b8 | ||
|
|
f7bdc6cc26 | ||
|
|
e94a153b2f | ||
|
|
74e639a772 | ||
|
|
f00b08a820 | ||
|
|
6cdd38d130 | ||
|
|
2c950d47ae | ||
|
|
71b9f3a94b | ||
|
|
91b7f6dcd9 | ||
|
|
ba10fb4dd4 | ||
|
|
18c1c45ce3 | ||
|
|
67e556bc80 | ||
|
|
7235000181 | ||
|
|
5124d431ea | ||
|
|
c879401bbc | ||
|
|
6c6f3ce9d2 | ||
|
|
aa83680832 | ||
|
|
06113d3e39 | ||
|
|
5150f21404 | ||
|
|
eb6bb98406 | ||
|
|
d8e3b6e738 | ||
|
|
25da051d62 | ||
|
|
f57af66dc2 | ||
|
|
b2f71d5352 | ||
|
|
6115f0d74a | ||
|
|
14c877e468 | ||
|
|
d80732d74f | ||
|
|
b739c1845c | ||
|
|
e9f0fcf397 | ||
|
|
924a20a0fc | ||
|
|
fc3b74d06f | ||
|
|
380ed38c71 | ||
|
|
e228cdd373 | ||
|
|
9bd92535d6 | ||
|
|
5825e30700 | ||
|
|
6d5983eaae | ||
|
|
2fd59a3b5a | ||
|
|
d1d3e9384d | ||
|
|
8428632eb1 | ||
|
|
fa443035f6 | ||
|
|
de2c7e1633 | ||
|
|
75d2435138 | ||
|
|
5328bb69e2 | ||
|
|
e3fe368baa | ||
|
|
99832587c5 | ||
|
|
47d0d4f84a | ||
|
|
aba54ee6cf | ||
|
|
cd7d3ef1c5 |
8
.changeset/README.md
Normal file
8
.changeset/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in
|
||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
||||
14
.changeset/config.json
Normal file
14
.changeset/config.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
|
||||
"changelog": [
|
||||
"@svitejs/changesets-changelog-github-compact",
|
||||
{ "repo": "vercel/vercel" }
|
||||
],
|
||||
"commit": false,
|
||||
"fixed": [],
|
||||
"linked": [],
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": []
|
||||
}
|
||||
@@ -1,72 +1,72 @@
|
||||
name: Publish
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- '!*'
|
||||
|
||||
env:
|
||||
TURBO_REMOTE_ONLY: 'true'
|
||||
TURBO_TEAM: 'vercel'
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Check Release
|
||||
id: check-release
|
||||
run: |
|
||||
tag="$(git describe --tags --exact-match 2> /dev/null || :)"
|
||||
if [[ -z "$tag" ]];
|
||||
then
|
||||
echo "IS_RELEASE=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "IS_RELEASE=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Setup Go
|
||||
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.13.15'
|
||||
|
||||
- name: Setup Node
|
||||
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: install npm@9
|
||||
run: npm i -g npm@9
|
||||
|
||||
- name: install pnpm@8.3.1
|
||||
run: npm i -g pnpm@8.3.1
|
||||
- name: Install
|
||||
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
|
||||
run: pnpm install
|
||||
- name: Build
|
||||
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build Packages
|
||||
run: pnpm build
|
||||
env:
|
||||
GA_TRACKING_ID: ${{ secrets.GA_TRACKING_ID }}
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
- name: Publish
|
||||
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
|
||||
run: pnpm publish-from-github
|
||||
|
||||
- name: Create Release Pull Request or Publish to npm
|
||||
id: changesets
|
||||
uses: changesets/action@v1
|
||||
with:
|
||||
version: pnpm version:prepare
|
||||
publish: pnpm release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
|
||||
NPM_CONFIG_PROVENANCE: 'true'
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }}
|
||||
GA_TRACKING_ID: ${{ secrets.GA_TRACKING_ID }}
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
- name: Trigger Update
|
||||
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
|
||||
|
||||
- name: Set latest Release to `vercel` (if a Publish Happened)
|
||||
if: steps.changesets.outputs.published == 'true'
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
|
||||
script: |
|
||||
const script = require('./utils/update-latest-release.js')
|
||||
await script({ github, context })
|
||||
|
||||
- name: Trigger Update (if a Publish Happened)
|
||||
if: steps.changesets.outputs.published == 'true'
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
|
||||
26
.github/workflows/required-pr-label.yml
vendored
26
.github/workflows/required-pr-label.yml
vendored
@@ -1,26 +0,0 @@
|
||||
name: Required PR Label
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, labeled, unlabeled, synchronize]
|
||||
jobs:
|
||||
label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check PR Labels
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
let missing = false;
|
||||
const labels = context.payload.pull_request.labels.map(l => l.name);
|
||||
if (labels.filter(l => l.startsWith('area:')).length === 0) {
|
||||
console.error('::error::Missing label: Please add at least one "area" label.');
|
||||
missing = true;
|
||||
}
|
||||
if (labels.filter(l => l.startsWith('semver:')).length !== 1) {
|
||||
console.error('::error::Missing label: Please add exactly one "semver" label.');
|
||||
missing = true;
|
||||
}
|
||||
if (missing) {
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('::notice::Success: This pull request has correct labels, thanks!');
|
||||
@@ -12,6 +12,7 @@ packages/gatsby-plugin-vercel-analytics
|
||||
node_modules
|
||||
dist
|
||||
pnpm-lock.yaml
|
||||
.changeset
|
||||
.vscode
|
||||
.DS_Store
|
||||
.next
|
||||
@@ -31,4 +32,4 @@ packages/**/test/fixtures
|
||||
packages/**/test/dev/fixtures
|
||||
packages/**/test/build-fixtures
|
||||
packages/**/test/cache-fixtures
|
||||
|
||||
packages/cli/src/util/dev/templates/*.ts
|
||||
|
||||
@@ -19,9 +19,7 @@
|
||||
|
||||
## Vercel
|
||||
|
||||
Vercel is the platform for frontend developers, providing the speed and reliability innovators need to create at the moment of inspiration.
|
||||
|
||||
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.
|
||||
Vercel's frontend cloud gives developers frameworks, workflows, and infrastructure to build a faster, more personalized web.
|
||||
|
||||
## Deploy
|
||||
|
||||
|
||||
@@ -14,11 +14,7 @@ pnpm dev
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
|
||||
|
||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
|
||||
|
||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
||||
You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
17
examples/nextjs/app/layout.js
Normal file
17
examples/nextjs/app/layout.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import './globals.css'
|
||||
import { Inter } from 'next/font/google'
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
|
||||
export const metadata = {
|
||||
title: 'Create Next App',
|
||||
description: 'Generated by create next app',
|
||||
}
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
95
examples/nextjs/app/page.js
Normal file
95
examples/nextjs/app/page.js
Normal file
@@ -0,0 +1,95 @@
|
||||
import Image from 'next/image'
|
||||
import styles from './page.module.css'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<div className={styles.description}>
|
||||
<p>
|
||||
Get started by editing
|
||||
<code className={styles.code}>app/page.js</code>
|
||||
</p>
|
||||
<div>
|
||||
<a
|
||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
By{' '}
|
||||
<Image
|
||||
src="/vercel.svg"
|
||||
alt="Vercel Logo"
|
||||
className={styles.vercelLogo}
|
||||
width={100}
|
||||
height={24}
|
||||
priority
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.center}>
|
||||
<Image
|
||||
className={styles.logo}
|
||||
src="/next.svg"
|
||||
alt="Next.js Logo"
|
||||
width={180}
|
||||
height={37}
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.grid}>
|
||||
<a
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2>
|
||||
Docs <span>-></span>
|
||||
</h2>
|
||||
<p>Find in-depth information about Next.js features and API.</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2>
|
||||
Learn <span>-></span>
|
||||
</h2>
|
||||
<p>Learn about Next.js in an interactive course with quizzes!</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2>
|
||||
Templates <span>-></span>
|
||||
</h2>
|
||||
<p>Explore the Next.js 13 playground.</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2>
|
||||
Deploy <span>-></span>
|
||||
</h2>
|
||||
<p>
|
||||
Instantly deploy your Next.js site to a shareable URL with Vercel.
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
}
|
||||
const nextConfig = {}
|
||||
|
||||
module.exports = nextConfig
|
||||
|
||||
645
examples/nextjs/package-lock.json
generated
645
examples/nextjs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,9 +9,9 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"eslint": "8.39.0",
|
||||
"eslint-config-next": "13.3.4",
|
||||
"next": "13.3.4",
|
||||
"eslint": "8.40.0",
|
||||
"eslint-config-next": "13.4.3",
|
||||
"next": "13.4.3",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import '@/styles/globals.css'
|
||||
|
||||
export default function App({ Component, pageProps }) {
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { Html, Head, Main, NextScript } from 'next/document'
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
|
||||
export default function handler(req, res) {
|
||||
res.status(200).json({ name: 'John Doe' })
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
import Head from 'next/head'
|
||||
import Image from 'next/image'
|
||||
import { Inter } from 'next/font/google'
|
||||
import styles from '@/styles/Home.module.css'
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Create Next App</title>
|
||||
<meta name="description" content="Generated by create next app" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
<main className={`${styles.main} ${inter.className}`}>
|
||||
<div className={styles.description}>
|
||||
<p>
|
||||
Get started by editing
|
||||
<code className={styles.code}>pages/index.js</code>
|
||||
</p>
|
||||
<div>
|
||||
<a
|
||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
By{' '}
|
||||
<Image
|
||||
src="/vercel.svg"
|
||||
alt="Vercel Logo"
|
||||
className={styles.vercelLogo}
|
||||
width={100}
|
||||
height={24}
|
||||
priority
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.center}>
|
||||
<Image
|
||||
className={styles.logo}
|
||||
src="/next.svg"
|
||||
alt="Next.js Logo"
|
||||
width={180}
|
||||
height={37}
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.grid}>
|
||||
<a
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2>
|
||||
Docs <span>-></span>
|
||||
</h2>
|
||||
<p>
|
||||
Find in-depth information about Next.js features and API.
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2>
|
||||
Learn <span>-></span>
|
||||
</h2>
|
||||
<p>
|
||||
Learn about Next.js in an interactive course with quizzes!
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2>
|
||||
Templates <span>-></span>
|
||||
</h2>
|
||||
<p>
|
||||
Discover and deploy boilerplate example Next.js projects.
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2>
|
||||
Deploy <span>-></span>
|
||||
</h2>
|
||||
<p>
|
||||
Instantly deploy your Next.js site to a shareable URL
|
||||
with Vercel.
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
||||
20
package.json
20
package.json
@@ -4,10 +4,9 @@
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"packageManager": "pnpm@8.3.1",
|
||||
"dependencies": {
|
||||
"lerna": "5.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "2.26.1",
|
||||
"@svitejs/changesets-changelog-github-compact": "1.1.0",
|
||||
"@types/node": "14.18.33",
|
||||
"@typescript-eslint/eslint-plugin": "5.21.0",
|
||||
"@typescript-eslint/parser": "5.21.0",
|
||||
@@ -22,6 +21,7 @@
|
||||
"eslint-plugin-jest": "26.1.5",
|
||||
"execa": "3.2.0",
|
||||
"fs-extra": "11.1.0",
|
||||
"glob": "10.2.3",
|
||||
"husky": "7.0.4",
|
||||
"jest": "29.5.0",
|
||||
"json5": "2.1.1",
|
||||
@@ -32,16 +32,10 @@
|
||||
"source-map-support": "0.5.12",
|
||||
"ts-eager": "2.0.2",
|
||||
"ts-jest": "29.1.0",
|
||||
"typescript": "4.9.5",
|
||||
"turbo": "1.9.3"
|
||||
"turbo": "1.9.8",
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"scripts": {
|
||||
"lerna": "lerna",
|
||||
"version": "pnpm install && git add pnpm-lock.yaml",
|
||||
"bootstrap": "lerna bootstrap",
|
||||
"publish-stable": "echo 'Run `pnpm changelog` for instructions'",
|
||||
"publish-from-github": "./utils/publish.sh",
|
||||
"changelog": "node utils/changelog.js",
|
||||
"build": "node utils/gen.js && turbo --no-update-notifier run build",
|
||||
"vercel-build": "pnpm build && pnpm run pack && cd api && node -r ts-eager/register ./_lib/script/build.ts",
|
||||
"pre-commit": "lint-staged",
|
||||
@@ -53,7 +47,9 @@
|
||||
"lint": "eslint . --cache --ext .ts,.js",
|
||||
"prettier-check": "prettier --check .",
|
||||
"prepare": "husky install",
|
||||
"pack": "cd utils && node -r ts-eager/register ./pack.ts"
|
||||
"pack": "cd utils && node -r ts-eager/register ./pack.ts",
|
||||
"version:prepare": "changeset version && pnpm install --no-frozen-lockfile",
|
||||
"release": "changeset publish"
|
||||
},
|
||||
"lint-staged": {
|
||||
"./{*,{api,packages,test,utils}/**/*}.{js,ts}": [
|
||||
|
||||
7
packages/build-utils/CHANGELOG.md
Normal file
7
packages/build-utils/CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# @vercel/build-utils
|
||||
|
||||
## 6.7.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Deprecate Node.js 14.x and 16.x with warning ([#9976](https://github.com/vercel/vercel/pull/9976))
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "6.7.2",
|
||||
"version": "6.7.3",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -6,8 +6,18 @@ import debug from '../debug';
|
||||
function getOptions() {
|
||||
const options = [
|
||||
{ major: 18, range: '18.x', runtime: 'nodejs18.x' },
|
||||
{ major: 16, range: '16.x', runtime: 'nodejs16.x' },
|
||||
{ major: 14, range: '14.x', runtime: 'nodejs14.x' },
|
||||
{
|
||||
major: 16,
|
||||
range: '16.x',
|
||||
runtime: 'nodejs16.x',
|
||||
discontinueDate: new Date('2023-08-15'),
|
||||
},
|
||||
{
|
||||
major: 14,
|
||||
range: '14.x',
|
||||
runtime: 'nodejs14.x',
|
||||
discontinueDate: new Date('2023-08-15'),
|
||||
},
|
||||
{
|
||||
major: 12,
|
||||
range: '12.x',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "16.14.0"
|
||||
"node": "18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "14.x"
|
||||
"node": "18.x"
|
||||
}
|
||||
}
|
||||
|
||||
66
packages/build-utils/test/unit.test.ts
vendored
66
packages/build-utils/test/unit.test.ts
vendored
@@ -140,20 +140,20 @@ it('should ignore node version in vercel dev getNodeVersion()', async () => {
|
||||
|
||||
it('should select project setting from config when no package.json is found and fallback undefined', async () => {
|
||||
expect(
|
||||
await getNodeVersion('/tmp', undefined, { nodeVersion: '16.x' }, {})
|
||||
).toHaveProperty('range', '16.x');
|
||||
await getNodeVersion('/tmp', undefined, { nodeVersion: '18.x' }, {})
|
||||
).toHaveProperty('range', '18.x');
|
||||
expect(warningMessages).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it('should select project setting from config when no package.json is found and fallback is null', async () => {
|
||||
expect(
|
||||
await getNodeVersion('/tmp', null as any, { nodeVersion: '16.x' }, {})
|
||||
).toHaveProperty('range', '16.x');
|
||||
await getNodeVersion('/tmp', null as any, { nodeVersion: '18.x' }, {})
|
||||
).toHaveProperty('range', '18.x');
|
||||
expect(warningMessages).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it('should select project setting from fallback when no package.json is found', async () => {
|
||||
expect(await getNodeVersion('/tmp', '16.x')).toHaveProperty('range', '16.x');
|
||||
expect(await getNodeVersion('/tmp', '18.x')).toHaveProperty('range', '18.x');
|
||||
expect(warningMessages).toStrictEqual([]);
|
||||
});
|
||||
|
||||
@@ -165,9 +165,9 @@ it('should prefer package.json engines over project setting from config and warn
|
||||
{ nodeVersion: '12.x' },
|
||||
{}
|
||||
)
|
||||
).toHaveProperty('range', '14.x');
|
||||
).toHaveProperty('range', '18.x');
|
||||
expect(warningMessages).toStrictEqual([
|
||||
'Warning: Due to "engines": { "node": "14.x" } in your `package.json` file, the Node.js Version defined in your Project Settings ("12.x") will not apply. Learn More: http://vercel.link/node-version',
|
||||
'Warning: Due to "engines": { "node": "18.x" } in your `package.json` file, the Node.js Version defined in your Project Settings ("12.x") will not apply. Learn More: http://vercel.link/node-version',
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -179,9 +179,9 @@ it('should warn when package.json engines is exact version', async () => {
|
||||
{},
|
||||
{}
|
||||
)
|
||||
).toHaveProperty('range', '16.x');
|
||||
).toHaveProperty('range', '18.x');
|
||||
expect(warningMessages).toStrictEqual([
|
||||
'Warning: Detected "engines": { "node": "16.14.0" } 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": "18.2.0" } in your `package.json` with major.minor.patch, but only major Node.js Version can be selected. Learn More: http://vercel.link/node-version',
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -204,30 +204,30 @@ it('should not warn when package.json engines matches project setting from confi
|
||||
await getNodeVersion(
|
||||
path.join(__dirname, 'pkg-engine-node'),
|
||||
undefined,
|
||||
{ nodeVersion: '14' },
|
||||
{ nodeVersion: '18' },
|
||||
{}
|
||||
)
|
||||
).toHaveProperty('range', '14.x');
|
||||
).toHaveProperty('range', '18.x');
|
||||
expect(warningMessages).toStrictEqual([]);
|
||||
|
||||
expect(
|
||||
await getNodeVersion(
|
||||
path.join(__dirname, 'pkg-engine-node'),
|
||||
undefined,
|
||||
{ nodeVersion: '14.x' },
|
||||
{ nodeVersion: '18.x' },
|
||||
{}
|
||||
)
|
||||
).toHaveProperty('range', '14.x');
|
||||
).toHaveProperty('range', '18.x');
|
||||
expect(warningMessages).toStrictEqual([]);
|
||||
|
||||
expect(
|
||||
await getNodeVersion(
|
||||
path.join(__dirname, 'pkg-engine-node'),
|
||||
undefined,
|
||||
{ nodeVersion: '<15' },
|
||||
{ nodeVersion: '<19' },
|
||||
{}
|
||||
)
|
||||
).toHaveProperty('range', '14.x');
|
||||
).toHaveProperty('range', '18.x');
|
||||
expect(warningMessages).toStrictEqual([]);
|
||||
});
|
||||
|
||||
@@ -238,7 +238,7 @@ it('should get latest node version', async () => {
|
||||
it('should throw for discontinued versions', async () => {
|
||||
// Mock a future date so that Node 8 and 10 become discontinued
|
||||
const realDateNow = Date.now.bind(global.Date);
|
||||
global.Date.now = () => new Date('2022-10-15').getTime();
|
||||
global.Date.now = () => new Date('2023-10-01').getTime();
|
||||
|
||||
expect(getSupportedNodeVersion('8.10.x', false)).rejects.toThrow();
|
||||
expect(getSupportedNodeVersion('8.10.x', true)).rejects.toThrow();
|
||||
@@ -246,12 +246,18 @@ it('should throw for discontinued versions', async () => {
|
||||
expect(getSupportedNodeVersion('10.x', true)).rejects.toThrow();
|
||||
expect(getSupportedNodeVersion('12.x', false)).rejects.toThrow();
|
||||
expect(getSupportedNodeVersion('12.x', true)).rejects.toThrow();
|
||||
expect(getSupportedNodeVersion('14.x', false)).rejects.toThrow();
|
||||
expect(getSupportedNodeVersion('14.x', true)).rejects.toThrow();
|
||||
expect(getSupportedNodeVersion('16.x', false)).rejects.toThrow();
|
||||
expect(getSupportedNodeVersion('16.x', true)).rejects.toThrow();
|
||||
|
||||
const discontinued = getDiscontinuedNodeVersions();
|
||||
expect(discontinued.length).toBe(3);
|
||||
expect(discontinued[0]).toHaveProperty('range', '12.x');
|
||||
expect(discontinued[1]).toHaveProperty('range', '10.x');
|
||||
expect(discontinued[2]).toHaveProperty('range', '8.10.x');
|
||||
expect(discontinued.length).toBe(5);
|
||||
expect(discontinued[0]).toHaveProperty('range', '16.x');
|
||||
expect(discontinued[1]).toHaveProperty('range', '14.x');
|
||||
expect(discontinued[2]).toHaveProperty('range', '12.x');
|
||||
expect(discontinued[3]).toHaveProperty('range', '10.x');
|
||||
expect(discontinued[4]).toHaveProperty('range', '8.10.x');
|
||||
|
||||
global.Date.now = realDateNow;
|
||||
});
|
||||
@@ -277,11 +283,31 @@ it('should warn for deprecated versions, soon to be discontinued', async () => {
|
||||
'major',
|
||||
12
|
||||
);
|
||||
expect(await getSupportedNodeVersion('14.x', false)).toHaveProperty(
|
||||
'major',
|
||||
14
|
||||
);
|
||||
expect(await getSupportedNodeVersion('14.x', true)).toHaveProperty(
|
||||
'major',
|
||||
14
|
||||
);
|
||||
expect(await getSupportedNodeVersion('16.x', false)).toHaveProperty(
|
||||
'major',
|
||||
16
|
||||
);
|
||||
expect(await getSupportedNodeVersion('16.x', true)).toHaveProperty(
|
||||
'major',
|
||||
16
|
||||
);
|
||||
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": "18.x" } in your `package.json` file to use Node.js 18.',
|
||||
'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 18.x in your Project Settings to use Node.js 18.',
|
||||
'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": "18.x" } in your `package.json` file to use Node.js 18.',
|
||||
'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 18.x in your Project Settings to use Node.js 18.',
|
||||
'Error: Node.js version 14.x has reached End-of-Life. Deployments created on or after 2023-08-15 will fail to build. Please set "engines": { "node": "18.x" } in your `package.json` file to use Node.js 18.',
|
||||
'Error: Node.js version 14.x has reached End-of-Life. Deployments created on or after 2023-08-15 will fail to build. Please set Node.js Version to 18.x in your Project Settings to use Node.js 18.',
|
||||
'Error: Node.js version 16.x has reached End-of-Life. Deployments created on or after 2023-08-15 will fail to build. Please set "engines": { "node": "18.x" } in your `package.json` file to use Node.js 18.',
|
||||
'Error: Node.js version 16.x has reached End-of-Life. Deployments created on or after 2023-08-15 will fail to build. Please set Node.js Version to 18.x in your Project Settings to use Node.js 18.',
|
||||
]);
|
||||
|
||||
global.Date.now = realDateNow;
|
||||
|
||||
66
packages/cli/CHANGELOG.md
Normal file
66
packages/cli/CHANGELOG.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# vercel
|
||||
|
||||
## 29.4.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Add `vercel link --repo` flag to link to repository (multiple projects), rather than an individual project (alpha) ([#8931](https://github.com/vercel/vercel/pull/8931))
|
||||
|
||||
## 29.3.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @vercel/static-build@1.3.32
|
||||
|
||||
## 29.3.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`2c950d47a`](https://github.com/vercel/vercel/commit/2c950d47aeb22a3de16f983259ea6f37a4555189), [`71b9f3a94`](https://github.com/vercel/vercel/commit/71b9f3a94b7922607f8f24bf7b2bd1742e62cc05), [`f00b08a82`](https://github.com/vercel/vercel/commit/f00b08a82085c3a63059f34f67f10ced92f2979c)]:
|
||||
- @vercel/static-build@1.3.31
|
||||
- @vercel/build-utils@6.7.3
|
||||
- @vercel/next@3.8.5
|
||||
- @vercel/node@2.14.3
|
||||
- @vercel/remix-builder@1.8.10
|
||||
|
||||
## 29.3.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`67e556bc8`](https://github.com/vercel/vercel/commit/67e556bc80c821c233120a2ec1611adb8e195baa), [`ba10fb4dd`](https://github.com/vercel/vercel/commit/ba10fb4dd4155a75df79b98a0c43a6c42eac7b62)]:
|
||||
- @vercel/remix-builder@1.8.9
|
||||
- @vercel/next@3.8.4
|
||||
|
||||
## 29.3.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`6c6f3ce9d`](https://github.com/vercel/vercel/commit/6c6f3ce9d228b1e038641e4bafb38c3487e7dff7)]:
|
||||
- @vercel/next@3.8.3
|
||||
|
||||
## 29.3.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [vc dev] Fix serverless function size limit condition ([#9961](https://github.com/vercel/vercel/pull/9961))
|
||||
|
||||
## 29.3.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Sort environment variables alphabetically in `vercel env pull` ([#9949](https://github.com/vercel/vercel/pull/9949))
|
||||
- Skip 50MB zip size limit for Python ([#9944](https://github.com/vercel/vercel/pull/9944))
|
||||
|
||||
## 29.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [cli] remove `vc rollback` beta label ([#9928](https://github.com/vercel/vercel/pull/9928))
|
||||
|
||||
## 29.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`6d5983eaa`](https://github.com/vercel/vercel/commit/6d5983eaaefe3fd2204f49c3228718ac64a452e3)]:
|
||||
- @vercel/remix-builder@1.8.8
|
||||
@@ -10,9 +10,7 @@
|
||||
|
||||
## Usage
|
||||
|
||||
Vercel is the platform for frontend developers, providing the speed and reliability innovators need to create at the moment of inspiration.
|
||||
|
||||
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.
|
||||
Vercel's frontend cloud gives developers frameworks, workflows, and infrastructure to build a faster, more personalized web.
|
||||
|
||||
To install the latest version of Vercel CLI, run this command:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "29.1.1",
|
||||
"version": "29.4.0",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -32,19 +32,20 @@
|
||||
"node": ">= 14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "6.7.2",
|
||||
"@vercel/build-utils": "6.7.3",
|
||||
"@vercel/go": "2.5.1",
|
||||
"@vercel/hydrogen": "0.0.64",
|
||||
"@vercel/next": "3.8.2",
|
||||
"@vercel/node": "2.14.1",
|
||||
"@vercel/next": "3.8.5",
|
||||
"@vercel/node": "2.14.3",
|
||||
"@vercel/python": "3.1.60",
|
||||
"@vercel/redwood": "1.1.15",
|
||||
"@vercel/remix-builder": "1.8.7",
|
||||
"@vercel/remix-builder": "1.8.10",
|
||||
"@vercel/ruby": "1.3.76",
|
||||
"@vercel/static-build": "1.3.29"
|
||||
"@vercel/static-build": "1.3.32"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alex_neo/jest-expect-message": "1.0.5",
|
||||
"@edge-runtime/node-utils": "2.0.3",
|
||||
"@next/env": "11.1.2",
|
||||
"@sentry/node": "5.5.0",
|
||||
"@sindresorhus/slugify": "0.11.0",
|
||||
@@ -81,16 +82,16 @@
|
||||
"@types/title": "3.4.1",
|
||||
"@types/universal-analytics": "0.4.2",
|
||||
"@types/update-notifier": "5.1.0",
|
||||
"@types/which": "1.3.2",
|
||||
"@types/which": "3.0.0",
|
||||
"@types/write-json-file": "2.2.1",
|
||||
"@types/yauzl-promise": "2.1.0",
|
||||
"@vercel-internals/constants": "*",
|
||||
"@vercel-internals/get-package-json": "*",
|
||||
"@vercel-internals/types": "*",
|
||||
"@vercel/client": "12.4.12",
|
||||
"@vercel/client": "12.5.1",
|
||||
"@vercel/error-utils": "1.0.10",
|
||||
"@vercel/frameworks": "1.4.1",
|
||||
"@vercel/fs-detectors": "3.9.1",
|
||||
"@vercel/frameworks": "1.4.2",
|
||||
"@vercel/fs-detectors": "3.9.3",
|
||||
"@vercel/fun": "1.0.4",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/routing-utils": "2.2.1",
|
||||
@@ -100,7 +101,7 @@
|
||||
"ansi-escapes": "4.3.2",
|
||||
"ansi-regex": "5.0.1",
|
||||
"arg": "5.0.0",
|
||||
"async-listen": "1.2.0",
|
||||
"async-listen": "3.0.0",
|
||||
"async-retry": "1.1.3",
|
||||
"async-sema": "2.1.4",
|
||||
"bytes": "3.0.0",
|
||||
@@ -147,6 +148,8 @@
|
||||
"pcre-to-regexp": "1.0.0",
|
||||
"pluralize": "7.0.0",
|
||||
"promisepipe": "3.0.0",
|
||||
"proxy": "2.0.0",
|
||||
"proxy-agent": "6.1.1",
|
||||
"psl": "1.1.31",
|
||||
"qr-image": "3.2.0",
|
||||
"raw-body": "2.4.1",
|
||||
@@ -165,6 +168,7 @@
|
||||
"ts-node": "10.9.1",
|
||||
"universal-analytics": "0.4.20",
|
||||
"utility-types": "2.1.0",
|
||||
"which": "3.0.0",
|
||||
"write-json-file": "2.2.0",
|
||||
"xdg-app-paths": "5.1.0",
|
||||
"yauzl-promise": "2.1.3"
|
||||
|
||||
82
packages/cli/src/args.ts
Normal file
82
packages/cli/src/args.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import chalk from 'chalk';
|
||||
import logo from './util/output/logo';
|
||||
import { getPkgName } from './util/pkg-name';
|
||||
|
||||
export const help = () => `
|
||||
${chalk.bold(`${logo} ${getPkgName()}`)} [options] <command | path>
|
||||
|
||||
${chalk.dim('For deploy command help, run `vercel deploy --help`')}
|
||||
|
||||
${chalk.dim('Commands:')}
|
||||
|
||||
${chalk.dim('Basic')}
|
||||
|
||||
deploy [path] Performs a deployment ${chalk.bold(
|
||||
'(default)'
|
||||
)}
|
||||
dev Start a local development server
|
||||
env Manages the Environment Variables for your current Project
|
||||
git Manage Git provider repository for your current Project
|
||||
help [cmd] Displays complete help for [cmd]
|
||||
init [example] Initialize an example project
|
||||
inspect [id] Displays information related to a deployment
|
||||
link [path] Link local directory to a Vercel Project
|
||||
ls | list [app] Lists deployments
|
||||
login [email] Logs into your account or creates a new one
|
||||
logout Logs out of your account
|
||||
pull [path] Pull your Project Settings from the cloud
|
||||
rollback [url|id] Quickly revert back to a previous deployment
|
||||
switch [scope] Switches between teams and your personal account
|
||||
|
||||
${chalk.dim('Advanced')}
|
||||
|
||||
alias [cmd] Manages your domain aliases
|
||||
bisect Use binary search to find the deployment that introduced a bug
|
||||
certs [cmd] Manages your SSL certificates
|
||||
dns [name] Manages your DNS records
|
||||
domains [name] Manages your domain names
|
||||
logs [url] Displays the logs for a deployment
|
||||
projects Manages your Projects
|
||||
rm | remove [id] Removes a deployment
|
||||
secrets [name] Manages your global Secrets, for use in Environment Variables
|
||||
teams Manages your teams
|
||||
whoami Shows the username of the currently logged in user
|
||||
|
||||
${chalk.dim('Global Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-v, --version Output the version number
|
||||
--cwd Current working directory
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.vercel`'} directory
|
||||
-d, --debug Debug mode [off]
|
||||
--no-color No color mode [off]
|
||||
-S, --scope Set a custom scope
|
||||
-t ${chalk.underline('TOKEN')}, --token=${chalk.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Deploy the current directory
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()}`)}
|
||||
|
||||
${chalk.gray('–')} Deploy a custom path
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} /usr/src/project`)}
|
||||
|
||||
${chalk.gray('–')} Deploy with Environment Variables
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} -e NODE_ENV=production`)}
|
||||
|
||||
${chalk.gray('–')} Show the usage information for the sub command ${chalk.dim(
|
||||
'`list`'
|
||||
)}
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} help list`)}
|
||||
`;
|
||||
@@ -2,80 +2,27 @@ import chalk from 'chalk';
|
||||
import logo from '../../util/output/logo';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
export const help = () => `
|
||||
${chalk.bold(`${logo} ${getPkgName()}`)} [options] <command | path>
|
||||
export const help = () => {
|
||||
return `
|
||||
${chalk.bold(`${logo} ${getPkgName()} [deploy]`)} [path-to-project] [options]
|
||||
|
||||
${chalk.dim('Commands:')}
|
||||
|
||||
${chalk.dim('Basic')}
|
||||
|
||||
deploy [path] Performs a deployment ${chalk.bold(
|
||||
'(default)'
|
||||
)}
|
||||
dev Start a local development server
|
||||
env Manages the Environment Variables for your current Project
|
||||
git Manage Git provider repository for your current Project
|
||||
help [cmd] Displays complete help for [cmd]
|
||||
init [example] Initialize an example project
|
||||
inspect [id] Displays information related to a deployment
|
||||
link [path] Link local directory to a Vercel Project
|
||||
ls | list [app] Lists deployments
|
||||
login [email] Logs into your account or creates a new one
|
||||
logout Logs out of your account
|
||||
pull [path] Pull your Project Settings from the cloud
|
||||
rollback [url|id] Quickly revert back to a previous deployment [beta]
|
||||
switch [scope] Switches between teams and your personal account
|
||||
|
||||
${chalk.dim('Advanced')}
|
||||
|
||||
alias [cmd] Manages your domain aliases
|
||||
bisect Use binary search to find the deployment that introduced a bug
|
||||
certs [cmd] Manages your SSL certificates
|
||||
dns [name] Manages your DNS records
|
||||
domains [name] Manages your domain names
|
||||
logs [url] Displays the logs for a deployment
|
||||
projects Manages your Projects
|
||||
rm | remove [id] Removes a deployment
|
||||
secrets [name] Manages your global Secrets, for use in Environment Variables
|
||||
teams Manages your teams
|
||||
whoami Shows the username of the currently logged in user
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-v, --version Output the version number
|
||||
--cwd Current working directory
|
||||
-V, --platform-version Set the platform version to deploy to
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.vercel`'} directory
|
||||
-d, --debug Debug mode [off]
|
||||
--no-color No color mode [off]
|
||||
-f, --force Force a new deployment even if nothing has changed
|
||||
--with-cache Retain build cache when using "--force"
|
||||
-t ${chalk.underline('TOKEN')}, --token=${chalk.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
-p, --public Deployment is public (${chalk.dim(
|
||||
'`/_src`'
|
||||
)} is exposed)
|
||||
-e, --env Include an env var during run time (e.g.: ${chalk.dim(
|
||||
'`-e KEY=value`'
|
||||
)}). Can appear many times.
|
||||
-b, --build-env Similar to ${chalk.dim(
|
||||
'`--env`'
|
||||
)} but for build time only.
|
||||
-m, --meta Add metadata for the deployment (e.g.: ${chalk.dim(
|
||||
'`-m KEY=value`'
|
||||
)}). Can appear many times.
|
||||
--no-wait Don't wait for the deployment to finish
|
||||
-S, --scope Set a custom scope
|
||||
--regions Set default regions to enable the deployment on
|
||||
--prod Create a production deployment
|
||||
-y, --yes Skip questions when setting up new project using default scope and settings
|
||||
--prod Create a production deployment
|
||||
-p, --public Deployment is public (${chalk.dim(
|
||||
'`/_src`'
|
||||
)} is exposed)
|
||||
-e, --env Include an env var during run time (e.g.: ${chalk.dim(
|
||||
'`-e KEY=value`'
|
||||
)}). Can appear many times.
|
||||
-b, --build-env Similar to ${chalk.dim(
|
||||
'`--env`'
|
||||
)} but for build time only.
|
||||
-m, --meta Add metadata for the deployment (e.g.: ${chalk.dim(
|
||||
'`-m KEY=value`'
|
||||
)}). Can appear many times.
|
||||
--no-wait Don't wait for the deployment to finish
|
||||
-f, --force Force a new deployment even if nothing has changed
|
||||
--with-cache Retain build cache when using "--force"
|
||||
--regions Set default regions to enable the deployment on
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
@@ -89,14 +36,15 @@ export const help = () => `
|
||||
|
||||
${chalk.gray('–')} Deploy with Environment Variables
|
||||
|
||||
${chalk.cyan(
|
||||
`$ ${getPkgName()} -e NODE_ENV=production -e SECRET=@mysql-secret`
|
||||
)}
|
||||
${chalk.cyan(`$ ${getPkgName()} -e NODE_ENV=production`)}
|
||||
|
||||
${chalk.gray('–')} Show the usage information for the sub command ${chalk.dim(
|
||||
'`list`'
|
||||
)}
|
||||
${chalk.gray('–')} Deploy with prebuilt outputs
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} help list`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} build`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} deploy --prebuilt`)}
|
||||
|
||||
${chalk.gray('–')} Write Deployment URL to a file
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} > deployment-url.txt`)}
|
||||
`;
|
||||
};
|
||||
|
||||
5
packages/cli/src/commands/env/pull.ts
vendored
5
packages/cli/src/commands/env/pull.ts
vendored
@@ -122,8 +122,9 @@ export default async function pull(
|
||||
|
||||
const contents =
|
||||
CONTENTS_PREFIX +
|
||||
Object.entries(records)
|
||||
.map(([key, value]) => `${key}="${escapeValue(value)}"`)
|
||||
Object.keys(records)
|
||||
.sort()
|
||||
.map(key => `${key}="${escapeValue(records[key])}"`)
|
||||
.join('\n') +
|
||||
'\n';
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ export default async function main(client: Client) {
|
||||
try {
|
||||
argv = getArgs(client.argv.slice(2), {
|
||||
'--force': Boolean,
|
||||
'-f': Boolean,
|
||||
'-f': '--force',
|
||||
});
|
||||
args = getSubcommand(argv._.slice(1), COMMAND_CONFIG).args;
|
||||
} catch (err) {
|
||||
|
||||
@@ -35,7 +35,7 @@ export default async function init(
|
||||
) {
|
||||
const { output } = client;
|
||||
const [name, dir] = args;
|
||||
const force = opts['-f'] || opts['--force'];
|
||||
const force = opts['--force'];
|
||||
|
||||
const examples = await fetchExampleList(client);
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@ import chalk from 'chalk';
|
||||
import Client from '../../util/client';
|
||||
import getArgs from '../../util/get-args';
|
||||
import logo from '../../util/output/logo';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import { ensureLink } from '../../util/link/ensure-link';
|
||||
import { ensureRepoLink } from '../../util/link/repo';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
@@ -12,6 +14,7 @@ const help = () => {
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-r, --repo Link multiple projects based on Git repository (alpha)
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
@@ -42,7 +45,14 @@ const help = () => {
|
||||
|
||||
${chalk.gray('–')} Link a specific directory to a Vercel Project
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} link /usr/src/project`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} link --cwd /path/to/project`)}
|
||||
|
||||
${chalk.gray('–')} ${chalk.yellow(
|
||||
'(alpha)'
|
||||
)} Link to the current Git repository, allowing for multiple
|
||||
Vercel Projects to be linked simultaneously (useful for monorepos)
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} link --repo`)}
|
||||
`);
|
||||
};
|
||||
|
||||
@@ -52,6 +62,8 @@ export default async function main(client: Client) {
|
||||
'-y': '--yes',
|
||||
'--project': String,
|
||||
'-p': '--project',
|
||||
'--repo': Boolean,
|
||||
'-r': '--repo',
|
||||
|
||||
// deprecated
|
||||
'--confirm': Boolean,
|
||||
@@ -68,17 +80,36 @@ export default async function main(client: Client) {
|
||||
argv['--yes'] = argv['--confirm'];
|
||||
}
|
||||
|
||||
const cwd = argv._[1] || process.cwd();
|
||||
const yes = !!argv['--yes'];
|
||||
|
||||
const link = await ensureLink('link', client, cwd, {
|
||||
autoConfirm: !!argv['--yes'],
|
||||
forceDelete: true,
|
||||
projectName: argv['--project'],
|
||||
successEmoji: 'success',
|
||||
});
|
||||
|
||||
if (typeof link === 'number') {
|
||||
return link;
|
||||
let cwd = argv._[1];
|
||||
if (cwd) {
|
||||
client.output.warn(
|
||||
`The ${cmd('vc link <directory>')} syntax is deprecated, please use ${cmd(
|
||||
`vc link --cwd ${cwd}`
|
||||
)} instead`
|
||||
);
|
||||
} else {
|
||||
cwd = process.cwd();
|
||||
}
|
||||
|
||||
if (argv['--repo']) {
|
||||
client.output.warn(
|
||||
`The ${cmd('--repo')} flag is in alpha, please report issues`
|
||||
);
|
||||
await ensureRepoLink(client, cwd, yes);
|
||||
} else {
|
||||
const link = await ensureLink('link', client, cwd, {
|
||||
autoConfirm: yes,
|
||||
forceDelete: true,
|
||||
projectName: argv['--project'],
|
||||
successEmoji: 'success',
|
||||
});
|
||||
|
||||
if (typeof link === 'number') {
|
||||
return link;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,10 @@ import { getCommandName, getTitleName } from './util/pkg-name';
|
||||
import doLoginPrompt from './util/login/prompt';
|
||||
import type { AuthConfig, GlobalConfig } from '@vercel-internals/types';
|
||||
import { VercelConfig } from '@vercel/client';
|
||||
import { ProxyAgent } from 'proxy-agent';
|
||||
import box from './util/output/box';
|
||||
import { execExtension } from './util/extension/exec';
|
||||
import { help } from './args';
|
||||
|
||||
const VERCEL_DIR = getGlobalPathConfig();
|
||||
const VERCEL_CONFIG_PATH = configFiles.getConfigFilePath();
|
||||
@@ -141,19 +144,21 @@ const main = async () => {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const cwd = argv['--cwd'];
|
||||
let cwd = argv['--cwd'];
|
||||
if (cwd) {
|
||||
process.chdir(cwd);
|
||||
}
|
||||
cwd = process.cwd();
|
||||
|
||||
// The second argument to the command can be:
|
||||
//
|
||||
// * a path to deploy (as in: `vercel path/`)
|
||||
// * a subcommand (as in: `vercel ls`)
|
||||
const targetOrSubcommand = argv._[2];
|
||||
const subSubCommand = argv._[3];
|
||||
|
||||
// Currently no beta commands - add here as needed
|
||||
const betaCommands: string[] = ['rollback'];
|
||||
// If empty, leave this code here for easy adding of beta commands later
|
||||
const betaCommands: string[] = [];
|
||||
if (betaCommands.includes(targetOrSubcommand)) {
|
||||
console.log(
|
||||
`${chalk.grey(
|
||||
@@ -172,6 +177,14 @@ const main = async () => {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Handle bare `-h` directly
|
||||
const bareHelpOption = !targetOrSubcommand && argv['--help'];
|
||||
const bareHelpSubcommand = targetOrSubcommand === 'help' && !subSubCommand;
|
||||
if (bareHelpOption || bareHelpSubcommand) {
|
||||
output.print(help());
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Ensure that the Vercel global configuration directory exists
|
||||
try {
|
||||
await mkdirp(VERCEL_DIR);
|
||||
@@ -251,6 +264,7 @@ const main = async () => {
|
||||
|
||||
// Shared API `Client` instance for all sub-commands to utilize
|
||||
client = new Client({
|
||||
agent: new ProxyAgent({ keepAlive: true }),
|
||||
apiUrl,
|
||||
stdin: process.stdin,
|
||||
stdout: process.stdout,
|
||||
@@ -263,11 +277,13 @@ const main = async () => {
|
||||
argv: process.argv,
|
||||
});
|
||||
|
||||
let subcommand;
|
||||
// Gets populated to the subcommand name when a built-in is
|
||||
// provided, otherwise it remains undefined for an extension
|
||||
let subcommand: string | undefined = undefined;
|
||||
|
||||
// Check if we are deploying something
|
||||
if (targetOrSubcommand) {
|
||||
const targetPath = join(process.cwd(), targetOrSubcommand);
|
||||
const targetPath = join(cwd, targetOrSubcommand);
|
||||
const targetPathExists = existsSync(targetPath);
|
||||
const subcommandExists =
|
||||
GLOBAL_COMMANDS.has(targetOrSubcommand) ||
|
||||
@@ -289,8 +305,7 @@ const main = async () => {
|
||||
debug(`user supplied known subcommand: "${targetOrSubcommand}"`);
|
||||
subcommand = targetOrSubcommand;
|
||||
} else {
|
||||
debug('user supplied a possible target for deployment');
|
||||
subcommand = 'deploy';
|
||||
debug('user supplied a possible target for deployment or an extension');
|
||||
}
|
||||
} else {
|
||||
debug('user supplied no target, defaulting to deploy');
|
||||
@@ -298,7 +313,7 @@ const main = async () => {
|
||||
}
|
||||
|
||||
if (subcommand === 'help') {
|
||||
subcommand = argv._[3] || 'deploy';
|
||||
subcommand = subSubCommand || 'deploy';
|
||||
client.argv.push('-h');
|
||||
}
|
||||
|
||||
@@ -310,6 +325,7 @@ const main = async () => {
|
||||
!client.argv.includes('-h') &&
|
||||
!client.argv.includes('--help') &&
|
||||
!argv['--token'] &&
|
||||
subcommand &&
|
||||
!subcommandsWithoutToken.includes(subcommand)
|
||||
) {
|
||||
if (isTTY) {
|
||||
@@ -401,7 +417,8 @@ const main = async () => {
|
||||
);
|
||||
}
|
||||
|
||||
const targetCommand = commands.get(subcommand);
|
||||
let targetCommand =
|
||||
typeof subcommand === 'string' ? commands.get(subcommand) : undefined;
|
||||
const scope = argv['--scope'] || argv['--team'] || localConfig?.scope;
|
||||
|
||||
if (
|
||||
@@ -409,7 +426,7 @@ const main = async () => {
|
||||
targetCommand !== 'login' &&
|
||||
targetCommand !== 'dev' &&
|
||||
targetCommand !== 'build' &&
|
||||
!(targetCommand === 'teams' && argv._[3] !== 'invite')
|
||||
!(targetCommand === 'teams' && subSubCommand !== 'invite')
|
||||
) {
|
||||
let user = null;
|
||||
|
||||
@@ -472,96 +489,123 @@ const main = async () => {
|
||||
|
||||
try {
|
||||
const start = Date.now();
|
||||
let func: any;
|
||||
switch (targetCommand) {
|
||||
case 'alias':
|
||||
func = require('./commands/alias').default;
|
||||
break;
|
||||
case 'bisect':
|
||||
func = require('./commands/bisect').default;
|
||||
break;
|
||||
case 'build':
|
||||
func = require('./commands/build').default;
|
||||
break;
|
||||
case 'certs':
|
||||
func = require('./commands/certs').default;
|
||||
break;
|
||||
case 'deploy':
|
||||
func = require('./commands/deploy').default;
|
||||
break;
|
||||
case 'dev':
|
||||
func = require('./commands/dev').default;
|
||||
break;
|
||||
case 'dns':
|
||||
func = require('./commands/dns').default;
|
||||
break;
|
||||
case 'domains':
|
||||
func = require('./commands/domains').default;
|
||||
break;
|
||||
case 'env':
|
||||
func = require('./commands/env').default;
|
||||
break;
|
||||
case 'git':
|
||||
func = require('./commands/git').default;
|
||||
break;
|
||||
case 'init':
|
||||
func = require('./commands/init').default;
|
||||
break;
|
||||
case 'inspect':
|
||||
func = require('./commands/inspect').default;
|
||||
break;
|
||||
case 'link':
|
||||
func = require('./commands/link').default;
|
||||
break;
|
||||
case 'list':
|
||||
func = require('./commands/list').default;
|
||||
break;
|
||||
case 'logs':
|
||||
func = require('./commands/logs').default;
|
||||
break;
|
||||
case 'login':
|
||||
func = require('./commands/login').default;
|
||||
break;
|
||||
case 'logout':
|
||||
func = require('./commands/logout').default;
|
||||
break;
|
||||
case 'project':
|
||||
func = require('./commands/project').default;
|
||||
break;
|
||||
case 'pull':
|
||||
func = require('./commands/pull').default;
|
||||
break;
|
||||
case 'remove':
|
||||
func = require('./commands/remove').default;
|
||||
break;
|
||||
case 'rollback':
|
||||
func = require('./commands/rollback').default;
|
||||
break;
|
||||
case 'secrets':
|
||||
func = require('./commands/secrets').default;
|
||||
break;
|
||||
case 'teams':
|
||||
func = require('./commands/teams').default;
|
||||
break;
|
||||
case 'whoami':
|
||||
func = require('./commands/whoami').default;
|
||||
break;
|
||||
default:
|
||||
func = null;
|
||||
break;
|
||||
|
||||
if (!targetCommand) {
|
||||
// Set this for the metrics to record it at the end
|
||||
targetCommand = argv._[2];
|
||||
|
||||
// Try to execute as an extension
|
||||
try {
|
||||
exitCode = await execExtension(
|
||||
client,
|
||||
targetCommand,
|
||||
argv._.slice(3),
|
||||
cwd
|
||||
);
|
||||
} catch (err: unknown) {
|
||||
if (isErrnoException(err) && err.code === 'ENOENT') {
|
||||
// Fall back to `vc deploy <dir>`
|
||||
targetCommand = subcommand = 'deploy';
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!func || !targetCommand) {
|
||||
const sub = param(subcommand);
|
||||
output.error(`The ${sub} subcommand does not exist`);
|
||||
return 1;
|
||||
}
|
||||
// Not using an `else` here because if the CLI extension
|
||||
// was not found then we have to fall back to `vc deploy`
|
||||
if (subcommand) {
|
||||
let func: any;
|
||||
switch (targetCommand) {
|
||||
case 'alias':
|
||||
func = require('./commands/alias').default;
|
||||
break;
|
||||
case 'bisect':
|
||||
func = require('./commands/bisect').default;
|
||||
break;
|
||||
case 'build':
|
||||
func = require('./commands/build').default;
|
||||
break;
|
||||
case 'certs':
|
||||
func = require('./commands/certs').default;
|
||||
break;
|
||||
case 'deploy':
|
||||
func = require('./commands/deploy').default;
|
||||
break;
|
||||
case 'dev':
|
||||
func = require('./commands/dev').default;
|
||||
break;
|
||||
case 'dns':
|
||||
func = require('./commands/dns').default;
|
||||
break;
|
||||
case 'domains':
|
||||
func = require('./commands/domains').default;
|
||||
break;
|
||||
case 'env':
|
||||
func = require('./commands/env').default;
|
||||
break;
|
||||
case 'git':
|
||||
func = require('./commands/git').default;
|
||||
break;
|
||||
case 'init':
|
||||
func = require('./commands/init').default;
|
||||
break;
|
||||
case 'inspect':
|
||||
func = require('./commands/inspect').default;
|
||||
break;
|
||||
case 'link':
|
||||
func = require('./commands/link').default;
|
||||
break;
|
||||
case 'list':
|
||||
func = require('./commands/list').default;
|
||||
break;
|
||||
case 'logs':
|
||||
func = require('./commands/logs').default;
|
||||
break;
|
||||
case 'login':
|
||||
func = require('./commands/login').default;
|
||||
break;
|
||||
case 'logout':
|
||||
func = require('./commands/logout').default;
|
||||
break;
|
||||
case 'project':
|
||||
func = require('./commands/project').default;
|
||||
break;
|
||||
case 'pull':
|
||||
func = require('./commands/pull').default;
|
||||
break;
|
||||
case 'remove':
|
||||
func = require('./commands/remove').default;
|
||||
break;
|
||||
case 'rollback':
|
||||
func = require('./commands/rollback').default;
|
||||
break;
|
||||
case 'secrets':
|
||||
func = require('./commands/secrets').default;
|
||||
break;
|
||||
case 'teams':
|
||||
func = require('./commands/teams').default;
|
||||
break;
|
||||
case 'whoami':
|
||||
func = require('./commands/whoami').default;
|
||||
break;
|
||||
default:
|
||||
func = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (func.default) {
|
||||
func = func.default;
|
||||
}
|
||||
if (!func || !targetCommand) {
|
||||
const sub = param(subcommand);
|
||||
output.error(`The ${sub} subcommand does not exist`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
exitCode = await func(client);
|
||||
if (func.default) {
|
||||
func = func.default;
|
||||
}
|
||||
|
||||
exitCode = await func(client);
|
||||
}
|
||||
const end = Date.now() - start;
|
||||
|
||||
if (shouldCollectMetrics) {
|
||||
|
||||
@@ -2,9 +2,6 @@ const ARG_COMMON = {
|
||||
'--help': Boolean,
|
||||
'-h': '--help',
|
||||
|
||||
'--platform-version': Number,
|
||||
'-V': '--platform-version',
|
||||
|
||||
'--debug': Boolean,
|
||||
'-d': '--debug',
|
||||
|
||||
@@ -28,6 +25,10 @@ const ARG_COMMON = {
|
||||
'--api': String,
|
||||
|
||||
'--cwd': String,
|
||||
|
||||
// Deprecated
|
||||
'--platform-version': Number,
|
||||
'-V': '--platform-version',
|
||||
};
|
||||
|
||||
export default () => ARG_COMMON;
|
||||
|
||||
@@ -23,6 +23,7 @@ import type {
|
||||
import { sharedPromise } from './promise';
|
||||
import { APIError } from './errors-ts';
|
||||
import { normalizeError } from '@vercel/error-utils';
|
||||
import type { Agent } from 'http';
|
||||
|
||||
const isSAMLError = (v: any): v is SAMLError => {
|
||||
return v && v.saml;
|
||||
@@ -44,6 +45,7 @@ export interface ClientOptions extends Stdio {
|
||||
config: GlobalConfig;
|
||||
localConfig?: VercelConfig;
|
||||
localConfigPath?: string;
|
||||
agent?: Agent;
|
||||
}
|
||||
|
||||
export const isJSONObject = (v: any): v is JSONObject => {
|
||||
@@ -59,13 +61,15 @@ export default class Client extends EventEmitter implements Stdio {
|
||||
stderr: WritableTTY;
|
||||
output: Output;
|
||||
config: GlobalConfig;
|
||||
agent?: Agent;
|
||||
localConfig?: VercelConfig;
|
||||
localConfigPath?: string;
|
||||
prompt!: inquirer.PromptModule;
|
||||
private requestIdCounter: number;
|
||||
requestIdCounter: number;
|
||||
|
||||
constructor(opts: ClientOptions) {
|
||||
super();
|
||||
this.agent = opts.agent;
|
||||
this.argv = opts.argv;
|
||||
this.apiUrl = opts.apiUrl;
|
||||
this.authConfig = opts.authConfig;
|
||||
@@ -126,10 +130,10 @@ export default class Client extends EventEmitter implements Stdio {
|
||||
} else {
|
||||
return `#${requestId} → ${opts.method || 'GET'} ${url.href}`;
|
||||
}
|
||||
}, fetch(url, { ...opts, headers, body }));
|
||||
}, fetch(url, { agent: this.agent, ...opts, headers, body }));
|
||||
}
|
||||
|
||||
fetch(url: string, opts: { json: false }): Promise<Response>;
|
||||
fetch(url: string, opts: FetchOptions & { json: false }): Promise<Response>;
|
||||
fetch<T>(url: string, opts?: FetchOptions): Promise<T>;
|
||||
fetch(url: string, opts: FetchOptions = {}) {
|
||||
return this.retry(async bail => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import type { Org } from '@vercel-internals/types';
|
||||
import ua from '../ua';
|
||||
import { linkFolderToProject } from '../projects/link';
|
||||
import { prependEmoji, emoji } from '../emoji';
|
||||
import type { Agent } from 'http';
|
||||
|
||||
function printInspectUrl(
|
||||
output: Output,
|
||||
@@ -35,6 +36,7 @@ export default async function processDeployment({
|
||||
archive,
|
||||
skipAutoDetectionConfirmation,
|
||||
noWait,
|
||||
agent,
|
||||
...args
|
||||
}: {
|
||||
now: Now;
|
||||
@@ -55,6 +57,7 @@ export default async function processDeployment({
|
||||
cwd?: string;
|
||||
rootDirectory?: string | null;
|
||||
noWait?: boolean;
|
||||
agent?: Agent;
|
||||
}) {
|
||||
let {
|
||||
now,
|
||||
@@ -91,6 +94,7 @@ export default async function processDeployment({
|
||||
rootDirectory,
|
||||
skipAutoDetectionConfirmation,
|
||||
archive,
|
||||
agent,
|
||||
};
|
||||
|
||||
const deployingSpinnerVal = isSettingUpProject
|
||||
|
||||
@@ -337,7 +337,10 @@ export async function executeBuild(
|
||||
// Enforce the lambda zip size soft watermark
|
||||
const maxLambdaBytes = bytes('50mb');
|
||||
for (const asset of Object.values(result.output)) {
|
||||
if (asset.type === 'Lambda') {
|
||||
if (
|
||||
asset.type === 'Lambda' &&
|
||||
!(typeof asset.runtime === 'string' && asset.runtime.startsWith('python'))
|
||||
) {
|
||||
const size = asset.zipBuffer.length;
|
||||
if (size > maxLambdaBytes) {
|
||||
throw new LambdaSizeExceededError(size, maxLambdaBytes);
|
||||
|
||||
@@ -5,7 +5,7 @@ import chalk from 'chalk';
|
||||
import fetch from 'node-fetch';
|
||||
import plural from 'pluralize';
|
||||
import rawBody from 'raw-body';
|
||||
import listen from 'async-listen';
|
||||
import { listen } from 'async-listen';
|
||||
import minimatch from 'minimatch';
|
||||
import httpProxy from 'http-proxy';
|
||||
import { randomBytes } from 'crypto';
|
||||
@@ -860,7 +860,7 @@ export default class DevServer {
|
||||
let address: string | null = null;
|
||||
while (typeof address !== 'string') {
|
||||
try {
|
||||
address = await listen(this.server, ...listenSpec);
|
||||
address = (await listen(this.server, ...listenSpec)).toString();
|
||||
} catch (err: unknown) {
|
||||
if (isErrnoException(err)) {
|
||||
this.output.debug(`Got listen error: ${err.code}`);
|
||||
|
||||
90
packages/cli/src/util/extension/exec.ts
Normal file
90
packages/cli/src/util/extension/exec.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import which from 'which';
|
||||
import execa from 'execa';
|
||||
import { dirname } from 'path';
|
||||
import { listen } from 'async-listen';
|
||||
import { scanParentDirs, walkParentDirs } from '@vercel/build-utils';
|
||||
import { createProxy } from './proxy';
|
||||
import type Client from '../client';
|
||||
|
||||
/**
|
||||
* Attempts to execute a Vercel CLI Extension.
|
||||
*
|
||||
* If the extension was found and executed, then the
|
||||
* exit code is returned.
|
||||
*
|
||||
* If the program could not be found, then an `ENOENT`
|
||||
* error is thrown.
|
||||
*/
|
||||
export async function execExtension(
|
||||
client: Client,
|
||||
name: string,
|
||||
args: string[],
|
||||
cwd: string
|
||||
): Promise<number> {
|
||||
const { debug } = client.output;
|
||||
const extensionCommand = `vercel-${name}`;
|
||||
|
||||
const { packageJsonPath, lockfilePath } = await scanParentDirs(cwd);
|
||||
const baseFile = lockfilePath || packageJsonPath;
|
||||
let extensionPath: string | null = null;
|
||||
|
||||
if (baseFile) {
|
||||
// Scan `node_modules/.bin` works for npm / pnpm / yarn v1
|
||||
// TOOD: add support for Yarn PnP
|
||||
extensionPath = await walkParentDirs({
|
||||
base: dirname(baseFile),
|
||||
start: cwd,
|
||||
filename: `node_modules/.bin/${extensionCommand}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (!extensionPath) {
|
||||
// Attempt global `$PATH` lookup
|
||||
extensionPath = which.sync(extensionCommand, { nothrow: true });
|
||||
}
|
||||
|
||||
if (!extensionPath) {
|
||||
debug(`failed to find extension command with name "${extensionCommand}"`);
|
||||
throw new ENOENT(extensionCommand);
|
||||
}
|
||||
|
||||
debug(`invoking extension: ${extensionPath}`);
|
||||
|
||||
const proxy = createProxy(client);
|
||||
proxy.once('close', () => {
|
||||
debug(`extension proxy server shut down`);
|
||||
});
|
||||
|
||||
const proxyUrl = await listen(proxy, { port: 0, host: '127.0.0.1' });
|
||||
const VERCEL_API = proxyUrl.href.replace(/\/$/, '');
|
||||
debug(`extension proxy server listening at ${VERCEL_API}`);
|
||||
|
||||
const result = await execa(extensionPath, args, {
|
||||
cwd,
|
||||
reject: false,
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
VERCEL_API,
|
||||
// TODO:
|
||||
// VERCEL_SCOPE
|
||||
// VERCEL_DEBUG
|
||||
// VERCEL_HELP
|
||||
},
|
||||
});
|
||||
|
||||
proxy.close();
|
||||
|
||||
if (result instanceof Error) {
|
||||
debug(`error running extension: ${result.message}`);
|
||||
}
|
||||
|
||||
return result.exitCode;
|
||||
}
|
||||
|
||||
class ENOENT extends Error {
|
||||
code = 'ENOENT';
|
||||
constructor(command: string) {
|
||||
super(`Command "${command}" not found`);
|
||||
}
|
||||
}
|
||||
44
packages/cli/src/util/extension/proxy.ts
Normal file
44
packages/cli/src/util/extension/proxy.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { createServer } from 'http';
|
||||
import { Headers } from 'node-fetch';
|
||||
import {
|
||||
toOutgoingHeaders,
|
||||
mergeIntoServerResponse,
|
||||
buildToHeaders,
|
||||
} from '@edge-runtime/node-utils';
|
||||
import type { Server } from 'http';
|
||||
import type Client from '../client';
|
||||
|
||||
const toHeaders = buildToHeaders({
|
||||
// @ts-expect-error - `node-fetch` Headers is missing `getAll()`
|
||||
Headers,
|
||||
});
|
||||
|
||||
export function createProxy(client: Client): Server {
|
||||
return createServer(async (req, res) => {
|
||||
try {
|
||||
// Proxy to the upstream Vercel REST API
|
||||
const headers = toHeaders(req.headers);
|
||||
headers.delete('host');
|
||||
const fetchRes = await client.fetch(req.url || '/', {
|
||||
headers,
|
||||
method: req.method,
|
||||
body: req.method === 'GET' || req.method === 'HEAD' ? undefined : req,
|
||||
useCurrentTeam: false,
|
||||
json: false,
|
||||
});
|
||||
res.statusCode = fetchRes.status;
|
||||
mergeIntoServerResponse(
|
||||
// @ts-expect-error - `node-fetch` Headers is missing `getAll()`
|
||||
toOutgoingHeaders(fetchRes.headers),
|
||||
res
|
||||
);
|
||||
fetchRes.body.pipe(res);
|
||||
} catch (err: unknown) {
|
||||
client.output.prettyError(err);
|
||||
if (!res.headersSent) {
|
||||
res.statusCode = 500;
|
||||
res.end('Unexpected error during API call');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -161,6 +161,7 @@ export default class Now extends EventEmitter {
|
||||
const deployment = await processDeployment({
|
||||
now: this,
|
||||
output: this._output,
|
||||
agent: this._client.agent,
|
||||
paths,
|
||||
requestBody,
|
||||
uploadStamp,
|
||||
@@ -294,7 +295,7 @@ export default class Now extends EventEmitter {
|
||||
if (
|
||||
error.errorCode === 'BUILD_FAILED' ||
|
||||
error.errorCode === 'UNEXPECTED_ERROR' ||
|
||||
error.errorCode.includes('BUILD_UTILS_SPAWN_')
|
||||
error.errorCode?.includes('BUILD_UTILS_SPAWN_')
|
||||
) {
|
||||
return new BuildError({
|
||||
message: error.errorMessage,
|
||||
|
||||
31
packages/cli/src/util/link/add-to-gitignore.ts
Normal file
31
packages/cli/src/util/link/add-to-gitignore.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import os from 'os';
|
||||
import { join } from 'path';
|
||||
import { readFile, writeFile } from 'fs-extra';
|
||||
import { VERCEL_DIR } from '../projects/link';
|
||||
|
||||
export async function addToGitIgnore(path: string, ignore = VERCEL_DIR) {
|
||||
let isGitIgnoreUpdated = false;
|
||||
try {
|
||||
const gitIgnorePath = join(path, '.gitignore');
|
||||
|
||||
let gitIgnore =
|
||||
(await readFile(gitIgnorePath, 'utf8').catch(() => null)) ?? '';
|
||||
const EOL = gitIgnore.includes('\r\n') ? '\r\n' : os.EOL;
|
||||
let contentModified = false;
|
||||
|
||||
if (!gitIgnore.split(EOL).includes(ignore)) {
|
||||
gitIgnore += `${
|
||||
gitIgnore.endsWith(EOL) || gitIgnore.length === 0 ? '' : EOL
|
||||
}${ignore}${EOL}`;
|
||||
contentModified = true;
|
||||
}
|
||||
|
||||
if (contentModified) {
|
||||
await writeFile(gitIgnorePath, gitIgnore);
|
||||
isGitIgnoreUpdated = true;
|
||||
}
|
||||
} catch (error) {
|
||||
// ignore errors since this is non-critical
|
||||
}
|
||||
return isGitIgnoreUpdated;
|
||||
}
|
||||
272
packages/cli/src/util/link/repo.ts
Normal file
272
packages/cli/src/util/link/repo.ts
Normal file
@@ -0,0 +1,272 @@
|
||||
import chalk from 'chalk';
|
||||
import pluralize from 'pluralize';
|
||||
import { homedir } from 'os';
|
||||
import { join, normalize } from 'path';
|
||||
import { normalizePath } from '@vercel/build-utils';
|
||||
import { lstat, readJSON, outputJSON, writeFile, readFile } from 'fs-extra';
|
||||
import confirm from '../input/confirm';
|
||||
import toHumanPath from '../humanize-path';
|
||||
import {
|
||||
VERCEL_DIR,
|
||||
VERCEL_DIR_README,
|
||||
VERCEL_DIR_REPO,
|
||||
} from '../projects/link';
|
||||
import { getRemoteUrls } from '../create-git-meta';
|
||||
import link from '../output/link';
|
||||
import { emoji, prependEmoji } from '../emoji';
|
||||
import selectOrg from '../input/select-org';
|
||||
import { addToGitIgnore } from './add-to-gitignore';
|
||||
import type Client from '../client';
|
||||
import type { Project } from '@vercel-internals/types';
|
||||
|
||||
const home = homedir();
|
||||
|
||||
export interface RepoProjectConfig {
|
||||
id: string;
|
||||
name: string;
|
||||
directory: string;
|
||||
}
|
||||
|
||||
export interface RepoProjectsConfig {
|
||||
orgId: string;
|
||||
remoteName: string;
|
||||
projects: RepoProjectConfig[];
|
||||
}
|
||||
|
||||
export interface RepoLink {
|
||||
rootPath: string;
|
||||
repoConfigPath: string;
|
||||
repoConfig?: RepoProjectsConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a directory path `cwd`, finds the root of the Git repository
|
||||
* and returns the parsed `.vercel/repo.json` file if the repository
|
||||
* has already been linked.
|
||||
*/
|
||||
export async function getRepoLink(cwd: string): Promise<RepoLink | undefined> {
|
||||
// Determine where the root of the repo is
|
||||
const rootPath = await findRepoRoot(cwd);
|
||||
if (!rootPath) return undefined;
|
||||
|
||||
// Read the `repo.json`, if this repo has already been linked
|
||||
const repoConfigPath = join(rootPath, VERCEL_DIR, VERCEL_DIR_REPO);
|
||||
const repoConfig: RepoProjectsConfig = await readJSON(repoConfigPath).catch(
|
||||
err => {
|
||||
if (err.code !== 'ENOENT') throw err;
|
||||
}
|
||||
);
|
||||
|
||||
return { rootPath, repoConfig, repoConfigPath };
|
||||
}
|
||||
|
||||
export async function ensureRepoLink(
|
||||
client: Client,
|
||||
cwd: string,
|
||||
yes = false
|
||||
): Promise<RepoLink | undefined> {
|
||||
const { output } = client;
|
||||
|
||||
const repoLink = await getRepoLink(cwd);
|
||||
if (repoLink) {
|
||||
output.debug(`Found Git repository root directory: ${repoLink.rootPath}`);
|
||||
} else {
|
||||
throw new Error('Could not determine Git repository root directory');
|
||||
}
|
||||
let { rootPath, repoConfig, repoConfigPath } = repoLink;
|
||||
|
||||
if (!repoConfig) {
|
||||
// Not yet linked, so prompt user to begin linking
|
||||
let shouldLink =
|
||||
yes ||
|
||||
(await confirm(
|
||||
client,
|
||||
`Link Git repository at ${chalk.cyan(
|
||||
`“${toHumanPath(rootPath)}”`
|
||||
)} to your Project(s)?`,
|
||||
true
|
||||
));
|
||||
|
||||
if (!shouldLink) {
|
||||
output.print(`Canceled. Repository not linked.\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
const org = await selectOrg(
|
||||
client,
|
||||
'Which scope should contain your Project(s)?',
|
||||
yes
|
||||
);
|
||||
client.config.currentTeam = org.type === 'team' ? org.id : undefined;
|
||||
|
||||
const remoteUrls = await getRemoteUrls(
|
||||
join(rootPath, '.git/config'),
|
||||
output
|
||||
);
|
||||
if (!remoteUrls) {
|
||||
throw new Error('Could not determine Git remote URLs');
|
||||
}
|
||||
const remoteNames = Object.keys(remoteUrls);
|
||||
let remoteName: string;
|
||||
if (remoteNames.length === 1) {
|
||||
remoteName = remoteNames[0];
|
||||
} else {
|
||||
// Prompt user to select which remote to use
|
||||
const originIndex = remoteNames.indexOf('origin');
|
||||
const answer = await client.prompt({
|
||||
type: 'list',
|
||||
name: 'value',
|
||||
message: 'Which Git remote should be used?',
|
||||
choices: remoteNames.map(name => {
|
||||
return { name: name, value: name };
|
||||
}),
|
||||
default: originIndex === -1 ? 0 : originIndex,
|
||||
});
|
||||
remoteName = answer.value;
|
||||
}
|
||||
const repoUrl = remoteUrls[remoteName];
|
||||
output.spinner(
|
||||
`Fetching Projects for ${link(repoUrl)} under ${chalk.bold(org.slug)}…`
|
||||
);
|
||||
// TODO: Add pagination to fetch all Projects
|
||||
const query = new URLSearchParams({ repoUrl, limit: '100' });
|
||||
const projects: Project[] = await client.fetch(`/v2/projects?${query}`);
|
||||
if (projects.length === 0) {
|
||||
output.log(
|
||||
`No Projects are linked to ${link(repoUrl)} under ${chalk.bold(
|
||||
org.slug
|
||||
)}.`
|
||||
);
|
||||
// TODO: run detection logic to find potential projects.
|
||||
// then prompt user to select valid projects.
|
||||
// then create new Projects
|
||||
} else {
|
||||
output.log(
|
||||
`Found ${chalk.bold(projects.length)} ${pluralize(
|
||||
'Project',
|
||||
projects.length
|
||||
)} linked to ${link(repoUrl)} under ${chalk.bold(org.slug)}:`
|
||||
);
|
||||
}
|
||||
|
||||
for (const project of projects) {
|
||||
output.print(` * ${chalk.cyan(`${org.slug}/${project.name}\n`)}`);
|
||||
}
|
||||
|
||||
shouldLink =
|
||||
yes ||
|
||||
(await confirm(
|
||||
client,
|
||||
`Link to ${projects.length === 1 ? 'it' : 'them'}?`,
|
||||
true
|
||||
));
|
||||
|
||||
if (!shouldLink) {
|
||||
output.print(`Canceled. Repository not linked.\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
repoConfig = {
|
||||
orgId: org.id,
|
||||
remoteName,
|
||||
projects: projects.map(project => {
|
||||
return {
|
||||
id: project.id,
|
||||
name: project.name,
|
||||
directory: normalize(project.rootDirectory || ''),
|
||||
};
|
||||
}),
|
||||
};
|
||||
await outputJSON(repoConfigPath, repoConfig, { spaces: 2 });
|
||||
|
||||
await writeFile(
|
||||
join(rootPath, VERCEL_DIR, VERCEL_DIR_README),
|
||||
await readFile(join(__dirname, 'VERCEL_DIR_README.txt'), 'utf8')
|
||||
);
|
||||
|
||||
// update .gitignore
|
||||
const isGitIgnoreUpdated = await addToGitIgnore(rootPath);
|
||||
|
||||
output.print(
|
||||
prependEmoji(
|
||||
`Linked to ${link(repoUrl)} under ${chalk.bold(
|
||||
org.slug
|
||||
)} (created ${VERCEL_DIR}${
|
||||
isGitIgnoreUpdated ? ' and added it to .gitignore' : ''
|
||||
})`,
|
||||
emoji('link')
|
||||
) + '\n'
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
repoConfig,
|
||||
repoConfigPath,
|
||||
rootPath,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a `start` directory, traverses up the directory hierarchy until
|
||||
* the nearest `.git/config` file is found. Returns the directory where
|
||||
* the Git config was found, or `undefined` when no Git repo was found.
|
||||
*/
|
||||
export async function findRepoRoot(start: string): Promise<string | undefined> {
|
||||
for (const current of traverseUpDirectories(start)) {
|
||||
if (current === home) {
|
||||
// Sometimes the $HOME directory is set up as a Git repo
|
||||
// (for dotfiles, etc.). In this case it's safe to say that
|
||||
// this isn't the repo we're looking for. Bail.
|
||||
break;
|
||||
}
|
||||
|
||||
const gitConfigPath = join(current, '.git/config');
|
||||
const stat = await lstat(gitConfigPath).catch(err => {
|
||||
if (err.code !== 'ENOENT') throw err;
|
||||
});
|
||||
if (stat) {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function* traverseUpDirectories(start: string) {
|
||||
let current: string | undefined = normalize(start);
|
||||
while (current) {
|
||||
yield current;
|
||||
// Go up one directory
|
||||
const next = join(current, '..');
|
||||
current = next === current ? undefined : next;
|
||||
}
|
||||
}
|
||||
|
||||
function sortByDirectory(a: RepoProjectConfig, b: RepoProjectConfig): number {
|
||||
const aParts = a.directory.split('/');
|
||||
const bParts = b.directory.split('/');
|
||||
return bParts.length - aParts.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the matching Project from an array of Project links
|
||||
* where the provided relative path is within the Project's
|
||||
* root directory.
|
||||
*/
|
||||
export function findProjectFromPath(
|
||||
projects: RepoProjectConfig[],
|
||||
path: string
|
||||
): RepoProjectConfig | undefined {
|
||||
const normalizedPath = normalizePath(path);
|
||||
return projects
|
||||
.slice()
|
||||
.sort(sortByDirectory)
|
||||
.find(project => {
|
||||
if (project.directory === '.') {
|
||||
// Project has no "Root Directory" setting, so any path is valid
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
normalizedPath === project.directory ||
|
||||
normalizedPath.startsWith(`${project.directory}/`)
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import http from 'http';
|
||||
import open from 'open';
|
||||
import { URL } from 'url';
|
||||
import listen from 'async-listen';
|
||||
import { listen } from 'async-listen';
|
||||
import isDocker from 'is-docker';
|
||||
import Client from '../client';
|
||||
import prompt, { readInput } from './prompt';
|
||||
@@ -64,8 +64,7 @@ async function getVerificationTokenInBand(
|
||||
) {
|
||||
const { output } = client;
|
||||
const server = http.createServer();
|
||||
const address = await listen(server, 0, '127.0.0.1');
|
||||
const { port } = new URL(address);
|
||||
const { port } = await listen(server, 0, '127.0.0.1');
|
||||
url.searchParams.set('next', `http://localhost:${port}`);
|
||||
|
||||
output.log(`Please visit the following URL in your web browser:`);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import AJV from 'ajv';
|
||||
import chalk from 'chalk';
|
||||
import { join } from 'path';
|
||||
import { ensureDir } from 'fs-extra';
|
||||
import { join, relative } from 'path';
|
||||
import { ensureDir, readJSON } from 'fs-extra';
|
||||
import { promisify } from 'util';
|
||||
|
||||
import getProjectByIdOrName from '../projects/get-project-by-id-or-name';
|
||||
@@ -23,6 +22,8 @@ import { isDirectory } from '../config/global-path';
|
||||
import { NowBuildError, getPlatformEnv } from '@vercel/build-utils';
|
||||
import outputCode from '../output/code';
|
||||
import { isErrnoException, isError } from '@vercel/error-utils';
|
||||
import { findProjectFromPath, findRepoRoot } from '../link/repo';
|
||||
import { addToGitIgnore } from '../link/add-to-gitignore';
|
||||
|
||||
const readFile = promisify(fs.readFile);
|
||||
const writeFile = promisify(fs.writeFile);
|
||||
@@ -31,6 +32,7 @@ export const VERCEL_DIR = '.vercel';
|
||||
export const VERCEL_DIR_FALLBACK = '.now';
|
||||
export const VERCEL_DIR_README = 'README.txt';
|
||||
export const VERCEL_DIR_PROJECT = 'project.json';
|
||||
export const VERCEL_DIR_REPO = 'repo.json';
|
||||
|
||||
const linkSchema = {
|
||||
type: 'object',
|
||||
@@ -53,7 +55,7 @@ const linkSchema = {
|
||||
*
|
||||
* Throws an error if *both* `.vercel` and `.now` directories exist.
|
||||
*/
|
||||
export function getVercelDirectory(cwd: string = process.cwd()): string {
|
||||
export function getVercelDirectory(cwd: string): string {
|
||||
const possibleDirs = [join(cwd, VERCEL_DIR), join(cwd, VERCEL_DIR_FALLBACK)];
|
||||
const existingDirs = possibleDirs.filter(d => isDirectory(d));
|
||||
if (existingDirs.length > 1) {
|
||||
@@ -67,9 +69,40 @@ export function getVercelDirectory(cwd: string = process.cwd()): string {
|
||||
return existingDirs[0] || possibleDirs[0];
|
||||
}
|
||||
|
||||
async function getLink(path?: string): Promise<ProjectLink | null> {
|
||||
async function getLink(path: string): Promise<ProjectLink | null> {
|
||||
// Try the individual project linking style (`${cwd}/.vercel/project.json`)
|
||||
const dir = getVercelDirectory(path);
|
||||
return getLinkFromDir(dir);
|
||||
const linkFromProject = await getLinkFromDir(dir);
|
||||
if (linkFromProject) {
|
||||
return linkFromProject;
|
||||
}
|
||||
|
||||
// Try the repo linking style (`${repoRoot}/.vercel/repo.json`)
|
||||
return getLinkFromRepo(path);
|
||||
}
|
||||
|
||||
async function getLinkFromRepo(path: string): Promise<ProjectLink | null> {
|
||||
const repoRoot = await findRepoRoot(path);
|
||||
if (!repoRoot) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const vercelDir = join(repoRoot, VERCEL_DIR);
|
||||
const repoJsonPath = join(vercelDir, VERCEL_DIR_REPO);
|
||||
const repoJson = await readJSON(repoJsonPath);
|
||||
const project = findProjectFromPath(
|
||||
repoJson.projects,
|
||||
relative(repoRoot, path)
|
||||
);
|
||||
if (project) {
|
||||
return { orgId: repoJson.orgId, projectId: project.id };
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (!isErrnoException(err) || err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function getLinkFromDir<T = ProjectLink>(
|
||||
@@ -123,7 +156,7 @@ async function getOrgById(client: Client, orgId: string): Promise<Org | null> {
|
||||
|
||||
export async function getLinkedProject(
|
||||
client: Client,
|
||||
path?: string
|
||||
path = process.cwd()
|
||||
): Promise<ProjectLinkResult> {
|
||||
const { output } = client;
|
||||
const VERCEL_ORG_ID = getPlatformEnv('ORG_ID');
|
||||
@@ -252,29 +285,7 @@ export async function linkFolderToProject(
|
||||
);
|
||||
|
||||
// update .gitignore
|
||||
let isGitIgnoreUpdated = false;
|
||||
try {
|
||||
const gitIgnorePath = join(path, '.gitignore');
|
||||
|
||||
let gitIgnore =
|
||||
(await readFile(gitIgnorePath, 'utf8').catch(() => null)) ?? '';
|
||||
const EOL = gitIgnore.includes('\r\n') ? '\r\n' : os.EOL;
|
||||
let contentModified = false;
|
||||
|
||||
if (!gitIgnore.split(EOL).includes(VERCEL_DIR)) {
|
||||
gitIgnore += `${
|
||||
gitIgnore.endsWith(EOL) || gitIgnore.length === 0 ? '' : EOL
|
||||
}${VERCEL_DIR}${EOL}`;
|
||||
contentModified = true;
|
||||
}
|
||||
|
||||
if (contentModified) {
|
||||
await writeFile(gitIgnorePath, gitIgnore);
|
||||
isGitIgnoreUpdated = true;
|
||||
}
|
||||
} catch (error) {
|
||||
// ignore errors since this is non-critical
|
||||
}
|
||||
const isGitIgnoreUpdated = await addToGitIgnore(path);
|
||||
|
||||
output.print(
|
||||
prependEmoji(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "nextjs-node",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "next",
|
||||
@@ -9,5 +10,6 @@
|
||||
"next": "13.0.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
{
|
||||
"name": "10a-nextjs-routes",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"next": "13.0.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "nextjs-src-dir",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "next",
|
||||
@@ -9,5 +10,6 @@
|
||||
"next": "13.0.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -8,5 +8,6 @@
|
||||
"next": "latest",
|
||||
"react": "latest",
|
||||
"react-dom": "latest"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -6,5 +6,6 @@
|
||||
"next": "9.0.0",
|
||||
"react": "16.8.6",
|
||||
"react-dom": "16.8.6"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -8,5 +8,6 @@
|
||||
"next": "12.1.6",
|
||||
"react": "18.1.0",
|
||||
"react-dom": "18.1.0"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "force-module-commonjs",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next",
|
||||
@@ -15,5 +16,6 @@
|
||||
"@types/node": "18.x",
|
||||
"@types/react": "18.0.25",
|
||||
"typescript": "4.9.3"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "nested-tsconfig",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next",
|
||||
@@ -15,5 +16,6 @@
|
||||
"@types/node": "18.x",
|
||||
"@types/react": "18.0.25",
|
||||
"typescript": "4.9.3"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import os from 'os';
|
||||
import url from 'url';
|
||||
import fs from 'fs-extra';
|
||||
import { join } from 'path';
|
||||
import listen from 'async-listen';
|
||||
import { listen } from 'async-listen';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
import { createServer } from 'http';
|
||||
|
||||
|
||||
16
packages/cli/test/fixtures/e2e/cli-extension-whoami/bin.js
vendored
Executable file
16
packages/cli/test/fixtures/e2e/cli-extension-whoami/bin.js
vendored
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env node
|
||||
const { get } = require('http');
|
||||
|
||||
const { VERCEL_API } = process.env;
|
||||
console.log('Hello from a CLI extension!');
|
||||
console.log(`VERCEL_API: ${VERCEL_API}`);
|
||||
|
||||
get(`${VERCEL_API}/v2/user`, async (res) => {
|
||||
let body = '';
|
||||
res.setEncoding('utf8');
|
||||
for await (const chunk of res) {
|
||||
body += chunk;
|
||||
}
|
||||
const data = JSON.parse(body);
|
||||
console.log(`Username: ${data.user.username}`);
|
||||
});
|
||||
6
packages/cli/test/fixtures/e2e/cli-extension-whoami/package.json
vendored
Normal file
6
packages/cli/test/fixtures/e2e/cli-extension-whoami/package.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "cli-extension-whoami",
|
||||
"bin": {
|
||||
"vercel-mywhoami": "bin.js"
|
||||
}
|
||||
}
|
||||
26
packages/cli/test/fixtures/e2e/cli-extension/package-lock.json
generated
vendored
Normal file
26
packages/cli/test/fixtures/e2e/cli-extension/package-lock.json
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "cli-extension",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"cli-extension-whoami": "file:../cli-extension-whoami"
|
||||
}
|
||||
},
|
||||
"../cli-extension-whoami": {
|
||||
"bin": {
|
||||
"vercel-mywhoami": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-extension-whoami": {
|
||||
"resolved": "../cli-extension-whoami",
|
||||
"link": true
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"cli-extension-whoami": {
|
||||
"version": "file:../cli-extension-whoami"
|
||||
}
|
||||
}
|
||||
}
|
||||
6
packages/cli/test/fixtures/e2e/cli-extension/package.json
vendored
Normal file
6
packages/cli/test/fixtures/e2e/cli-extension/package.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"cli-extension-whoami": "file:../cli-extension-whoami"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "13.0.4"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -8,5 +8,6 @@
|
||||
"next": "^8.0.0",
|
||||
"react": "^16.7.0",
|
||||
"react-dom": "^16.7.0"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -8,5 +8,6 @@
|
||||
"next": "^8.0.0",
|
||||
"react": "^16.7.0",
|
||||
"react-dom": "^16.7.0"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -8,5 +8,6 @@
|
||||
"next": "^8.0.0",
|
||||
"react": "^16.7.0",
|
||||
"react-dom": "^16.7.0"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -8,5 +8,6 @@
|
||||
"next": "^8.0.0",
|
||||
"react": "^16.7.0",
|
||||
"react-dom": "^16.7.0"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
14
packages/cli/test/integration-3.test.ts
vendored
14
packages/cli/test/integration-3.test.ts
vendored
@@ -5,6 +5,7 @@ import { URL, parse as parseUrl } from 'url';
|
||||
import semVer from 'semver';
|
||||
import { Readable } from 'stream';
|
||||
import { homedir } from 'os';
|
||||
import { runNpmInstall } from '@vercel/build-utils';
|
||||
import { execCli } from './helpers/exec';
|
||||
import fetch, { RequestInit, RequestInfo } from 'node-fetch';
|
||||
import retry from 'async-retry';
|
||||
@@ -1420,6 +1421,19 @@ test('use build-env', async () => {
|
||||
expect(content.trim()).toBe('bar');
|
||||
});
|
||||
|
||||
test('should invoke CLI extension', async () => {
|
||||
const fixture = path.join(__dirname, 'fixtures/e2e/cli-extension');
|
||||
|
||||
// Ensure the `.bin` is populated in the fixture
|
||||
await runNpmInstall(fixture);
|
||||
|
||||
const output = await execCli(binaryPath, ['mywhoami'], { cwd: fixture });
|
||||
const formatted = formatOutput(output);
|
||||
expect(output.stdout, formatted).toContain('Hello from a CLI extension!');
|
||||
expect(output.stdout, formatted).toContain('VERCEL_API: http://127.0.0.1:');
|
||||
expect(output.stdout, formatted).toContain(`Username: ${contextName}`);
|
||||
});
|
||||
|
||||
// NOTE: Order matters here. This must be the last test in the file.
|
||||
test('default command should prompt login with empty auth.json', async () => {
|
||||
await clearAuthConfig();
|
||||
|
||||
@@ -5,7 +5,7 @@ import chalk from 'chalk';
|
||||
import { PassThrough } from 'stream';
|
||||
import { createServer, Server } from 'http';
|
||||
import express, { Express, Router } from 'express';
|
||||
import listen from 'async-listen';
|
||||
import { listen } from 'async-listen';
|
||||
import Client from '../../src/util/client';
|
||||
import { Output } from '../../src/util/output';
|
||||
|
||||
@@ -101,6 +101,9 @@ export class MockClient extends Client {
|
||||
this.localConfig = {};
|
||||
|
||||
this.scenario = Router();
|
||||
|
||||
this.agent?.destroy();
|
||||
this.agent = undefined;
|
||||
}
|
||||
|
||||
async startMockServer() {
|
||||
|
||||
@@ -31,6 +31,16 @@ const envs: ProjectEnvVariable[] = [
|
||||
updatedAt: 1557241361455,
|
||||
createdAt: 1557241361455,
|
||||
},
|
||||
{
|
||||
type: 'encrypted',
|
||||
id: '781dt89g8r2h789g',
|
||||
key: 'ANOTHER',
|
||||
value: 'one',
|
||||
target: ['preview'],
|
||||
configurationId: null,
|
||||
updatedAt: 1557241361455,
|
||||
createdAt: 1557241361455,
|
||||
},
|
||||
{
|
||||
type: 'encrypted',
|
||||
id: 'r124t6frtu25df16',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { parse } from 'dotenv';
|
||||
import env from '../../../src/commands/env';
|
||||
import { setupUnitFixture } from '../../helpers/setup-unit-fixture';
|
||||
import { client } from '../../mocks/client';
|
||||
@@ -103,6 +104,13 @@ describe('env', () => {
|
||||
expect(rawDevEnv).toContain(
|
||||
'BRANCH_ENV_VAR="env var for a specific branch"'
|
||||
);
|
||||
|
||||
const parsed = parse(rawDevEnv);
|
||||
const keys = Object.keys(parsed);
|
||||
expect(keys).toHaveLength(3);
|
||||
expect(keys[0]).toEqual('ANOTHER');
|
||||
expect(keys[1]).toEqual('BRANCH_ENV_VAR');
|
||||
expect(keys[2]).toEqual('REDIS_CONNECTION_STRING');
|
||||
});
|
||||
|
||||
it('should handle alternate filename', async () => {
|
||||
|
||||
35
packages/cli/test/unit/util/client.test.ts
Normal file
35
packages/cli/test/unit/util/client.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { listen } from 'async-listen';
|
||||
import { createProxy } from 'proxy';
|
||||
import { ProxyAgent } from 'proxy-agent';
|
||||
import { client } from '../../mocks/client';
|
||||
|
||||
describe('Client', () => {
|
||||
describe('fetch()', () => {
|
||||
beforeEach(() => {
|
||||
delete process.env.HTTPS_PROXY;
|
||||
});
|
||||
|
||||
it('should respect the `HTTPS_PROXY` env var', async () => {
|
||||
let connectCount = 0;
|
||||
const proxy = createProxy();
|
||||
const proxyUrl = await listen(proxy);
|
||||
proxy.on('connect', () => {
|
||||
connectCount++;
|
||||
});
|
||||
|
||||
try {
|
||||
process.env.HTTPS_PROXY = proxyUrl.href;
|
||||
|
||||
client.agent = new ProxyAgent({ keepAlive: true });
|
||||
|
||||
expect(connectCount).toEqual(0);
|
||||
const res = await client.fetch('https://example.com/', { json: false });
|
||||
expect(connectCount).toEqual(1);
|
||||
expect(res.status).toEqual(200);
|
||||
} finally {
|
||||
client.agent?.destroy();
|
||||
proxy.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import fetch from 'node-fetch';
|
||||
import listen from 'async-listen';
|
||||
import { listen } from 'async-listen';
|
||||
import { createServer, IncomingMessage, Server, ServerResponse } from 'http';
|
||||
import type { JSONValue } from '@vercel-internals/types';
|
||||
import {
|
||||
@@ -22,7 +22,7 @@ describe('responseError()', () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
server = createServer((req, res) => handler(req, res));
|
||||
url = await listen(server);
|
||||
url = (await listen(server)).toString();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
|
||||
91
packages/cli/test/unit/util/link/repo.test.ts
Normal file
91
packages/cli/test/unit/util/link/repo.test.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join, sep } from 'node:path';
|
||||
import {
|
||||
findProjectFromPath,
|
||||
findRepoRoot,
|
||||
RepoProjectConfig,
|
||||
traverseUpDirectories,
|
||||
} from '../../../../src/util/link/repo';
|
||||
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
// Root of `vercel/vercel` repo
|
||||
const vercelRepoRoot = join(__dirname, '../../../../../..');
|
||||
|
||||
describe('findRepoRoot()', () => {
|
||||
it('should find Git repo root from root', async () => {
|
||||
const repoRoot = await findRepoRoot(vercelRepoRoot);
|
||||
expect(repoRoot).toEqual(vercelRepoRoot);
|
||||
});
|
||||
|
||||
it('should find Git repo root sub directory', async () => {
|
||||
const repoRoot = await findRepoRoot(__dirname);
|
||||
expect(repoRoot).toEqual(vercelRepoRoot);
|
||||
});
|
||||
|
||||
it('should return `undefined` when no Git root found', async () => {
|
||||
const repoRoot = await findRepoRoot(tmpdir());
|
||||
expect(repoRoot).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('traverseUpDirectories()', () => {
|
||||
test.each(
|
||||
isWindows
|
||||
? [
|
||||
{
|
||||
input: 'C:\\foo\\bar\\baz',
|
||||
expected: ['C:\\foo\\bar\\baz', 'C:\\foo\\bar', 'C:\\foo', 'C:\\'],
|
||||
},
|
||||
{
|
||||
input: 'C:\\foo\\..\\bar\\.\\baz',
|
||||
expected: ['C:\\bar\\baz', 'C:\\bar', 'C:\\'],
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
input: '/foo/bar/baz',
|
||||
expected: ['/foo/bar/baz', '/foo/bar', '/foo', '/'],
|
||||
},
|
||||
{
|
||||
input: '/foo/../bar/./baz',
|
||||
expected: ['/bar/baz', '/bar', '/'],
|
||||
},
|
||||
]
|
||||
)('should traverse "$input"', ({ input, expected }) => {
|
||||
expect(Array.from(traverseUpDirectories(input))).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findProjectFromPath()', () => {
|
||||
const projects: RepoProjectConfig[] = [
|
||||
{ id: 'root', name: 'r', directory: '.' },
|
||||
{ id: 'site', name: 'a', directory: 'apps/site' },
|
||||
{ id: 'site2', name: 'a', directory: 'apps/site2' },
|
||||
{ id: 'other', name: 'b', directory: 'apps/other' },
|
||||
{ id: 'nested', name: 'n', directory: 'apps/other/nested' },
|
||||
];
|
||||
|
||||
it.each([
|
||||
{ id: 'root', path: '.' },
|
||||
{ id: 'root', path: 'lib' },
|
||||
{ id: 'root', path: 'lib' },
|
||||
{ id: 'site', path: `apps${sep}site` },
|
||||
{ id: 'site', path: `apps${sep}site` },
|
||||
{ id: 'site', path: `apps${sep}site${sep}components` },
|
||||
{ id: 'site2', path: `apps${sep}site2` },
|
||||
{ id: 'site2', path: `apps${sep}site2${sep}inner` },
|
||||
{ id: 'other', path: `apps${sep}other` },
|
||||
{ id: 'other', path: `apps${sep}other${sep}lib` },
|
||||
{ id: 'nested', path: `apps${sep}other${sep}nested` },
|
||||
{ id: 'nested', path: `apps${sep}other${sep}nested${sep}foo` },
|
||||
])('should find Project "$id" for path "$path"', ({ path, id }) => {
|
||||
const actual = findProjectFromPath(projects, path);
|
||||
expect(actual?.id).toEqual(id);
|
||||
});
|
||||
|
||||
it('should return `undefined` when there are no matching Projects', () => {
|
||||
const actual = findProjectFromPath([projects[1]], '.');
|
||||
expect(actual).toBeUndefined();
|
||||
});
|
||||
});
|
||||
8
packages/client/CHANGELOG.md
Normal file
8
packages/client/CHANGELOG.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# @vercel/client
|
||||
|
||||
## 12.5.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`71b9f3a94`](https://github.com/vercel/vercel/commit/71b9f3a94b7922607f8f24bf7b2bd1742e62cc05)]:
|
||||
- @vercel/build-utils@6.7.3
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "12.4.12",
|
||||
"version": "12.5.1",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -35,7 +35,7 @@
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "6.7.2",
|
||||
"@vercel/build-utils": "6.7.3",
|
||||
"@vercel/routing-utils": "2.2.1",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
|
||||
@@ -56,7 +56,7 @@ export async function* checkDeploymentStatus(
|
||||
teamId ? `?teamId=${teamId}` : ''
|
||||
}`,
|
||||
token,
|
||||
{ apiUrl, userAgent }
|
||||
{ apiUrl, userAgent, agent: clientOptions.agent }
|
||||
);
|
||||
const deploymentUpdate = await deploymentData.json();
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ async function* postDeployment(
|
||||
}),
|
||||
apiUrl: clientOptions.apiUrl,
|
||||
userAgent: clientOptions.userAgent,
|
||||
agent: clientOptions.agent,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { Agent } from 'http';
|
||||
import type {
|
||||
Builder,
|
||||
BuilderFunctions,
|
||||
@@ -31,6 +32,7 @@ export interface VercelClientOptions {
|
||||
isDirectory?: boolean;
|
||||
skipAutoDetectionConfirmation?: boolean;
|
||||
archive?: ArchiveFormat;
|
||||
agent?: Agent;
|
||||
}
|
||||
|
||||
/** @deprecated Use VercelClientOptions instead. */
|
||||
|
||||
@@ -79,7 +79,7 @@ export async function* upload(
|
||||
debug('Building an upload list...');
|
||||
|
||||
const semaphore = new Sema(50, { capacity: 50 });
|
||||
const agent = apiUrl?.startsWith('https://')
|
||||
const defaultAgent = apiUrl?.startsWith('https://')
|
||||
? new https.Agent({ keepAlive: true })
|
||||
: new http.Agent({ keepAlive: true });
|
||||
|
||||
@@ -132,7 +132,7 @@ export async function* upload(
|
||||
API_FILES,
|
||||
token,
|
||||
{
|
||||
agent,
|
||||
agent: clientOptions.agent || defaultAgent,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
{
|
||||
"name": "@vercel/frameworks",
|
||||
"version": "1.4.1",
|
||||
"version": "1.4.2",
|
||||
"main": "./dist/frameworks.js",
|
||||
"types": "./dist/frameworks.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vercel/vercel.git",
|
||||
"directory": "packages/frameworks"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
|
||||
62
packages/frameworks/test/frameworks.unit.test.ts
vendored
62
packages/frameworks/test/frameworks.unit.test.ts
vendored
@@ -62,16 +62,40 @@ const SchemaSettings = {
|
||||
],
|
||||
};
|
||||
|
||||
const RouteSchema = {
|
||||
type: 'array',
|
||||
items: {
|
||||
properties: {
|
||||
src: { type: 'string' },
|
||||
dest: { type: 'string' },
|
||||
status: { type: 'number' },
|
||||
handle: { type: 'string' },
|
||||
headers: { type: 'object' },
|
||||
continue: { type: 'boolean' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Schema = {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
required: ['name', 'slug', 'logo', 'description', 'settings'],
|
||||
additionalProperties: false,
|
||||
required: [
|
||||
'name',
|
||||
'slug',
|
||||
'logo',
|
||||
'description',
|
||||
'settings',
|
||||
'getOutputDirName',
|
||||
],
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
slug: { type: ['string', 'null'] },
|
||||
sort: { type: 'number' },
|
||||
logo: { type: 'string' },
|
||||
darkModeLogo: { type: 'string' },
|
||||
screenshot: { type: 'string' },
|
||||
demo: { type: 'string' },
|
||||
tagline: { type: 'string' },
|
||||
website: { type: 'string' },
|
||||
@@ -116,6 +140,26 @@ const Schema = {
|
||||
outputDirectory: SchemaSettings,
|
||||
},
|
||||
},
|
||||
getOutputDirName: {
|
||||
isFunction: true,
|
||||
},
|
||||
defaultRoutes: {
|
||||
oneOf: [{ isFunction: true }, RouteSchema],
|
||||
},
|
||||
defaulHeaders: {
|
||||
type: 'array',
|
||||
items: {
|
||||
properties: {
|
||||
source: { type: 'string' },
|
||||
regex: { type: 'string' },
|
||||
headers: { type: 'object' },
|
||||
continue: { type: 'boolean' },
|
||||
},
|
||||
},
|
||||
},
|
||||
disableRootMiddleware: {
|
||||
type: 'boolean',
|
||||
},
|
||||
recommendedIntegrations: {
|
||||
type: 'array',
|
||||
items: {
|
||||
@@ -167,7 +211,8 @@ describe('frameworks', () => {
|
||||
});
|
||||
|
||||
it('ensure schema', async () => {
|
||||
const ajv = new Ajv();
|
||||
const ajv = getValidator();
|
||||
|
||||
const result = ajv.validate(Schema, frameworkList);
|
||||
|
||||
if (ajv.errors) {
|
||||
@@ -246,3 +291,16 @@ describe('frameworks', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function getValidator() {
|
||||
const ajv = new Ajv();
|
||||
|
||||
ajv.addKeyword('isFunction', {
|
||||
compile: shouldMatch => data => {
|
||||
const matches = typeof data === 'function';
|
||||
return (shouldMatch && matches) || (!shouldMatch && !matches);
|
||||
},
|
||||
});
|
||||
|
||||
return ajv;
|
||||
}
|
||||
|
||||
7
packages/fs-detectors/CHANGELOG.md
Normal file
7
packages/fs-detectors/CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# @vercel/fs-detectors
|
||||
|
||||
## 3.9.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- clarify next.js dupe api directory warning ([#9979](https://github.com/vercel/vercel/pull/9979))
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/fs-detectors",
|
||||
"version": "3.9.1",
|
||||
"version": "3.9.3",
|
||||
"description": "Vercel filesystem detectors",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -20,7 +20,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/error-utils": "1.0.10",
|
||||
"@vercel/frameworks": "1.4.1",
|
||||
"@vercel/frameworks": "1.4.2",
|
||||
"@vercel/routing-utils": "2.2.1",
|
||||
"glob": "8.0.3",
|
||||
"js-yaml": "4.1.0",
|
||||
@@ -35,7 +35,7 @@
|
||||
"@types/minimatch": "3.0.5",
|
||||
"@types/node": "14.18.33",
|
||||
"@types/semver": "7.3.10",
|
||||
"@vercel/build-utils": "6.7.2",
|
||||
"@vercel/build-utils": "6.7.3",
|
||||
"typescript": "4.9.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,7 +339,7 @@ export async function detectBuilders(
|
||||
warnings.push({
|
||||
code: 'conflicting_files',
|
||||
message:
|
||||
'When using Next.js, it is recommended to place Node.js Serverless Functions inside of the `pages/api` (provided by Next.js) directory instead of `api` (provided by Vercel).',
|
||||
'When using Next.js, it is recommended to place JavaScript Functions inside of the `pages/api` (provided by Next.js) directory instead of `api` (provided by Vercel). Other languages (Python, Go, etc) should still go in the `api` directory.',
|
||||
link: 'https://nextjs.org/docs/api-routes/introduction',
|
||||
action: 'Learn More',
|
||||
});
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"next": "^4.3.2"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"next": "^4.3.2"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"next": "^4.3.2"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"next": "^4.3.2"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"next": "^4.3.2"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"next": "^4.3.2"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"next": "^4.3.2"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"next": "^4.3.2"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"next": "^4.3.2"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "^4.3.2"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "1.2.3"
|
||||
}
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
|
||||
@@ -2785,7 +2785,7 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
||||
{
|
||||
code: 'conflicting_files',
|
||||
message:
|
||||
'When using Next.js, it is recommended to place Node.js Serverless Functions inside of the `pages/api` (provided by Next.js) directory instead of `api` (provided by Vercel).',
|
||||
'When using Next.js, it is recommended to place JavaScript Functions inside of the `pages/api` (provided by Next.js) directory instead of `api` (provided by Vercel). Other languages (Python, Go, etc) should still go in the `api` directory.',
|
||||
link: 'https://nextjs.org/docs/api-routes/introduction',
|
||||
action: 'Learn More',
|
||||
},
|
||||
@@ -2820,7 +2820,7 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
||||
{
|
||||
code: 'conflicting_files',
|
||||
message:
|
||||
'When using Next.js, it is recommended to place Node.js Serverless Functions inside of the `pages/api` (provided by Next.js) directory instead of `api` (provided by Vercel).',
|
||||
'When using Next.js, it is recommended to place JavaScript Functions inside of the `pages/api` (provided by Next.js) directory instead of `api` (provided by Vercel). Other languages (Python, Go, etc) should still go in the `api` directory.',
|
||||
link: 'https://nextjs.org/docs/api-routes/introduction',
|
||||
action: 'Learn More',
|
||||
},
|
||||
|
||||
15
packages/gatsby-plugin-vercel-builder/CHANGELOG.md
Normal file
15
packages/gatsby-plugin-vercel-builder/CHANGELOG.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# @vercel/gatsby-plugin-vercel-builder
|
||||
|
||||
## 1.3.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- add repo details for gatsby-plugin-vercel-builder ([#9990](https://github.com/vercel/vercel/pull/9990))
|
||||
|
||||
## 1.3.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`71b9f3a94`](https://github.com/vercel/vercel/commit/71b9f3a94b7922607f8f24bf7b2bd1742e62cc05)]:
|
||||
- @vercel/build-utils@6.7.3
|
||||
- @vercel/node@2.14.3
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/gatsby-plugin-vercel-builder",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.5",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -8,6 +8,11 @@
|
||||
"gatsby-node.js",
|
||||
"gatsby-node.js.map"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vercel/vercel.git",
|
||||
"directory": "packages/gatsby-plugin-vercel-builder"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm build:src && pnpm build:gatsby",
|
||||
"build:gatsby": "tsc -p tsconfig.gatsby.json",
|
||||
@@ -15,8 +20,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@sinclair/typebox": "0.25.24",
|
||||
"@vercel/build-utils": "6.7.2",
|
||||
"@vercel/node": "2.14.1",
|
||||
"@vercel/build-utils": "6.7.3",
|
||||
"@vercel/node": "2.14.3",
|
||||
"@vercel/routing-utils": "2.2.1",
|
||||
"esbuild": "0.14.47",
|
||||
"etag": "1.8.1",
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"@types/node-fetch": "^2.3.0",
|
||||
"@types/tar": "^4.0.0",
|
||||
"@types/yauzl-promise": "2.1.0",
|
||||
"@vercel/build-utils": "6.7.2",
|
||||
"@vercel/build-utils": "6.7.3",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"async-retry": "1.3.1",
|
||||
"execa": "^1.0.0",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "27.5.1",
|
||||
"@types/node": "14.18.33",
|
||||
"@vercel/build-utils": "6.7.2",
|
||||
"@vercel/build-utils": "6.7.3",
|
||||
"@vercel/static-config": "2.0.17",
|
||||
"execa": "3.2.0",
|
||||
"fs-extra": "11.1.0",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user