mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-23 18:59:59 +00:00
Compare commits
120 Commits
@vercel/bu
...
@vercel/st
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e90521b5a4 | ||
|
|
10c598834a | ||
|
|
e58b34b82c | ||
|
|
02e1c921ac | ||
|
|
fcb14fa70e | ||
|
|
46e18128c7 | ||
|
|
e06d5247ef | ||
|
|
02195b92a7 | ||
|
|
7945155a0f | ||
|
|
edb6043c2c | ||
|
|
971481ba51 | ||
|
|
db8e456603 | ||
|
|
d1b4f24a4a | ||
|
|
4eb39ed53b | ||
|
|
6c7236ffd5 | ||
|
|
f0def040ac | ||
|
|
ee8dff2dc9 | ||
|
|
df86a6d48c | ||
|
|
b3958b3d12 | ||
|
|
8dd22703a7 | ||
|
|
c7e2280d23 | ||
|
|
2c239a6ee2 | ||
|
|
43709fbc74 | ||
|
|
52cd486e1d | ||
|
|
175f302a7f | ||
|
|
070bbd5825 | ||
|
|
cdeb4004c9 | ||
|
|
6bb162e5f5 | ||
|
|
8282442939 | ||
|
|
1f4f2af2f2 | ||
|
|
f1bb0e465a | ||
|
|
6dab4399c2 | ||
|
|
e87465d076 | ||
|
|
2f53b0dcdb | ||
|
|
b2e5212d0f | ||
|
|
28c9999c10 | ||
|
|
edb31fb412 | ||
|
|
2f0ea24552 | ||
|
|
7d09fdc5a4 | ||
|
|
23dce48b23 | ||
|
|
d7d308ca09 | ||
|
|
0e412361ad | ||
|
|
d563a99fb8 | ||
|
|
6ce4c2e182 | ||
|
|
e31d6d4062 | ||
|
|
f2f421b494 | ||
|
|
46836e12b5 | ||
|
|
3fc2611bf0 | ||
|
|
b4d8c411bd | ||
|
|
82b7d6980c | ||
|
|
895b233605 | ||
|
|
d9e6b5348b | ||
|
|
1934a64864 | ||
|
|
c412642668 | ||
|
|
793fe9aee1 | ||
|
|
ddc54d2ca4 | ||
|
|
39e5f0a364 | ||
|
|
43ed9ec859 | ||
|
|
8ba44fca79 | ||
|
|
27dbefaecf | ||
|
|
b4a13913c7 | ||
|
|
f842266b2e | ||
|
|
ad0cc858ed | ||
|
|
4bb7180de9 | ||
|
|
7a4faa480d | ||
|
|
421be5d738 | ||
|
|
b8eaf10974 | ||
|
|
92a4bf27cf | ||
|
|
2672838b64 | ||
|
|
1c96071ddc | ||
|
|
71cdf759da | ||
|
|
93ebd213de | ||
|
|
a32ba8f214 | ||
|
|
d416f70a6e | ||
|
|
ba9e1dd0ba | ||
|
|
d513f74b70 | ||
|
|
47f92f8f14 | ||
|
|
9aa669d735 | ||
|
|
99cab6f34a | ||
|
|
547e7dccf7 | ||
|
|
dce9a89407 | ||
|
|
c1cdddd974 | ||
|
|
4bce77dd4b | ||
|
|
3a79810944 | ||
|
|
5b3fbdddb3 | ||
|
|
25e9ecb5a2 | ||
|
|
415515840c | ||
|
|
9ba14be12e | ||
|
|
f1321946c5 | ||
|
|
230b69abf4 | ||
|
|
0d96f80ac8 | ||
|
|
178f5e7f00 | ||
|
|
dbfdf20c9d | ||
|
|
a80ed4ec9b | ||
|
|
833c3d7c01 | ||
|
|
19d0db7270 | ||
|
|
041e8cc601 | ||
|
|
b118e461b1 | ||
|
|
e519d49d7b | ||
|
|
27683818ba | ||
|
|
e016e38229 | ||
|
|
5db1c5e610 | ||
|
|
24c228569f | ||
|
|
963de9b64f | ||
|
|
ab7fd52305 | ||
|
|
0fdb0dac91 | ||
|
|
bb0b632dcf | ||
|
|
ced9495143 | ||
|
|
fadc3f2588 | ||
|
|
a1d548dfef | ||
|
|
754090a8ab | ||
|
|
8269a48ee0 | ||
|
|
9f05a1865c | ||
|
|
8d1afc026f | ||
|
|
130f36aad6 | ||
|
|
dd87c9b0c6 | ||
|
|
f813b3340b | ||
|
|
976b02e895 | ||
|
|
843be9658c | ||
|
|
ad501a4cd0 |
@@ -34,3 +34,6 @@ packages/now-node-bridge/bridge.*
|
|||||||
|
|
||||||
# now-static-build
|
# now-static-build
|
||||||
packages/now-static-build/test/fixtures
|
packages/now-static-build/test/fixtures
|
||||||
|
|
||||||
|
# redwood
|
||||||
|
packages/redwood/test/fixtures
|
||||||
|
|||||||
2
.github/workflows/cancel.yml
vendored
2
.github/workflows/cancel.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 2
|
timeout-minutes: 2
|
||||||
steps:
|
steps:
|
||||||
- uses: styfle/cancel-workflow-action@0.3.2
|
- uses: styfle/cancel-workflow-action@0.4.1
|
||||||
with:
|
with:
|
||||||
workflow_id: 849295, 849296, 849297, 849298
|
workflow_id: 849295, 849296, 849297, 849298
|
||||||
access_token: ${{ github.token }}
|
access_token: ${{ github.token }}
|
||||||
|
|||||||
8
.github/workflows/test-integration-cli.yml
vendored
8
.github/workflows/test-integration-cli.yml
vendored
@@ -20,8 +20,12 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: git fetch origin master --depth=10
|
with:
|
||||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
fetch-depth: 100
|
||||||
|
- run: git --version
|
||||||
|
- run: git fetch origin master --depth=100
|
||||||
|
- run: git fetch origin ${{ github.ref }} --depth=100
|
||||||
|
- run: git diff origin/master...HEAD --name-only
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
- run: yarn run build
|
- run: yarn run build
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
|
|||||||
8
.github/workflows/test-integration-dev.yml
vendored
8
.github/workflows/test-integration-dev.yml
vendored
@@ -20,8 +20,12 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: git fetch origin master --depth=10
|
with:
|
||||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
fetch-depth: 100
|
||||||
|
- run: git --version
|
||||||
|
- run: git fetch origin master --depth=100
|
||||||
|
- run: git fetch origin ${{ github.ref }} --depth=100
|
||||||
|
- run: git diff origin/master...HEAD --name-only
|
||||||
- name: Install Hugo
|
- name: Install Hugo
|
||||||
if: matrix.os == 'macos-latest'
|
if: matrix.os == 'macos-latest'
|
||||||
run: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.56.0/hugo_0.56.0_macOS-64bit.tar.gz && tar -xzf hugo_0.56.0_macOS-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
run: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.56.0/hugo_0.56.0_macOS-64bit.tar.gz && tar -xzf hugo_0.56.0_macOS-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
||||||
|
|||||||
8
.github/workflows/test-integration-once.yml
vendored
8
.github/workflows/test-integration-once.yml
vendored
@@ -15,8 +15,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: git fetch origin master --depth=10
|
with:
|
||||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
fetch-depth: 100
|
||||||
|
- run: git --version
|
||||||
|
- run: git fetch origin master --depth=100
|
||||||
|
- run: git fetch origin ${{ github.ref }} --depth=100
|
||||||
|
- run: git diff origin/master...HEAD --name-only
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
- run: yarn run build
|
- run: yarn run build
|
||||||
- run: yarn test-integration-once --clean false
|
- run: yarn test-integration-once --clean false
|
||||||
|
|||||||
10
.github/workflows/test-unit.yml
vendored
10
.github/workflows/test-unit.yml
vendored
@@ -20,9 +20,15 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: git fetch origin master --depth=10
|
with:
|
||||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
fetch-depth: 100
|
||||||
|
- run: git --version
|
||||||
|
- run: git fetch origin master --depth=100
|
||||||
|
- run: git fetch origin ${{ github.ref }} --depth=100
|
||||||
|
- run: git diff origin/master...HEAD --name-only
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node }}
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
- run: yarn run build
|
- run: yarn run build
|
||||||
- run: yarn run lint
|
- run: yarn run lint
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,4 @@
|
|||||||
node_modules
|
node_modules
|
||||||
package-lock.json
|
|
||||||
dist
|
dist
|
||||||
.vscode
|
.vscode
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
|
|||||||
@@ -2,16 +2,14 @@
|
|||||||
|
|
||||||
#### Why This Error Occurred
|
#### Why This Error Occurred
|
||||||
|
|
||||||
The domain you supplied cannot be verified using either the intended set of nameservers or the given verification TXT record.
|
The domain you supplied cannot be verified using the intended nameservers.
|
||||||
|
|
||||||
#### Possible Ways to Fix It
|
#### Possible Way to Fix It
|
||||||
|
|
||||||
Apply the intended set of nameservers to your domain or add the given TXT verification record through your domain provider.
|
Apply the intended set of nameservers to your domain.
|
||||||
|
|
||||||
You can retrieve both the intended nameservers and TXT verification record for the domain you wish to verify by running `vercel domains inspect <domain>`.
|
You can retrieve both the intended nameservers and TXT verification record for the domain you wish to verify by running `vercel domains inspect <domain>`.
|
||||||
|
|
||||||
When you have added either verification method to your domain, you can run `vercel domains verify <domain>` again to complete verification for your domain.
|
|
||||||
|
|
||||||
Vercel will also automatically check periodically that your domain has been verified and automatically mark it as such if we detect either verification method on the domain.
|
Vercel will also automatically check periodically that your domain has been verified and automatically mark it as such if we detect either verification method on the domain.
|
||||||
|
|
||||||
If you would not like to verify your domain, you can remove it from your account using `vercel domains rm <domain>`.
|
If you would not like to verify your domain, you can remove it from your account using `vercel domains rm <domain>`.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ _Live Example: https://ionic-angular.now-examples.now.sh_
|
|||||||
To get started with Ionic Angular deployed with Vercel, you can use the [Ionic CLI](https://ionicframework.com/docs/cli) to initialize the project:
|
To get started with Ionic Angular deployed with Vercel, you can use the [Ionic CLI](https://ionicframework.com/docs/cli) to initialize the project:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ npx ionic start [project-name] conference --type angular && cd [project-name]
|
$ npx @ionic/cli start [project-name] conference --type angular && cd [project-name]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Deploying From Your Terminal
|
### Deploying From Your Terminal
|
||||||
|
|||||||
13585
examples/ionic-angular/package-lock.json
generated
Normal file
13585
examples/ionic-angular/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,7 @@
|
|||||||
"@ionic-native/in-app-browser": "5.0.0-beta.15",
|
"@ionic-native/in-app-browser": "5.0.0-beta.15",
|
||||||
"@ionic-native/splash-screen": "5.0.0-beta.15",
|
"@ionic-native/splash-screen": "5.0.0-beta.15",
|
||||||
"@ionic-native/status-bar": "5.0.0-beta.15",
|
"@ionic-native/status-bar": "5.0.0-beta.15",
|
||||||
"@ionic/angular": "^5.0.6",
|
"@ionic/angular": "^5.1.1",
|
||||||
"@ionic/storage": "^2.1.3",
|
"@ionic/storage": "^2.1.3",
|
||||||
"cordova-android": "^8.1.0",
|
"cordova-android": "^8.1.0",
|
||||||
"cordova-ios": "^5.1.1",
|
"cordova-ios": "^5.1.1",
|
||||||
|
|||||||
14
examples/nextjs/.gitignore
vendored
14
examples/nextjs/.gitignore
vendored
@@ -11,20 +11,24 @@
|
|||||||
# next.js
|
# next.js
|
||||||
/.next/
|
/.next/
|
||||||
/out/
|
/out/
|
||||||
!public/
|
|
||||||
|
|
||||||
# production
|
# production
|
||||||
/build
|
/build
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.env*
|
*.pem
|
||||||
|
|
||||||
# debug
|
# debug
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
|
||||||
# Environment Variables
|
# local env files
|
||||||
.env
|
.env.local
|
||||||
.env.build
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/zeit/next.js/tree/canary/packages/create-next-app).
|
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ To learn more about Next.js, take a look at the following resources:
|
|||||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||||
|
|
||||||
You can check out [the Next.js GitHub repository](https://github.com/zeit/next.js) - your feedback and contributions are welcome!
|
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||||
|
|
||||||
## Deploy on Vercel
|
## Deploy on Vercel
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
"start": "next start"
|
"start": "next start"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "^9.3.3",
|
"next": "9.5.1",
|
||||||
"react": "^16.13.0",
|
"react": "16.13.1",
|
||||||
"react-dom": "^16.13.0"
|
"react-dom": "16.13.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
examples/nextjs/pages/_app.js
Normal file
7
examples/nextjs/pages/_app.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import '../styles/globals.css'
|
||||||
|
|
||||||
|
function MyApp({ Component, pageProps }) {
|
||||||
|
return <Component {...pageProps} />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyApp
|
||||||
6
examples/nextjs/pages/api/hello.js
Normal file
6
examples/nextjs/pages/api/hello.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||||
|
|
||||||
|
export default (req, res) => {
|
||||||
|
res.statusCode = 200
|
||||||
|
res.json({ name: 'John Doe' })
|
||||||
|
}
|
||||||
@@ -1,203 +1,65 @@
|
|||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
|
import styles from '../styles/Home.module.css'
|
||||||
|
|
||||||
const Home = () => (
|
export default function Home() {
|
||||||
<div className="container">
|
return (
|
||||||
<Head>
|
<div className={styles.container}>
|
||||||
<title>Create Next App</title>
|
<Head>
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<title>Create Next App</title>
|
||||||
</Head>
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
</Head>
|
||||||
|
|
||||||
<main>
|
<main className={styles.main}>
|
||||||
<h1 className="title">
|
<h1 className={styles.title}>
|
||||||
Welcome to <a href="https://nextjs.org">Next.js!</a>
|
Welcome to <a href="https://nextjs.org">Next.js!</a>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p className="description">
|
<p className={styles.description}>
|
||||||
Get started by editing <code>pages/index.js</code>
|
Get started by editing{' '}
|
||||||
</p>
|
<code className={styles.code}>pages/index.js</code>
|
||||||
|
</p>
|
||||||
|
|
||||||
<div className="grid">
|
<div className={styles.grid}>
|
||||||
<a href="https://nextjs.org/docs" className="card">
|
<a href="https://nextjs.org/docs" className={styles.card}>
|
||||||
<h3>Documentation →</h3>
|
<h3>Documentation →</h3>
|
||||||
<p>Find in-depth information about Next.js features and API.</p>
|
<p>Find in-depth information about Next.js features and API.</p>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="https://nextjs.org/learn" className="card">
|
<a href="https://nextjs.org/learn" className={styles.card}>
|
||||||
<h3>Learn →</h3>
|
<h3>Learn →</h3>
|
||||||
<p>Learn about Next.js in an interactive course with quizzes!</p>
|
<p>Learn about Next.js in an interactive course with quizzes!</p>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="https://github.com/vercel/next.js/tree/master/examples"
|
||||||
|
className={styles.card}
|
||||||
|
>
|
||||||
|
<h3>Examples →</h3>
|
||||||
|
<p>Discover and deploy boilerplate example Next.js projects.</p>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="https://vercel.com/import?filter=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||||
|
className={styles.card}
|
||||||
|
>
|
||||||
|
<h3>Deploy →</h3>
|
||||||
|
<p>
|
||||||
|
Instantly deploy your Next.js site to a public URL with Vercel.
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer className={styles.footer}>
|
||||||
<a
|
<a
|
||||||
href="https://github.com/zeit/next.js/tree/master/examples"
|
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||||
className="card"
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
<h3>Examples →</h3>
|
Powered by{' '}
|
||||||
<p>Discover and deploy boilerplate example Next.js projects.</p>
|
<img src="/vercel.svg" alt="Vercel Logo" className={styles.logo} />
|
||||||
</a>
|
</a>
|
||||||
|
</footer>
|
||||||
<a
|
</div>
|
||||||
href="https://vercel.com/new?filter=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
)
|
||||||
className="card"
|
}
|
||||||
>
|
|
||||||
<h3>Deploy →</h3>
|
|
||||||
<p>
|
|
||||||
Instantly deploy your Next.js site to a public URL with Vercel.
|
|
||||||
</p>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<a
|
|
||||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Powered by <img src="/vercel.svg" alt="Vercel Logo" />
|
|
||||||
</a>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<style jsx>{`
|
|
||||||
.container {
|
|
||||||
min-height: 100vh;
|
|
||||||
padding: 0 0.5rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
padding: 5rem 0;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
width: 100%;
|
|
||||||
height: 100px;
|
|
||||||
border-top: 1px solid #eaeaea;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer img {
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer a {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title a {
|
|
||||||
color: #0070f3;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title a:hover,
|
|
||||||
.title a:focus,
|
|
||||||
.title a:active {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
margin: 0;
|
|
||||||
line-height: 1.15;
|
|
||||||
font-size: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title,
|
|
||||||
.description {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
line-height: 1.5;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background: #fafafa;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 0.75rem;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
font-family: Menlo, Monaco, Lucida Console, Liberation Mono,
|
|
||||||
DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
max-width: 800px;
|
|
||||||
margin-top: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin: 1rem;
|
|
||||||
flex-basis: 45%;
|
|
||||||
padding: 1.5rem;
|
|
||||||
text-align: left;
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
border: 1px solid #eaeaea;
|
|
||||||
border-radius: 10px;
|
|
||||||
transition: color 0.15s ease, border-color 0.15s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover,
|
|
||||||
.card:focus,
|
|
||||||
.card:active {
|
|
||||||
color: #0070f3;
|
|
||||||
border-color: #0070f3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card h3 {
|
|
||||||
margin: 0 0 1rem 0;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card p {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
.grid {
|
|
||||||
width: 100%;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
|
|
||||||
<style jsx global>{`
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
|
||||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
`}</style>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
export default Home
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
<svg width="70" height="16" viewBox="0 0 70 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
|
||||||
<path d="M9.255.05l9.108 15.753H.148L9.255.05zM39.434 8.418c0-2.535-1.87-4.307-4.554-4.307-2.683 0-4.554 1.772-4.554 4.307 0 2.487 2.019 4.308 4.8 4.308 1.526 0 2.905-.566 3.79-1.6l-1.673-.96c-.517.517-1.28.837-2.117.837-1.23 0-2.29-.665-2.658-1.674l-.074-.172h6.966a3.76 3.76 0 00.074-.739zm-7.065-.738l.05-.148c.32-1.058 1.255-1.698 2.436-1.698 1.207 0 2.117.64 2.437 1.698l.05.148h-4.973zM65.945 8.418c0-2.535-1.871-4.307-4.554-4.307-2.683 0-4.554 1.772-4.554 4.307 0 2.487 2.018 4.308 4.8 4.308 1.526 0 2.904-.566 3.79-1.6l-1.673-.96c-.517.517-1.28.837-2.117.837-1.23 0-2.29-.665-2.659-1.674l-.073-.172h6.966a3.76 3.76 0 00.074-.739zM58.88 7.68l.05-.148c.32-1.058 1.255-1.698 2.436-1.698 1.206 0 2.117.64 2.437 1.698l.05.148H58.88zM54.13 7.015l1.673-.96c-.788-1.23-2.19-1.92-3.89-1.92-2.682 0-4.553 1.773-4.553 4.308 0 2.536 1.87 4.308 4.554 4.308 1.698 0 3.101-.69 3.89-1.92l-1.675-.96c-.443.738-1.23 1.157-2.215 1.157-1.55 0-2.585-1.034-2.585-2.585 0-1.55 1.034-2.585 2.585-2.585.96 0 1.772.419 2.215 1.157zM69.637 1.428h-1.97v11.077h1.97V1.428zM31.779 1.428h-2.265L25.182 8.91l-4.333-7.483H18.56l6.622 11.421 6.597-11.421zM45.71 6.4c.222 0 .444.025.665.074V4.382c-1.673.049-3.249.984-3.249 2.141V4.382h-1.97v8.123h1.97v-3.52c0-1.527 1.059-2.585 2.585-2.585z" fill="#000"/>
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
</svg>
|
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.1 KiB |
123
examples/nextjs/styles/Home.module.css
Normal file
123
examples/nextjs/styles/Home.module.css
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
.container {
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
padding: 5rem 0;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
width: 100%;
|
||||||
|
height: 100px;
|
||||||
|
border-top: 1px solid #eaeaea;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer img {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title a {
|
||||||
|
color: #0070f3;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title a:hover,
|
||||||
|
.title a:focus,
|
||||||
|
.title a:active {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.15;
|
||||||
|
font-size: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title,
|
||||||
|
.description {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
line-height: 1.5;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code {
|
||||||
|
background: #fafafa;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 0.75rem;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
|
||||||
|
Bitstream Vera Sans Mono, Courier New, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
max-width: 800px;
|
||||||
|
margin-top: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
margin: 1rem;
|
||||||
|
flex-basis: 45%;
|
||||||
|
padding: 1.5rem;
|
||||||
|
text-align: left;
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 1px solid #eaeaea;
|
||||||
|
border-radius: 10px;
|
||||||
|
transition: color 0.15s ease, border-color 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover,
|
||||||
|
.card:focus,
|
||||||
|
.card:active {
|
||||||
|
color: #0070f3;
|
||||||
|
border-color: #0070f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card h3 {
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.grid {
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
examples/nextjs/styles/globals.css
Normal file
16
examples/nextjs/styles/globals.css
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
html,
|
||||||
|
body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
||||||
|
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
10
examples/redwoodjs/.editorconfig
Normal file
10
examples/redwoodjs/.editorconfig
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
7
examples/redwoodjs/.env.defaults
Normal file
7
examples/redwoodjs/.env.defaults
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# These environment variables will be used by default if you do not create any
|
||||||
|
# yourself in .env. This file should be safe to check into your version control
|
||||||
|
# system. Any custom values should go in .env and .env should *not* be checked
|
||||||
|
# into version control.
|
||||||
|
|
||||||
|
DATABASE_URL=file:./dev.db
|
||||||
|
BINARY_TARGET=native
|
||||||
10
examples/redwoodjs/.gitignore
vendored
Normal file
10
examples/redwoodjs/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
|
.netlify
|
||||||
|
dev.db
|
||||||
|
dist
|
||||||
|
dist-babel
|
||||||
|
node_modules
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
.vercel
|
||||||
1
examples/redwoodjs/.nvmrc
Normal file
1
examples/redwoodjs/.nvmrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
lts/*
|
||||||
21
examples/redwoodjs/LICENSE
Normal file
21
examples/redwoodjs/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Redwood
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
29
examples/redwoodjs/README.md
Normal file
29
examples/redwoodjs/README.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|

|
||||||
|
|
||||||
|
# RedwoodJS Example
|
||||||
|
|
||||||
|
This directory is a brief example of a [RedwoodJS](https://redwoodjs.com) app with [Serverless Functions](https://vercel.com/docs/v2/serverless-functions/introduction) that can be deployed with Vercel and zero configuration.
|
||||||
|
|
||||||
|
## Deploy Your Own
|
||||||
|
|
||||||
|
Deploy your own RedwoodJS project, along with Serverless Functions, with Vercel.
|
||||||
|
|
||||||
|
[](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/redwoodjs)
|
||||||
|
|
||||||
|
_Live Example: https://redwoodjs.now-examples.now.sh_
|
||||||
|
|
||||||
|
### How We Created This Example
|
||||||
|
|
||||||
|
To get started with RedwoodJS on Vercel, you can [use Yarn to initialize](https://redwoodjs.com/tutorial/installation-starting-development) the project:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ yarn create redwood-app ./my-redwood-app
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deploying From Your Terminal
|
||||||
|
|
||||||
|
You can deploy your new RedwoodJS project, along with [Serverless Functions](https://vercel.com/docs/v2/serverless-functions/introduction), with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ vercel
|
||||||
|
```
|
||||||
1
examples/redwoodjs/api/.babelrc.js
Normal file
1
examples/redwoodjs/api/.babelrc.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = { extends: '../babel.config.js' }
|
||||||
9
examples/redwoodjs/api/jsconfig.json
Normal file
9
examples/redwoodjs/api/jsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"src/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"]
|
||||||
|
}
|
||||||
8
examples/redwoodjs/api/package.json
Normal file
8
examples/redwoodjs/api/package.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "api",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@redwoodjs/api": "0.15.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
18
examples/redwoodjs/api/prisma/schema.prisma
Normal file
18
examples/redwoodjs/api/prisma/schema.prisma
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
datasource DS {
|
||||||
|
provider = "sqlite"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
binaryTargets = env("BINARY_TARGET")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define your own datamodels here and run `yarn redwood db save` to create
|
||||||
|
// migrations for them.
|
||||||
|
// TODO: Please remove the following example:
|
||||||
|
model UserExample {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
email String @unique
|
||||||
|
name String?
|
||||||
|
}
|
||||||
26
examples/redwoodjs/api/prisma/seeds.js
Normal file
26
examples/redwoodjs/api/prisma/seeds.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
const { PrismaClient } = require('@prisma/client')
|
||||||
|
const dotenv = require('dotenv')
|
||||||
|
|
||||||
|
dotenv.config()
|
||||||
|
const db = new PrismaClient()
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// Seed data is database data that needs to exist for your app to run.
|
||||||
|
// Ideally this file should be idempotent: running it multiple times
|
||||||
|
// will result in the same database state (usually by checking for the
|
||||||
|
// existence of a record before trying to create it). For example:
|
||||||
|
//
|
||||||
|
// const existing = await db.user.findMany({ where: { email: 'admin@email.com' }})
|
||||||
|
// if (!existing.length) {
|
||||||
|
// await db.user.create({ data: { name: 'Admin', email: 'admin@email.com' }})
|
||||||
|
// }
|
||||||
|
|
||||||
|
console.info('No data to seed. See api/prisma/seeds.js for info.')
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch((e) => console.error(e))
|
||||||
|
.finally(async () => {
|
||||||
|
await db.disconnect()
|
||||||
|
})
|
||||||
19
examples/redwoodjs/api/src/functions/graphql.js
Normal file
19
examples/redwoodjs/api/src/functions/graphql.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import {
|
||||||
|
createGraphQLHandler,
|
||||||
|
makeMergedSchema,
|
||||||
|
makeServices,
|
||||||
|
} from '@redwoodjs/api'
|
||||||
|
import importAll from '@redwoodjs/api/importAll.macro'
|
||||||
|
|
||||||
|
import { db } from 'src/lib/db'
|
||||||
|
|
||||||
|
const schemas = importAll('api', 'graphql')
|
||||||
|
const services = importAll('api', 'services')
|
||||||
|
|
||||||
|
export const handler = createGraphQLHandler({
|
||||||
|
schema: makeMergedSchema({
|
||||||
|
schemas,
|
||||||
|
services: makeServices({ services }),
|
||||||
|
}),
|
||||||
|
db,
|
||||||
|
})
|
||||||
0
packages/now-cli/test/fixtures/unit/simple/lib/woot.jsx → examples/redwoodjs/api/src/graphql/.keep
Executable file → Normal file
0
packages/now-cli/test/fixtures/unit/simple/lib/woot.jsx → examples/redwoodjs/api/src/graphql/.keep
Executable file → Normal file
6
examples/redwoodjs/api/src/lib/db.js
Normal file
6
examples/redwoodjs/api/src/lib/db.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// See https://github.com/prisma/prisma2/blob/master/docs/prisma-client-js/api.md#constructor
|
||||||
|
// for options.
|
||||||
|
|
||||||
|
import { PrismaClient } from '@prisma/client'
|
||||||
|
|
||||||
|
export const db = new PrismaClient()
|
||||||
0
packages/now-cli/test/fixtures/unit/simple/lib/woot → examples/redwoodjs/api/src/services/.keep
Executable file → Normal file
0
packages/now-cli/test/fixtures/unit/simple/lib/woot → examples/redwoodjs/api/src/services/.keep
Executable file → Normal file
3
examples/redwoodjs/babel.config.js
Normal file
3
examples/redwoodjs/babel.config.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: ['@redwoodjs/core/config/babel-preset'],
|
||||||
|
}
|
||||||
19
examples/redwoodjs/package.json
Normal file
19
examples/redwoodjs/package.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"workspaces": {
|
||||||
|
"packages": [
|
||||||
|
"api",
|
||||||
|
"web"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@redwoodjs/core": "0.15.0"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": "@redwoodjs/eslint-config"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12",
|
||||||
|
"yarn": ">=1.15"
|
||||||
|
}
|
||||||
|
}
|
||||||
9
examples/redwoodjs/prettier.config.js
Normal file
9
examples/redwoodjs/prettier.config.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// https://prettier.io/docs/en/options.html
|
||||||
|
module.exports = {
|
||||||
|
trailingComma: 'es5',
|
||||||
|
bracketSpacing: true,
|
||||||
|
tabWidth: 2,
|
||||||
|
semi: false,
|
||||||
|
singleQuote: true,
|
||||||
|
arrowParens: 'always',
|
||||||
|
}
|
||||||
7
examples/redwoodjs/redwood.toml
Normal file
7
examples/redwoodjs/redwood.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[web]
|
||||||
|
port = 8910
|
||||||
|
apiProxyPath = "/api"
|
||||||
|
[api]
|
||||||
|
port = 8911
|
||||||
|
[browser]
|
||||||
|
open = false
|
||||||
1
examples/redwoodjs/web/.babelrc.js
Normal file
1
examples/redwoodjs/web/.babelrc.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = { extends: '../babel.config.js' }
|
||||||
10
examples/redwoodjs/web/jsconfig.json
Normal file
10
examples/redwoodjs/web/jsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"src/*": ["./src/*"]
|
||||||
|
},
|
||||||
|
"jsx": "preserve"
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"]
|
||||||
|
}
|
||||||
15
examples/redwoodjs/web/package.json
Normal file
15
examples/redwoodjs/web/package.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "web",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"browserslist": [
|
||||||
|
"defaults"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@redwoodjs/router": "0.15.0",
|
||||||
|
"@redwoodjs/web": "0.15.0",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react": "^16.13.1",
|
||||||
|
"react-dom": "^16.13.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
36
examples/redwoodjs/web/public/README.md
Normal file
36
examples/redwoodjs/web/public/README.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Static Assets
|
||||||
|
Use this folder to add static files directly to your app. All included files and folders will be copied directly into the `/dist` folder (created when Webpack builds for production). They will also be available during development when you run `yarn rw dev`.
|
||||||
|
>Note: files will *not* hot reload while the development server is running. You'll need to manually stop/start to access file changes.
|
||||||
|
|
||||||
|
### Example Use
|
||||||
|
A file like `favicon.png` will be copied to `/dist/favicon.png`. A folder containing a file such as `static-files/my-logo.jpg` will be copied to `/dist/static-files/my-logo.jpg`. These can be referenced in your code directly without any special handling, e.g.
|
||||||
|
```
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
||||||
|
```
|
||||||
|
and
|
||||||
|
```
|
||||||
|
<img src="/static-files/my-logo.jpg"> alt="Logo" />
|
||||||
|
```
|
||||||
|
|
||||||
|
Behind the scenes, we are using Webpack's ["copy-webpack-plugin"](https://github.com/webpack-contrib/copy-webpack-plugin).
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
Because assets in this folder are bypassing the javascript module system, **this folder should be used sparingly** for assets such as favicons, robots.txt, manifests, libraries incompatible with Webpack, etc.
|
||||||
|
|
||||||
|
In general, it's best to import files directly into a template, page, or component. This allows Webpack to include that file in the bundle, which ensures Webpack will correctly process and move assets into the distribution folder, providing error checks and correct paths along the way.
|
||||||
|
|
||||||
|
### Example Asset Import with Webpack
|
||||||
|
Instead of handling our logo image as a static file per the example above, we can do the following:
|
||||||
|
```
|
||||||
|
import React from "react"
|
||||||
|
import logo from "./my-logo.jpg"
|
||||||
|
|
||||||
|
|
||||||
|
function Header() {
|
||||||
|
return <img src={logo} alt="Logo" />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Header
|
||||||
|
```
|
||||||
|
|
||||||
|
Behind the scenes, we are using Webpack's ["file-loader"](https://webpack.js.org/loaders/file-loader/) and ["url-loader](https://webpack.js.org/loaders/url-loader/) (for files smaller than 10kb).
|
||||||
BIN
examples/redwoodjs/web/public/favicon.png
Normal file
BIN
examples/redwoodjs/web/public/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
2
examples/redwoodjs/web/public/robots.txt
Normal file
2
examples/redwoodjs/web/public/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
22
examples/redwoodjs/web/src/Routes.js
Normal file
22
examples/redwoodjs/web/src/Routes.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// In this file, all Page components from 'src/pages` are auto-imported. Nested
|
||||||
|
// directories are supported, and should be uppercase. Each subdirectory will be
|
||||||
|
// prepended onto the component name.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// 'src/pages/HomePage/HomePage.js' -> HomePage
|
||||||
|
// 'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage
|
||||||
|
|
||||||
|
import { Router, Route } from '@redwoodjs/router'
|
||||||
|
|
||||||
|
const Routes = () => {
|
||||||
|
return (
|
||||||
|
<Router>
|
||||||
|
<Route path="/" page={HomePage} name="home" />
|
||||||
|
<Route path="/about" page={AboutPage} name="about" />
|
||||||
|
<Route notfound page={NotFoundPage} />
|
||||||
|
</Router>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Routes
|
||||||
0
packages/now-cli/test/fixtures/unit/simple/index.js → examples/redwoodjs/web/src/components/.keep
Executable file → Normal file
0
packages/now-cli/test/fixtures/unit/simple/index.js → examples/redwoodjs/web/src/components/.keep
Executable file → Normal file
0
packages/now-cli/test/fixtures/unit/simple/bin/test → examples/redwoodjs/web/src/index.css
Executable file → Normal file
0
packages/now-cli/test/fixtures/unit/simple/bin/test → examples/redwoodjs/web/src/index.css
Executable file → Normal file
12
examples/redwoodjs/web/src/index.html
Normal file
12
examples/redwoodjs/web/src/index.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="redwood-app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
16
examples/redwoodjs/web/src/index.js
Normal file
16
examples/redwoodjs/web/src/index.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import { RedwoodProvider, FatalErrorBoundary } from '@redwoodjs/web'
|
||||||
|
import FatalErrorPage from 'src/pages/FatalErrorPage'
|
||||||
|
|
||||||
|
import Routes from 'src/Routes'
|
||||||
|
|
||||||
|
import './index.css'
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<FatalErrorBoundary page={FatalErrorPage}>
|
||||||
|
<RedwoodProvider>
|
||||||
|
<Routes />
|
||||||
|
</RedwoodProvider>
|
||||||
|
</FatalErrorBoundary>,
|
||||||
|
document.getElementById('redwood-app')
|
||||||
|
)
|
||||||
0
packages/now-cli/test/fixtures/unit/simple-main/index.js → examples/redwoodjs/web/src/layouts/.keep
Executable file → Normal file
0
packages/now-cli/test/fixtures/unit/simple-main/index.js → examples/redwoodjs/web/src/layouts/.keep
Executable file → Normal file
44
examples/redwoodjs/web/src/pages/AboutPage/AboutPage.js
Normal file
44
examples/redwoodjs/web/src/pages/AboutPage/AboutPage.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
export default () => (
|
||||||
|
<main>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
html * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #E2E8F0;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
width: 32rem;
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
color: #2D3748;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<section>
|
||||||
|
<h1>
|
||||||
|
<span>About RedwoodJS</span>
|
||||||
|
</h1>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// This page will be rendered when an error makes it all the way to the top of the
|
||||||
|
// application without being handled by a Javascript catch statement or React error
|
||||||
|
// boundary.
|
||||||
|
//
|
||||||
|
// You can modify this page as you wish, but it is important to keep things simple to
|
||||||
|
// avoid the possibility that it will cause its own error. If it does, Redwood will
|
||||||
|
// still render a generic error page, but your users will prefer something a bit more
|
||||||
|
// thoughtful. =)
|
||||||
|
|
||||||
|
export default () => (
|
||||||
|
<main>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
html * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #E2E8F0;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
width: 32rem;
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
color: #2D3748;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<section>
|
||||||
|
<h1>
|
||||||
|
<span>Something went wrong</span>
|
||||||
|
</h1>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
44
examples/redwoodjs/web/src/pages/HomePage/HomePage.js
Normal file
44
examples/redwoodjs/web/src/pages/HomePage/HomePage.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
export default () => (
|
||||||
|
<main>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
html * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #E2E8F0;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
width: 32rem;
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
color: #2D3748;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<section>
|
||||||
|
<h1>
|
||||||
|
<span>Welcome to RedwoodJS!</span>
|
||||||
|
</h1>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
export default () => (
|
||||||
|
<main>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
html * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #E2E8F0;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
width: 32rem;
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
color: #2D3748;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<section>
|
||||||
|
<h1>
|
||||||
|
<span>404 Page Not Found</span>
|
||||||
|
</h1>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
14339
examples/redwoodjs/yarn.lock
Normal file
14339
examples/redwoodjs/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,17 +2,17 @@
|
|||||||
"name": "svelte-app",
|
"name": "svelte-app",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-commonjs": "^13.0.0",
|
||||||
|
"@rollup/plugin-node-resolve": "^8.1.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"rollup": "^1.10.1",
|
"rollup": "^2.18.0",
|
||||||
"rollup-plugin-commonjs": "^9.3.4",
|
|
||||||
"rollup-plugin-livereload": "^1.0.0",
|
"rollup-plugin-livereload": "^1.0.0",
|
||||||
"rollup-plugin-node-resolve": "^4.2.3",
|
|
||||||
"rollup-plugin-svelte": "^5.0.3",
|
"rollup-plugin-svelte": "^5.0.3",
|
||||||
"rollup-plugin-terser": "^4.0.4",
|
"rollup-plugin-terser": "^6.1.0",
|
||||||
"svelte": "^3.0.0"
|
"svelte": "^3.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"sirv-cli": "^0.4.4"
|
"sirv-cli": "^1.0.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup -c",
|
"build": "rollup -c",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import svelte from 'rollup-plugin-svelte';
|
import svelte from 'rollup-plugin-svelte';
|
||||||
import resolve from 'rollup-plugin-node-resolve';
|
import resolve from '@rollup/plugin-node-resolve';
|
||||||
import commonjs from 'rollup-plugin-commonjs';
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
import livereload from 'rollup-plugin-livereload';
|
import livereload from 'rollup-plugin-livereload';
|
||||||
import { terser } from 'rollup-plugin-terser';
|
import { terser } from 'rollup-plugin-terser';
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
"tagline": "Blitz.js: The Fullstack React Framework",
|
"tagline": "Blitz.js: The Fullstack React Framework",
|
||||||
"description": "A brand new Blitz.js app - the result of running `npx blitz new`.",
|
"description": "A brand new Blitz.js app - the result of running `npx blitz new`.",
|
||||||
"website": "https://blitzjs.com",
|
"website": "https://blitzjs.com",
|
||||||
|
"useRuntime": { "src": "package.json", "use": "@vercel/next" },
|
||||||
"detectors": {
|
"detectors": {
|
||||||
"every": [
|
"every": [
|
||||||
{
|
{
|
||||||
@@ -36,6 +37,7 @@
|
|||||||
"description": "A Next.js app and a Serverless Function API.",
|
"description": "A Next.js app and a Serverless Function API.",
|
||||||
"website": "https://nextjs.org",
|
"website": "https://nextjs.org",
|
||||||
"sort": 1,
|
"sort": 1,
|
||||||
|
"useRuntime": { "src": "package.json", "use": "@vercel/next" },
|
||||||
"detectors": {
|
"detectors": {
|
||||||
"every": [
|
"every": [
|
||||||
{
|
{
|
||||||
@@ -682,6 +684,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "RedwoodJS",
|
||||||
|
"slug": "redwoodjs",
|
||||||
|
"demo": "https://redwoodjs.now-examples.now.sh",
|
||||||
|
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/redwoodjs.svg",
|
||||||
|
"tagline": "RedwoodJS is a full-stack framework for the Jamstack.",
|
||||||
|
"description": "A RedwoodJS app, bootstraped with create-redwood-app.",
|
||||||
|
"website": "https://redwoodjs.com",
|
||||||
|
"useRuntime": { "src": "package.json", "use": "@vercel/redwood" },
|
||||||
|
"ignoreRuntimes": ["@vercel/node"],
|
||||||
|
"detectors": {
|
||||||
|
"every": [
|
||||||
|
{
|
||||||
|
"path": "package.json",
|
||||||
|
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@redwoodjs\\/core\":\\s*\".+?\"[^}]*}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"buildCommand": {
|
||||||
|
"value": "yarn rw build && yarn rw db up --no-db-client --auto-approve && yarn rw dataMigrate up"
|
||||||
|
},
|
||||||
|
"devCommand": {
|
||||||
|
"value": "yarn rw dev --fwd=\"--port=$PORT --open=false\""
|
||||||
|
},
|
||||||
|
"outputDirectory": {
|
||||||
|
"placeholder": "RedwoodJS default"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Hugo",
|
"name": "Hugo",
|
||||||
"slug": "hugo",
|
"slug": "hugo",
|
||||||
@@ -804,7 +836,7 @@
|
|||||||
"description": "No framework or a unoptimized framework.",
|
"description": "No framework or a unoptimized framework.",
|
||||||
"settings": {
|
"settings": {
|
||||||
"buildCommand": {
|
"buildCommand": {
|
||||||
"placeholder": "`npm run now-build` or `npm run build`"
|
"placeholder": "`npm run vercel-build` or `npm run build`"
|
||||||
},
|
},
|
||||||
"devCommand": {
|
"devCommand": {
|
||||||
"placeholder": "None"
|
"placeholder": "None"
|
||||||
|
|||||||
2
packages/frameworks/index.d.ts
vendored
2
packages/frameworks/index.d.ts
vendored
@@ -20,6 +20,8 @@ export interface Framework {
|
|||||||
website?: string;
|
website?: string;
|
||||||
description: string;
|
description: string;
|
||||||
sort?: number;
|
sort?: number;
|
||||||
|
useRuntime?: { src: string; use: string };
|
||||||
|
ignoreRuntimes?: string[];
|
||||||
detectors?: {
|
detectors?: {
|
||||||
every?: FrameworkDetectionItem[];
|
every?: FrameworkDetectionItem[];
|
||||||
some?: FrameworkDetectionItem[];
|
some?: FrameworkDetectionItem[];
|
||||||
|
|||||||
1
packages/frameworks/logos/redwoodjs.svg
Normal file
1
packages/frameworks/logos/redwoodjs.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg fill="none" width="48" height="48" viewBox="0 0 917 1000" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="m249.557 144.582 194.171 132.54c4.383 2.918 9.502 4.516 14.755 4.606 5.261-.038 10.394-1.641 14.755-4.606l194.319-132.986c7.55-5.406 11.714-14.418 10.957-23.717-.757-9.298-6.322-17.507-14.646-21.6024l-194.171-96.13614c-7.366-3.573948-15.947-3.573948-23.313 0l-193.581 96.13614c-8.474 4.1174-14.113 12.4854-14.783 21.9354-.67 9.451 3.73 18.541 11.537 23.83zm274.879 174.144c.016 8.789 4.318 17.01 11.509 21.991l155.662 106.389c9.965 6.87 23.298 6.012 32.313-2.081l130.579-116.789c5.819-5.199 9.051-12.729 8.823-20.56s-3.892-15.158-10.004-20.005l-124.677-99.702c-9.062-7.199-21.704-7.68-31.28-1.189l-161.416 110.401c-7.064 4.89-11.35 12.914-11.509 21.545zm-387.163 144.724c6.292 5.652 9.526 13.988 8.706 22.437-.817 8.499-5.726 16.052-13.132 20.208l-92.9545 55.72c-9.4227 5.633-21.32 4.82-29.90183-2.041-8.5818-6.861-12.06543-18.346-8.75546-28.865l34.37839-108.172c2.6969-8.57 9.5328-15.175 18.1483-17.533 8.609-2.505 17.8924-.309 24.4928 5.795zm504.168 11.293-168.056-115.007c-8.931-6.01-20.578-6.01-29.509 0l-168.056 115.007c-6.684 4.626-10.919 12.061-11.509 20.208-.435 8.203 2.816 16.169 8.853 21.693l167.909 150.222c4.842 4.319 11.089 6.698 17.558 6.687 6.465-.002 12.708-2.38 17.558-6.687l167.908-150.222c6.056-5.501 9.265-13.5 8.705-21.693-.469-8.146-4.666-15.612-11.361-20.208zm-448.247-29.718-130.4316-116.79c-5.8687-5.331-9.1073-12.995-8.8528-20.95.1419-7.841 3.7705-15.204 9.8856-20.06l124.6768-100.296c9.126-7.179 21.793-7.658 31.428-1.189l161.269 110.401c7.484 4.908 11.998 13.293 11.998 22.288 0 8.994-4.514 17.38-11.998 22.288l-155.515 106.388c-10.025 6.841-23.376 5.985-32.46-2.08zm669.715 167.756-132.792-79.495c-9.862-5.943-22.415-4.739-30.985 2.972l-162.301 144.873c-6.846 6.114-10.062 15.362-8.499 24.441 1.563 9.08 7.681 16.698 16.171 20.135l225.157 91.233c3.088 1.283 6.397 1.939 9.738 1.932 10.449.033 19.936-6.142 24.197-15.751l69.79-156.314c5.68-12.37 1.157-27.062-10.476-34.026zm18.443-190.043 34.379 108.171h-.295c2.542 8.091 1.097 16.919-3.889 23.761-4.986 6.841-12.915 10.876-21.342 10.86-4.728.016-9.37-1.269-13.427-3.715l-93.102-55.72c-7.254-4.243-11.992-11.789-12.689-20.208-.87-8.456 2.373-16.814 8.705-22.436l59.019-52.6c6.668-5.976 15.881-8.156 24.493-5.795 8.609 2.459 15.423 9.098 18.148 17.682zm-492.511 282.761c1.587-9.042-1.597-18.266-8.41-24.368l-162.302-144.873c-8.57-7.711-21.123-8.915-30.985-2.972l-132.7921 79.495c-11.4977 6.995-16.0467 21.502-10.6233 33.878l69.9374 156.314c5.794 13.034 20.774 19.134 33.936 13.818l225.009-91.232c8.492-3.407 14.632-10.995 16.23-20.06zm79.675 44.577 180.598 73.105c8.83 3.779 14.93 12.084 15.935 21.694 1.143 9.729-3.178 19.291-11.214 24.814l-180.745 125.556c-4.331 3.043-9.473 4.7-14.754 4.755-5.277-.082-10.411-1.737-14.755-4.755l-180.597-125.556c-8.066-5.508-12.439-15.061-11.362-24.814 1.206-9.71 7.526-18.006 16.526-21.694l180.597-73.105c6.351-2.532 13.421-2.532 19.771 0z" fill="#bf4722" fill-rule="evenodd"/></svg>
|
||||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/frameworks",
|
"name": "@vercel/frameworks",
|
||||||
"version": "0.0.17-canary.2",
|
"version": "0.1.0",
|
||||||
"main": "frameworks.json",
|
"main": "frameworks.json",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
15
packages/frameworks/test/frameworks.unit.test.ts
vendored
15
packages/frameworks/test/frameworks.unit.test.ts
vendored
@@ -64,6 +64,21 @@ const Schema = {
|
|||||||
tagline: { type: 'string' },
|
tagline: { type: 'string' },
|
||||||
website: { type: 'string' },
|
website: { type: 'string' },
|
||||||
description: { type: 'string' },
|
description: { type: 'string' },
|
||||||
|
useRuntime: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['src', 'use'],
|
||||||
|
additionalProperties: false,
|
||||||
|
properties: {
|
||||||
|
src: { type: 'string' },
|
||||||
|
use: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ignoreRuntimes: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
detectors: {
|
detectors: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vercel/build-utils",
|
"name": "@vercel/build-utils",
|
||||||
"version": "2.4.2-canary.1",
|
"version": "2.5.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.js",
|
"types": "./dist/index.d.js",
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
"@types/node-fetch": "^2.1.6",
|
"@types/node-fetch": "^2.1.6",
|
||||||
"@types/semver": "6.0.0",
|
"@types/semver": "6.0.0",
|
||||||
"@types/yazl": "^2.4.1",
|
"@types/yazl": "^2.4.1",
|
||||||
|
"@vercel/frameworks": "0.1.0",
|
||||||
"aggregate-error": "3.0.1",
|
"aggregate-error": "3.0.1",
|
||||||
"async-retry": "1.2.3",
|
"async-retry": "1.2.3",
|
||||||
"async-sema": "2.1.4",
|
"async-sema": "2.1.4",
|
||||||
|
|||||||
@@ -2,8 +2,13 @@ import minimatch from 'minimatch';
|
|||||||
import { valid as validSemver } from 'semver';
|
import { valid as validSemver } from 'semver';
|
||||||
import { parse as parsePath, extname } from 'path';
|
import { parse as parsePath, extname } from 'path';
|
||||||
import { Route, Source } from '@vercel/routing-utils';
|
import { Route, Source } from '@vercel/routing-utils';
|
||||||
|
import _frameworks, { Framework } from '@vercel/frameworks';
|
||||||
import { PackageJson, Builder, Config, BuilderFunctions } from './types';
|
import { PackageJson, Builder, Config, BuilderFunctions } from './types';
|
||||||
import { isOfficialRuntime } from './';
|
import { isOfficialRuntime } from './';
|
||||||
|
const frameworkList = _frameworks as Framework[];
|
||||||
|
const slugToFramework = new Map<string | null, Framework>(
|
||||||
|
frameworkList.map(f => [f.slug, f])
|
||||||
|
);
|
||||||
|
|
||||||
interface ErrorResponse {
|
interface ErrorResponse {
|
||||||
code: string;
|
code: string;
|
||||||
@@ -21,6 +26,7 @@ interface Options {
|
|||||||
devCommand?: string | null;
|
devCommand?: string | null;
|
||||||
buildCommand?: string | null;
|
buildCommand?: string | null;
|
||||||
outputDirectory?: string | null;
|
outputDirectory?: string | null;
|
||||||
|
createdAt?: number;
|
||||||
};
|
};
|
||||||
cleanUrls?: boolean;
|
cleanUrls?: boolean;
|
||||||
trailingSlash?: boolean;
|
trailingSlash?: boolean;
|
||||||
@@ -106,7 +112,6 @@ export async function detectBuilders(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiMatches = getApiMatches(options);
|
|
||||||
const sortedFiles = files.sort(sortFiles);
|
const sortedFiles = files.sort(sortFiles);
|
||||||
const apiSortedFiles = files.sort(sortFilesBySegmentCount);
|
const apiSortedFiles = files.sort(sortFilesBySegmentCount);
|
||||||
|
|
||||||
@@ -122,6 +127,16 @@ export async function detectBuilders(
|
|||||||
|
|
||||||
const { projectSettings = {} } = options;
|
const { projectSettings = {} } = options;
|
||||||
const { buildCommand, outputDirectory, framework } = projectSettings;
|
const { buildCommand, outputDirectory, framework } = projectSettings;
|
||||||
|
const ignoreRuntimes = new Set(
|
||||||
|
slugToFramework.get(framework || '')?.ignoreRuntimes
|
||||||
|
);
|
||||||
|
const withTag = options.tag ? `@${options.tag}` : '';
|
||||||
|
const apiMatches = getApiMatches()
|
||||||
|
.filter(b => !ignoreRuntimes.has(b.use))
|
||||||
|
.map(b => {
|
||||||
|
b.use = `${b.use}${withTag}`;
|
||||||
|
return b;
|
||||||
|
});
|
||||||
|
|
||||||
// If either is missing we'll make the frontend static
|
// If either is missing we'll make the frontend static
|
||||||
const makeFrontendStatic = buildCommand === '' || outputDirectory === '';
|
const makeFrontendStatic = buildCommand === '' || outputDirectory === '';
|
||||||
@@ -394,16 +409,15 @@ function getFunction(fileName: string, { functions = {} }: Options) {
|
|||||||
: { fnPattern: null, func: null };
|
: { fnPattern: null, func: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
function getApiMatches({ tag }: Options = {}) {
|
function getApiMatches() {
|
||||||
const withTag = tag ? `@${tag}` : '';
|
|
||||||
const config = { zeroConfig: true };
|
const config = { zeroConfig: true };
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{ src: 'api/**/*.js', use: `@vercel/node${withTag}`, config },
|
{ src: 'api/**/*.js', use: `@vercel/node`, config },
|
||||||
{ src: 'api/**/*.ts', use: `@vercel/node${withTag}`, config },
|
{ src: 'api/**/*.ts', use: `@vercel/node`, config },
|
||||||
{ src: 'api/**/!(*_test).go', use: `@vercel/go${withTag}`, config },
|
{ src: 'api/**/!(*_test).go', use: `@vercel/go`, config },
|
||||||
{ src: 'api/**/*.py', use: `@vercel/python${withTag}`, config },
|
{ src: 'api/**/*.py', use: `@vercel/python`, config },
|
||||||
{ src: 'api/**/*.rb', use: `@vercel/ruby${withTag}`, config },
|
{ src: 'api/**/*.rb', use: `@vercel/ruby`, config },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,6 +435,7 @@ function detectFrontBuilder(
|
|||||||
): Builder {
|
): Builder {
|
||||||
const { tag, projectSettings = {} } = options;
|
const { tag, projectSettings = {} } = options;
|
||||||
const withTag = tag ? `@${tag}` : '';
|
const withTag = tag ? `@${tag}` : '';
|
||||||
|
const { createdAt = 0 } = projectSettings;
|
||||||
let { framework } = projectSettings;
|
let { framework } = projectSettings;
|
||||||
|
|
||||||
const config: Config = {
|
const config: Config = {
|
||||||
@@ -443,7 +458,7 @@ function detectFrontBuilder(
|
|||||||
config.outputDirectory = projectSettings.outputDirectory;
|
config.outputDirectory = projectSettings.outputDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pkg) {
|
if (pkg && (framework !== null || createdAt < Date.parse('2020-03-01'))) {
|
||||||
const deps: PackageJson['dependencies'] = {
|
const deps: PackageJson['dependencies'] = {
|
||||||
...pkg.dependencies,
|
...pkg.dependencies,
|
||||||
...pkg.devDependencies,
|
...pkg.devDependencies,
|
||||||
@@ -464,8 +479,10 @@ function detectFrontBuilder(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (framework === 'nextjs' || framework === 'blitzjs') {
|
const f = slugToFramework.get(framework || '');
|
||||||
return { src: 'package.json', use: `@vercel/next${withTag}`, config };
|
if (f && f.useRuntime) {
|
||||||
|
const { src, use } = f.useRuntime;
|
||||||
|
return { src, use: `${use}${withTag}`, config };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entrypoints for other frameworks
|
// Entrypoints for other frameworks
|
||||||
@@ -914,11 +931,10 @@ function getRouteResult(
|
|||||||
const redirectRoutes: Route[] = [];
|
const redirectRoutes: Route[] = [];
|
||||||
const rewriteRoutes: Route[] = [];
|
const rewriteRoutes: Route[] = [];
|
||||||
const errorRoutes: Route[] = [];
|
const errorRoutes: Route[] = [];
|
||||||
const isNextjs =
|
const framework = frontendBuilder?.config?.framework || '';
|
||||||
frontendBuilder &&
|
const use = frontendBuilder?.use || '';
|
||||||
((frontendBuilder.use && frontendBuilder.use.startsWith('@vercel/next')) ||
|
const isNextjs = framework === 'nextjs' || use.startsWith('@vercel/next');
|
||||||
(frontendBuilder.config &&
|
const ignoreRuntimes = slugToFramework.get(framework)?.ignoreRuntimes;
|
||||||
frontendBuilder.config.framework === 'nextjs'));
|
|
||||||
|
|
||||||
if (apiRoutes && apiRoutes.length > 0) {
|
if (apiRoutes && apiRoutes.length > 0) {
|
||||||
if (options.featHandleMiss) {
|
if (options.featHandleMiss) {
|
||||||
@@ -956,11 +972,17 @@ function getRouteResult(
|
|||||||
}
|
}
|
||||||
|
|
||||||
rewriteRoutes.push(...dynamicRoutes);
|
rewriteRoutes.push(...dynamicRoutes);
|
||||||
rewriteRoutes.push({
|
|
||||||
src: '^/api(/.*)?$',
|
if (typeof ignoreRuntimes === 'undefined') {
|
||||||
status: 404,
|
// This route is only necessary to hide the directory listing
|
||||||
continue: true,
|
// to avoid enumerating serverless function names.
|
||||||
});
|
// But it causes issues in `vc dev` for frameworks that handle
|
||||||
|
// their own functions such as redwood, so we ignore.
|
||||||
|
errorRoutes.push({
|
||||||
|
status: 404,
|
||||||
|
src: '^/api(/.*)?$',
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
defaultRoutes.push(...apiRoutes);
|
defaultRoutes.push(...apiRoutes);
|
||||||
|
|
||||||
@@ -1030,5 +1052,5 @@ function sortFilesBySegmentCount(fileA: string, fileB: string): number {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return fileA.localeCompare(fileB);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ async function readFileOrNull(file: string) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function readConfigFile<T>(files: string | string[]) {
|
export async function readConfigFile<T>(
|
||||||
|
files: string | string[]
|
||||||
|
): Promise<T | null> {
|
||||||
files = Array.isArray(files) ? files : [files];
|
files = Array.isArray(files) ? files : [files];
|
||||||
|
|
||||||
for (const name of files) {
|
for (const name of files) {
|
||||||
@@ -24,11 +26,11 @@ export async function readConfigFile<T>(files: string | string[]) {
|
|||||||
if (data) {
|
if (data) {
|
||||||
const str = data.toString('utf8');
|
const str = data.toString('utf8');
|
||||||
if (name.endsWith('.json')) {
|
if (name.endsWith('.json')) {
|
||||||
return JSON.parse(str);
|
return JSON.parse(str) as T;
|
||||||
} else if (name.endsWith('.toml')) {
|
} else if (name.endsWith('.toml')) {
|
||||||
return (toml.parse(str) as unknown) as T;
|
return (toml.parse(str) as unknown) as T;
|
||||||
} else if (name.endsWith('.yaml') || name.endsWith('.yml')) {
|
} else if (name.endsWith('.yaml') || name.endsWith('.yml')) {
|
||||||
return yaml.safeLoad(str, { filename: name });
|
return yaml.safeLoad(str, { filename: name }) as T;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@
|
|||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"types": ["node", "jest"],
|
"types": ["node", "jest"],
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"target": "esnext"
|
"target": "es2019"
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
declare module 'which-promise' {
|
|
||||||
export default function (name: string): Promise<string>;
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vercel",
|
"name": "vercel",
|
||||||
"version": "19.1.3-canary.4",
|
"version": "20.0.0",
|
||||||
"preferGlobal": true,
|
"preferGlobal": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"description": "The command-line interface for Vercel",
|
"description": "The command-line interface for Vercel",
|
||||||
@@ -14,7 +14,6 @@
|
|||||||
"preinstall": "node ./scripts/preinstall.js",
|
"preinstall": "node ./scripts/preinstall.js",
|
||||||
"test-unit": "nyc ava test/unit.js test/dev-builder.unit.js test/dev-router.unit.js test/dev-server.unit.js test/dev-validate.unit.js --serial --fail-fast --verbose",
|
"test-unit": "nyc ava test/unit.js test/dev-builder.unit.js test/dev-router.unit.js test/dev-server.unit.js test/dev-validate.unit.js --serial --fail-fast --verbose",
|
||||||
"test-integration-cli": "ava test/integration.js --serial --fail-fast --verbose",
|
"test-integration-cli": "ava test/integration.js --serial --fail-fast --verbose",
|
||||||
"test-integration-v1": "ava test/integration-v1.js --serial --fail-fast",
|
|
||||||
"test-integration-dev": "ava test/dev/integration.js --serial --fail-fast --verbose",
|
"test-integration-dev": "ava test/dev/integration.js --serial --fail-fast --verbose",
|
||||||
"prepublishOnly": "yarn build",
|
"prepublishOnly": "yarn build",
|
||||||
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
||||||
@@ -62,13 +61,12 @@
|
|||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/build-utils": "2.4.2-canary.1",
|
"@vercel/build-utils": "2.5.0",
|
||||||
"@vercel/go": "1.1.4-canary.0",
|
"@vercel/go": "1.1.5",
|
||||||
"@vercel/next": "2.6.13-canary.1",
|
"@vercel/node": "1.8.0",
|
||||||
"@vercel/node": "1.7.3-canary.0",
|
|
||||||
"@vercel/python": "1.2.2",
|
"@vercel/python": "1.2.2",
|
||||||
"@vercel/ruby": "1.2.3",
|
"@vercel/ruby": "1.2.3",
|
||||||
"@vercel/static-build": "0.17.6-canary.1"
|
"update-notifier": "4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sentry/node": "5.5.0",
|
"@sentry/node": "5.5.0",
|
||||||
@@ -102,7 +100,7 @@
|
|||||||
"@types/universal-analytics": "0.4.2",
|
"@types/universal-analytics": "0.4.2",
|
||||||
"@types/which": "1.3.2",
|
"@types/which": "1.3.2",
|
||||||
"@types/write-json-file": "2.2.1",
|
"@types/write-json-file": "2.2.1",
|
||||||
"@zeit/dockerignore": "0.0.5",
|
"@vercel/frameworks": "0.1.0",
|
||||||
"@zeit/fun": "0.11.2",
|
"@zeit/fun": "0.11.2",
|
||||||
"@zeit/ncc": "0.18.5",
|
"@zeit/ncc": "0.18.5",
|
||||||
"@zeit/source-map-support": "0.6.2",
|
"@zeit/source-map-support": "0.6.2",
|
||||||
@@ -119,17 +117,13 @@
|
|||||||
"chalk": "2.4.2",
|
"chalk": "2.4.2",
|
||||||
"chokidar": "3.3.1",
|
"chokidar": "3.3.1",
|
||||||
"clipboardy": "2.1.0",
|
"clipboardy": "2.1.0",
|
||||||
"codecov": "3.6.5",
|
"codecov": "3.7.1",
|
||||||
"cpy": "7.2.0",
|
"cpy": "7.2.0",
|
||||||
"credit-card": "3.0.1",
|
"credit-card": "3.0.1",
|
||||||
"date-fns": "1.29.0",
|
"date-fns": "1.29.0",
|
||||||
"death": "1.1.0",
|
|
||||||
"debug": "3.1.0",
|
"debug": "3.1.0",
|
||||||
"deployment-type": "1.0.1",
|
|
||||||
"docker-file-parser": "1.0.2",
|
|
||||||
"dot": "1.1.3",
|
"dot": "1.1.3",
|
||||||
"dotenv": "4.0.0",
|
"dotenv": "4.0.0",
|
||||||
"download": "6.2.5",
|
|
||||||
"email-prompt": "0.3.2",
|
"email-prompt": "0.3.2",
|
||||||
"email-validator": "1.1.1",
|
"email-validator": "1.1.1",
|
||||||
"epipebomb": "1.0.0",
|
"epipebomb": "1.0.0",
|
||||||
@@ -141,8 +135,6 @@
|
|||||||
"get-port": "5.1.1",
|
"get-port": "5.1.1",
|
||||||
"glob": "7.1.2",
|
"glob": "7.1.2",
|
||||||
"http-proxy": "1.17.0",
|
"http-proxy": "1.17.0",
|
||||||
"ignore": "4.0.6",
|
|
||||||
"ini": "1.3.4",
|
|
||||||
"inquirer": "7.0.4",
|
"inquirer": "7.0.4",
|
||||||
"is-port-reachable": "3.0.0",
|
"is-port-reachable": "3.0.0",
|
||||||
"is-url": "1.2.2",
|
"is-url": "1.2.2",
|
||||||
@@ -161,14 +153,11 @@
|
|||||||
"ora": "3.4.0",
|
"ora": "3.4.0",
|
||||||
"pcre-to-regexp": "1.0.0",
|
"pcre-to-regexp": "1.0.0",
|
||||||
"pluralize": "7.0.0",
|
"pluralize": "7.0.0",
|
||||||
"printf": "0.2.5",
|
|
||||||
"progress": "2.0.3",
|
"progress": "2.0.3",
|
||||||
"promisepipe": "3.0.0",
|
"promisepipe": "3.0.0",
|
||||||
"psl": "1.1.31",
|
"psl": "1.1.31",
|
||||||
"qr-image": "3.2.0",
|
"qr-image": "3.2.0",
|
||||||
"raw-body": "2.4.1",
|
"raw-body": "2.4.1",
|
||||||
"read-pkg": "2.0.0",
|
|
||||||
"rx-lite-aggregates": "4.0.8",
|
|
||||||
"semver": "5.5.0",
|
"semver": "5.5.0",
|
||||||
"serve-handler": "6.1.1",
|
"serve-handler": "6.1.1",
|
||||||
"sinon": "4.4.2",
|
"sinon": "4.4.2",
|
||||||
@@ -177,18 +166,14 @@
|
|||||||
"tar-fs": "1.16.3",
|
"tar-fs": "1.16.3",
|
||||||
"test-listen": "1.1.0",
|
"test-listen": "1.1.0",
|
||||||
"text-table": "0.2.0",
|
"text-table": "0.2.0",
|
||||||
"then-sleep": "1.0.1",
|
|
||||||
"through2": "2.0.3",
|
|
||||||
"title": "3.4.1",
|
"title": "3.4.1",
|
||||||
"tmp-promise": "1.0.3",
|
"tmp-promise": "1.0.3",
|
||||||
"tree-kill": "1.2.1",
|
"tree-kill": "1.2.1",
|
||||||
"ts-node": "8.3.0",
|
"ts-node": "8.3.0",
|
||||||
"typescript": "3.9.3",
|
"typescript": "3.9.3",
|
||||||
"universal-analytics": "0.4.20",
|
"universal-analytics": "0.4.20",
|
||||||
"update-check": "1.5.3",
|
|
||||||
"utility-types": "2.1.0",
|
"utility-types": "2.1.0",
|
||||||
"which": "2.0.2",
|
"which": "2.0.2",
|
||||||
"which-promise": "1.0.0",
|
|
||||||
"write-json-file": "2.2.0",
|
"write-json-file": "2.2.0",
|
||||||
"xdg-app-paths": "5.1.0"
|
"xdg-app-paths": "5.1.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,13 @@ async function main() {
|
|||||||
// Do the initial `ncc` build
|
// Do the initial `ncc` build
|
||||||
console.log();
|
console.log();
|
||||||
const src = join(dirRoot, 'src');
|
const src = join(dirRoot, 'src');
|
||||||
const args = ['@zeit/ncc', 'build', '--source-map'];
|
const args = [
|
||||||
|
'@zeit/ncc',
|
||||||
|
'build',
|
||||||
|
'--source-map',
|
||||||
|
'--external',
|
||||||
|
'update-notifier',
|
||||||
|
];
|
||||||
if (!isDev) {
|
if (!isDev) {
|
||||||
args.push('--minify');
|
args.push('--minify');
|
||||||
}
|
}
|
||||||
@@ -86,7 +92,7 @@ async function main() {
|
|||||||
// A bunch of source `.ts` files from CLI's `util` directory
|
// A bunch of source `.ts` files from CLI's `util` directory
|
||||||
await remove(join(dirRoot, 'dist', 'util'));
|
await remove(join(dirRoot, 'dist', 'util'));
|
||||||
|
|
||||||
console.log('Finished building `now-cli`');
|
console.log('Finished building Vercel CLI');
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on('unhandledRejection', (reason: any, promise: Promise<any>) => {
|
process.on('unhandledRejection', (reason: any, promise: Promise<any>) => {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
//
|
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
|
||||||
import { handleError } from '../../util/error';
|
import { handleError } from '../../util/error';
|
||||||
@@ -32,15 +31,11 @@ const help = () => {
|
|||||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||||
'DIR'
|
'DIR'
|
||||||
)} Path to the global ${'`.vercel`'} directory
|
)} Path to the global ${'`.vercel`'} directory
|
||||||
-r ${chalk.bold.underline('RULES_FILE')}, --rules=${chalk.bold.underline(
|
|
||||||
'RULES_FILE'
|
|
||||||
)} Rules file
|
|
||||||
-d, --debug Debug mode [off]
|
-d, --debug Debug mode [off]
|
||||||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
||||||
'TOKEN'
|
'TOKEN'
|
||||||
)} Login token
|
)} Login token
|
||||||
-S, --scope Set a custom scope
|
-S, --scope Set a custom scope
|
||||||
-n, --no-verify Don't wait until instance count meets the previous alias constraints
|
|
||||||
-N, --next Show next page of results
|
-N, --next Show next page of results
|
||||||
${chalk.dim('Examples:')}
|
${chalk.dim('Examples:')}
|
||||||
|
|
||||||
@@ -66,30 +61,6 @@ const help = () => {
|
|||||||
${chalk.dim('–')} ${chalk.dim(
|
${chalk.dim('–')} ${chalk.dim(
|
||||||
'Protocols'
|
'Protocols'
|
||||||
)} in the URLs are unneeded and ignored.
|
)} in the URLs are unneeded and ignored.
|
||||||
|
|
||||||
${chalk.gray('–')} Add and modify path based aliases for ${chalk.underline(
|
|
||||||
'example.com'
|
|
||||||
)}
|
|
||||||
|
|
||||||
${chalk.cyan(
|
|
||||||
`$ ${getPkgName()} alias ${chalk.underline(
|
|
||||||
'example.com'
|
|
||||||
)} -r ${chalk.underline('rules.json')}`
|
|
||||||
)}
|
|
||||||
|
|
||||||
Export effective routing rules
|
|
||||||
|
|
||||||
${chalk.cyan(
|
|
||||||
`$ ${getPkgName()} alias ls aliasId --json > ${chalk.underline(
|
|
||||||
'rules.json'
|
|
||||||
)}`
|
|
||||||
)}
|
|
||||||
|
|
||||||
${chalk.gray('–')} Paginate results, where ${chalk.dim(
|
|
||||||
'`1584722256178`'
|
|
||||||
)} is the time in milliseconds since the UNIX epoch.
|
|
||||||
|
|
||||||
${chalk.cyan(`$ ${getPkgName()} alias ls --next 1584722256178`)}
|
|
||||||
`);
|
`);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -106,12 +77,8 @@ export default async function main(ctx) {
|
|||||||
try {
|
try {
|
||||||
argv = getArgs(ctx.argv.slice(2), {
|
argv = getArgs(ctx.argv.slice(2), {
|
||||||
'--json': Boolean,
|
'--json': Boolean,
|
||||||
'--no-verify': Boolean,
|
|
||||||
'--rules': String,
|
|
||||||
'--yes': Boolean,
|
'--yes': Boolean,
|
||||||
'--next': Number,
|
'--next': Number,
|
||||||
'-n': '--no-verify',
|
|
||||||
'-r': '--rules',
|
|
||||||
'-y': '--yes',
|
'-y': '--yes',
|
||||||
'-N': '--next',
|
'-N': '--next',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import ms from 'ms';
|
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { SetDifference } from 'utility-types';
|
import { SetDifference } from 'utility-types';
|
||||||
import { AliasRecord } from '../../util/alias/create-alias';
|
import { AliasRecord } from '../../util/alias/create-alias';
|
||||||
@@ -7,28 +6,24 @@ import { Output } from '../../util/output';
|
|||||||
import * as ERRORS from '../../util/errors-ts';
|
import * as ERRORS from '../../util/errors-ts';
|
||||||
import assignAlias from '../../util/alias/assign-alias';
|
import assignAlias from '../../util/alias/assign-alias';
|
||||||
import Client from '../../util/client';
|
import Client from '../../util/client';
|
||||||
import formatDnsTable from '../../util/format-dns-table';
|
|
||||||
import formatNSTable from '../../util/format-ns-table';
|
import formatNSTable from '../../util/format-ns-table';
|
||||||
import getDeploymentForAlias from '../../util/alias/get-deployment-for-alias';
|
import getDeploymentByIdOrHost from '../../util/deploy/get-deployment-by-id-or-host';
|
||||||
import getRulesFromFile from '../../util/alias/get-rules-from-file';
|
import { getDeploymentForAlias } from '../../util/alias/get-deployment-by-alias';
|
||||||
import getScope from '../../util/get-scope';
|
import getScope from '../../util/get-scope';
|
||||||
import { getTargetsForAlias } from '../../util/alias/get-targets-for-alias';
|
|
||||||
import humanizePath from '../../util/humanize-path';
|
|
||||||
import setupDomain from '../../util/domains/setup-domain';
|
import setupDomain from '../../util/domains/setup-domain';
|
||||||
import stamp from '../../util/output/stamp';
|
import stamp from '../../util/output/stamp';
|
||||||
import { isValidName } from '../../util/is-valid-name';
|
import { isValidName } from '../../util/is-valid-name';
|
||||||
import upsertPathAlias from '../../util/alias/upsert-path-alias';
|
|
||||||
import handleCertError from '../../util/certs/handle-cert-error';
|
import handleCertError from '../../util/certs/handle-cert-error';
|
||||||
import isWildcardAlias from '../../util/alias/is-wildcard-alias';
|
import isWildcardAlias from '../../util/alias/is-wildcard-alias';
|
||||||
import link from '../../util/output/link';
|
import link from '../../util/output/link';
|
||||||
import { User } from '../../types';
|
import { User } from '../../types';
|
||||||
import { getCommandName } from '../../util/pkg-name';
|
import { getCommandName } from '../../util/pkg-name';
|
||||||
|
import toHost from '../../util/to-host';
|
||||||
|
import { NowConfig } from '../../util/dev/types';
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
'--debug': boolean;
|
'--debug': boolean;
|
||||||
'--local-config': string;
|
'--local-config': string;
|
||||||
'--no-verify': boolean;
|
|
||||||
'--rules': string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function set(
|
export default async function set(
|
||||||
@@ -47,11 +42,7 @@ export default async function set(
|
|||||||
const { apiUrl } = ctx;
|
const { apiUrl } = ctx;
|
||||||
const setStamp = stamp();
|
const setStamp = stamp();
|
||||||
|
|
||||||
const {
|
const { '--debug': debugEnabled } = opts;
|
||||||
'--debug': debugEnabled,
|
|
||||||
'--no-verify': noVerify,
|
|
||||||
'--rules': rulesPath,
|
|
||||||
} = opts;
|
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
apiUrl,
|
apiUrl,
|
||||||
@@ -96,35 +87,7 @@ export default async function set(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the path alias rules in case there is is given
|
if (args.length === 0) {
|
||||||
const rules = await getRulesFromFile(rulesPath);
|
|
||||||
if (rules instanceof ERRORS.FileNotFound) {
|
|
||||||
output.error(`Can't find the provided rules file at location:`);
|
|
||||||
output.print(` ${chalk.gray('-')} ${rules.meta.file}\n`);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rules instanceof ERRORS.CantParseJSONFile) {
|
|
||||||
output.error(`Error parsing provided rules.json file at location:`);
|
|
||||||
output.print(` ${chalk.gray('-')} ${rules.meta.file}\n`);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rules instanceof ERRORS.RulesFileValidationError) {
|
|
||||||
output.error(`Path Alias validation error: ${rules.meta.message}`);
|
|
||||||
output.print(` ${chalk.gray('-')} ${rules.meta.location}\n`);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user provided rules and also a deployment target, we should fail
|
|
||||||
if (args.length === 2 && rules) {
|
|
||||||
output.error(
|
|
||||||
`You can't supply a deployment target and target rules simultaneously.`
|
|
||||||
);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.length === 0 && !rules) {
|
|
||||||
output.error(
|
output.error(
|
||||||
`To ship to production, optionally configure your domains (${link(
|
`To ship to production, optionally configure your domains (${link(
|
||||||
'https://vercel.com/docs/v2/custom-domains'
|
'https://vercel.com/docs/v2/custom-domains'
|
||||||
@@ -133,62 +96,79 @@ export default async function set(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the targets to perform the alias
|
// For `now alias set <argument>`
|
||||||
const targets = getTargetsForAlias(args, localConfig);
|
if (args.length === 1) {
|
||||||
|
const deployment = handleCertError(
|
||||||
if (targets instanceof ERRORS.NoAliasInConfig) {
|
output,
|
||||||
output.error(`Couldn't find an alias in config`);
|
await getDeploymentForAlias(
|
||||||
return 1;
|
client,
|
||||||
}
|
output,
|
||||||
|
args,
|
||||||
if (targets instanceof ERRORS.InvalidAliasInConfig) {
|
opts['--local-config'],
|
||||||
output.error(
|
user,
|
||||||
`Wrong value for alias found in config. It must be a string or array of string.`
|
contextName,
|
||||||
|
localConfig
|
||||||
|
)
|
||||||
);
|
);
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rules) {
|
if (deployment === 1) {
|
||||||
// If we have rules for path alias we assign them to the domain
|
return deployment;
|
||||||
for (const target of targets) {
|
}
|
||||||
output.log(
|
|
||||||
`Assigning path alias rules from ${humanizePath(
|
if (deployment instanceof Error) {
|
||||||
rulesPath
|
output.error(deployment.message);
|
||||||
)} to ${target}`
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deployment) {
|
||||||
|
output.error(
|
||||||
|
`Couldn't find a deployment to alias. Please provide one as an argument.`
|
||||||
);
|
);
|
||||||
const pathAlias = await upsertPathAlias(
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the targets to perform the alias
|
||||||
|
const targets = getTargetsForAlias(args, localConfig);
|
||||||
|
|
||||||
|
if (targets instanceof Error) {
|
||||||
|
output.prettyError(targets);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const target of targets) {
|
||||||
|
output.log(`Assigning alias ${target} to deployment ${deployment.url}`);
|
||||||
|
|
||||||
|
const record = await assignAlias(
|
||||||
output,
|
output,
|
||||||
client,
|
client,
|
||||||
rules,
|
deployment,
|
||||||
target,
|
target,
|
||||||
contextName
|
contextName
|
||||||
);
|
);
|
||||||
const remaining = handleCreateAliasError(output, pathAlias);
|
|
||||||
if (handleSetupDomainError(output, remaining) !== 1) {
|
const handleResult = handleSetupDomainError(
|
||||||
console.log(
|
output,
|
||||||
`${chalk.cyan('> Success!')} ${
|
handleCreateAliasError(output, record)
|
||||||
rules.length
|
);
|
||||||
} rules configured for ${chalk.underline(target)} ${setStamp()}`
|
|
||||||
);
|
if (handleResult === 1) {
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`${chalk.cyan('> Success!')} ${chalk.bold(
|
||||||
|
`${isWildcardAlias(target) ? '' : 'https://'}${handleResult.alias}`
|
||||||
|
)} now points to https://${deployment.url} ${setStamp()}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are no rules for path alias we should find out a deployment and perform the alias
|
const [deploymentIdOrHost, aliasTarget] = args;
|
||||||
|
|
||||||
const deployment = handleCertError(
|
const deployment = handleCertError(
|
||||||
output,
|
output,
|
||||||
await getDeploymentForAlias(
|
await getDeploymentByIdOrHost(client, contextName, deploymentIdOrHost)
|
||||||
client,
|
|
||||||
output,
|
|
||||||
args,
|
|
||||||
opts['--local-config'],
|
|
||||||
user,
|
|
||||||
contextName,
|
|
||||||
localConfig
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (deployment === 1) {
|
if (deployment === 1) {
|
||||||
@@ -225,37 +205,32 @@ export default async function set(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign the alias for each of the targets in the array
|
output.log(`Assigning alias ${aliasTarget} to deployment ${deployment.url}`);
|
||||||
for (const target of targets) {
|
|
||||||
output.log(`Assigning alias ${target} to deployment ${deployment.url}`);
|
|
||||||
|
|
||||||
const isWildcard = isWildcardAlias(target);
|
const isWildcard = isWildcardAlias(aliasTarget);
|
||||||
const record = await assignAlias(
|
const record = await assignAlias(
|
||||||
output,
|
output,
|
||||||
client,
|
client,
|
||||||
deployment,
|
deployment,
|
||||||
target,
|
aliasTarget,
|
||||||
contextName,
|
contextName
|
||||||
noVerify
|
);
|
||||||
);
|
const handleResult = handleSetupDomainError(
|
||||||
const handleResult = handleSetupDomainError(
|
output,
|
||||||
output,
|
handleCreateAliasError(output, record)
|
||||||
handleCreateAliasError(output, record),
|
);
|
||||||
isWildcard
|
if (handleResult === 1) {
|
||||||
);
|
return 1;
|
||||||
if (handleResult === 1) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const prefix = isWildcard ? '' : 'https://';
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`${chalk.cyan('> Success!')} ${chalk.bold(
|
|
||||||
`${prefix}${handleResult.alias}`
|
|
||||||
)} now points to https://${deployment.url} ${setStamp()}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const prefix = isWildcard ? '' : 'https://';
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`${chalk.cyan('> Success!')} ${chalk.bold(
|
||||||
|
`${prefix}${handleResult.alias}`
|
||||||
|
)} now points to https://${deployment.url} ${setStamp()}`
|
||||||
|
);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,8 +240,7 @@ type SetupDomainError = Exclude<SetupDomainResolve, Domain>;
|
|||||||
|
|
||||||
function handleSetupDomainError<T>(
|
function handleSetupDomainError<T>(
|
||||||
output: Output,
|
output: Output,
|
||||||
error: SetupDomainError | T,
|
error: SetupDomainError | T
|
||||||
isWildcard: boolean = false
|
|
||||||
): T | 1 {
|
): T | 1 {
|
||||||
if (
|
if (
|
||||||
error instanceof ERRORS.DomainVerificationFailed ||
|
error instanceof ERRORS.DomainVerificationFailed ||
|
||||||
@@ -278,9 +252,7 @@ function handleSetupDomainError<T>(
|
|||||||
`We could not alias since the domain ${domain} could not be verified due to the following reasons:\n`
|
`We could not alias since the domain ${domain} could not be verified due to the following reasons:\n`
|
||||||
);
|
);
|
||||||
output.print(
|
output.print(
|
||||||
` ${chalk.gray(
|
`Nameservers verification failed since we see a different set than the intended set:`
|
||||||
'a)'
|
|
||||||
)} Nameservers verification failed since we see a different set than the intended set:`
|
|
||||||
);
|
);
|
||||||
output.print(
|
output.print(
|
||||||
`\n${formatNSTable(
|
`\n${formatNSTable(
|
||||||
@@ -289,34 +261,6 @@ function handleSetupDomainError<T>(
|
|||||||
{ extraSpace: ' ' }
|
{ extraSpace: ' ' }
|
||||||
)}\n\n`
|
)}\n\n`
|
||||||
);
|
);
|
||||||
if (error instanceof ERRORS.DomainVerificationFailed && !isWildcard) {
|
|
||||||
const { txtVerification } = error.meta;
|
|
||||||
output.print(
|
|
||||||
` ${chalk.gray(
|
|
||||||
'b)'
|
|
||||||
)} DNS TXT verification failed since found no matching records.`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
`\n${formatDnsTable(
|
|
||||||
[['_now', 'TXT', txtVerification.verificationRecord]],
|
|
||||||
{ extraSpace: ' ' }
|
|
||||||
)}\n\n`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
` Once your domain uses either the nameservers or the TXT DNS record from above, run again ${getCommandName(
|
|
||||||
'domains verify <domain>'
|
|
||||||
)}.\n`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
` We will also periodically run a verification check for you and you will receive an email once your domain is verified.\n`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
output.print(
|
|
||||||
` Once your domain uses the nameservers from above, run again ${getCommandName(
|
|
||||||
'domains verify <domain>'
|
|
||||||
)}.\n`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
output.print(' Read more: https://err.sh/now/domain-verification\n');
|
output.print(' Read more: https://err.sh/now/domain-verification\n');
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -457,66 +401,6 @@ function handleCreateAliasError<T>(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error instanceof ERRORS.RuleValidationFailed) {
|
|
||||||
output.error(`Rule validation error: ${error.meta.message}.`);
|
|
||||||
output.print(` Make sure your rules file is written correctly.\n`);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (error instanceof ERRORS.VerifyScaleTimeout) {
|
|
||||||
output.error(`Instance verification timed out (${ms(error.meta.timeout)})`);
|
|
||||||
output.log('Read more: https://err.sh/now/verification-timeout');
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (error instanceof ERRORS.NotSupportedMinScaleSlots) {
|
|
||||||
output.error(
|
|
||||||
`Scale rules from previous aliased deployment ${chalk.dim(
|
|
||||||
error.meta.url
|
|
||||||
)} could not be copied since Cloud v2 deployments cannot have a non-zero min`
|
|
||||||
);
|
|
||||||
output.log(
|
|
||||||
`Update the scale settings on ${chalk.dim(
|
|
||||||
error.meta.url
|
|
||||||
)} with ${getCommandName('scale')} and try again`
|
|
||||||
);
|
|
||||||
output.log('Read more: https://err.sh/now/v2-no-min');
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (error instanceof ERRORS.ForbiddenScaleMaxInstances) {
|
|
||||||
output.error(
|
|
||||||
`Scale rules from previous aliased deployment ${chalk.dim(
|
|
||||||
error.meta.url
|
|
||||||
)} could not be copied since the given number of max instances (${
|
|
||||||
error.meta.max
|
|
||||||
}) is not allowed.`
|
|
||||||
);
|
|
||||||
output.log(
|
|
||||||
`Update the scale settings on ${chalk.dim(
|
|
||||||
error.meta.url
|
|
||||||
)} with ${getCommandName('scale')} and try again`
|
|
||||||
);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (error instanceof ERRORS.ForbiddenScaleMinInstances) {
|
|
||||||
output.error(
|
|
||||||
`You can't scale to more than ${error.meta.max} min instances with your current plan.`
|
|
||||||
);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error instanceof ERRORS.InvalidScaleMinMaxRelation) {
|
|
||||||
output.error(
|
|
||||||
`Scale rules from previous aliased deployment ${chalk.dim(
|
|
||||||
error.meta.url
|
|
||||||
)} could not be copied becuase the relation between min and max instances is wrong.`
|
|
||||||
);
|
|
||||||
output.log(
|
|
||||||
`Update the scale settings on ${chalk.dim(
|
|
||||||
error.meta.url
|
|
||||||
)} with ${getCommandName('scale')} and try again`
|
|
||||||
);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error instanceof ERRORS.CertMissing) {
|
if (error instanceof ERRORS.CertMissing) {
|
||||||
output.error(
|
output.error(
|
||||||
`There is no certificate for the domain ${error.meta.domain} and it could not be created.`
|
`There is no certificate for the domain ${error.meta.domain} and it could not be created.`
|
||||||
@@ -552,3 +436,22 @@ function handleCreateAliasError<T>(
|
|||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTargetsForAlias(args: string[], { alias }: NowConfig) {
|
||||||
|
if (args.length) {
|
||||||
|
return [args[args.length - 1]]
|
||||||
|
.map(target => (target.indexOf('.') !== -1 ? toHost(target) : target))
|
||||||
|
.filter((x): x is string => !!x && typeof x === 'string');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!alias) {
|
||||||
|
return new ERRORS.NoAliasInConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the type for the option aliases
|
||||||
|
if (typeof alias !== 'string' && !Array.isArray(alias)) {
|
||||||
|
return new ERRORS.InvalidAliasInConfig(alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeof alias === 'string' ? [alias] : alias;
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import * as ERRORS from '../../util/errors-ts';
|
|||||||
import { Output } from '../../util/output';
|
import { Output } from '../../util/output';
|
||||||
import deleteCertById from '../../util/certs/delete-cert-by-id';
|
import deleteCertById from '../../util/certs/delete-cert-by-id';
|
||||||
import getCertById from '../../util/certs/get-cert-by-id';
|
import getCertById from '../../util/certs/get-cert-by-id';
|
||||||
import getCertsForDomain from '../../util/certs/get-certs-for-domain';
|
import { getCustomCertsForDomain } from '../../util/certs/get-custom-certs-for-domain';
|
||||||
import Client from '../../util/client';
|
import Client from '../../util/client';
|
||||||
import getScope from '../../util/get-scope';
|
import getScope from '../../util/get-scope';
|
||||||
import stamp from '../../util/output/stamp';
|
import stamp from '../../util/output/stamp';
|
||||||
@@ -66,9 +66,17 @@ async function rm(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (certs.length === 0) {
|
if (certs.length === 0) {
|
||||||
output.error(
|
if (id.includes('.')) {
|
||||||
`No certificates found by id "${id}" under ${chalk.bold(contextName)}`
|
output.error(
|
||||||
);
|
`No custom certificates found for "${id}" under ${chalk.bold(
|
||||||
|
contextName
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
output.error(
|
||||||
|
`No certificates found by id "${id}" under ${chalk.bold(contextName)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +109,7 @@ async function getCertsToDelete(
|
|||||||
) {
|
) {
|
||||||
const cert = await getCertById(client, id);
|
const cert = await getCertById(client, id);
|
||||||
if (cert instanceof ERRORS.CertNotFound) {
|
if (cert instanceof ERRORS.CertNotFound) {
|
||||||
const certs = await getCertsForDomain(output, client, contextName, id);
|
const certs = await getCustomCertsForDomain(client, contextName, id);
|
||||||
if (certs instanceof ERRORS.CertsPermissionDenied) {
|
if (certs instanceof ERRORS.CertsPermissionDenied) {
|
||||||
return certs;
|
return certs;
|
||||||
}
|
}
|
||||||
@@ -125,12 +133,7 @@ function readConfirmation(output: Output, msg: string, certs: Cert[]) {
|
|||||||
process.stdin
|
process.stdin
|
||||||
.on('data', d => {
|
.on('data', d => {
|
||||||
process.stdin.pause();
|
process.stdin.pause();
|
||||||
resolve(
|
resolve(d.toString().trim().toLowerCase() === 'y');
|
||||||
d
|
|
||||||
.toString()
|
|
||||||
.trim()
|
|
||||||
.toLowerCase() === 'y'
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.resume();
|
.resume();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import code from '../../util/output/code';
|
|||||||
import note from '../../util/output/note';
|
import note from '../../util/output/note';
|
||||||
import { getPkgName } from '../../util/pkg-name.ts';
|
import { getPkgName } from '../../util/pkg-name.ts';
|
||||||
|
|
||||||
export const latestHelp = () => `
|
export const help = () => `
|
||||||
${chalk.bold(`${logo} ${getPkgName()}`)} [options] <command | path>
|
${chalk.bold(`${logo} ${getPkgName()}`)} [options] <command | path>
|
||||||
|
|
||||||
${chalk.dim('Commands:')}
|
${chalk.dim('Commands:')}
|
||||||
@@ -100,7 +100,7 @@ export const latestHelp = () => `
|
|||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const latestArgs = {
|
export const args = {
|
||||||
'--force': Boolean,
|
'--force': Boolean,
|
||||||
'--with-cache': Boolean,
|
'--with-cache': Boolean,
|
||||||
'--public': Boolean,
|
'--public': Boolean,
|
||||||
@@ -108,7 +108,6 @@ export const latestArgs = {
|
|||||||
'--env': [String],
|
'--env': [String],
|
||||||
'--build-env': [String],
|
'--build-env': [String],
|
||||||
'--meta': [String],
|
'--meta': [String],
|
||||||
'--no-scale': Boolean,
|
|
||||||
// This is not an array in favor of matching
|
// This is not an array in favor of matching
|
||||||
// the config property name.
|
// the config property name.
|
||||||
'--regions': String,
|
'--regions': String,
|
||||||
@@ -127,180 +126,3 @@ export const latestArgs = {
|
|||||||
'-n': '--name',
|
'-n': '--name',
|
||||||
'--target': String,
|
'--target': String,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const legacyArgsMri = {
|
|
||||||
string: [
|
|
||||||
'name',
|
|
||||||
'build-env',
|
|
||||||
'alias',
|
|
||||||
'meta',
|
|
||||||
'session-affinity',
|
|
||||||
'regions',
|
|
||||||
'dotenv',
|
|
||||||
'target',
|
|
||||||
],
|
|
||||||
boolean: [
|
|
||||||
'help',
|
|
||||||
'version',
|
|
||||||
'debug',
|
|
||||||
'force',
|
|
||||||
'links',
|
|
||||||
'C',
|
|
||||||
'clipboard',
|
|
||||||
'forward-npm',
|
|
||||||
'docker',
|
|
||||||
'npm',
|
|
||||||
'static',
|
|
||||||
'public',
|
|
||||||
'no-scale',
|
|
||||||
'no-verify',
|
|
||||||
'dotenv',
|
|
||||||
'prod',
|
|
||||||
],
|
|
||||||
default: {
|
|
||||||
C: false,
|
|
||||||
clipboard: true,
|
|
||||||
},
|
|
||||||
alias: {
|
|
||||||
env: 'e',
|
|
||||||
meta: 'm',
|
|
||||||
'build-env': 'b',
|
|
||||||
dotenv: 'E',
|
|
||||||
help: 'h',
|
|
||||||
debug: 'd',
|
|
||||||
version: 'v',
|
|
||||||
force: 'f',
|
|
||||||
links: 'l',
|
|
||||||
public: 'p',
|
|
||||||
'forward-npm': 'N',
|
|
||||||
'session-affinity': 'S',
|
|
||||||
name: 'n',
|
|
||||||
project: 'P',
|
|
||||||
alias: 'a',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// The following arg parsing is simply to make it compatible
|
|
||||||
// with the index. Let's not migrate it to the new args parsing, as
|
|
||||||
// we are gonna delete this file soon anyways.
|
|
||||||
const argList = {};
|
|
||||||
|
|
||||||
for (const item of legacyArgsMri.string) {
|
|
||||||
argList[`--${item}`] = String;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const item of legacyArgsMri.boolean) {
|
|
||||||
argList[`--${item}`] = Boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const item of Object.keys(legacyArgsMri.alias)) {
|
|
||||||
argList[`-${legacyArgsMri.alias[item]}`] = `--${item}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const legacyArgs = argList;
|
|
||||||
|
|
||||||
export const legacyHelp = () => `
|
|
||||||
${chalk.bold(`${logo} now`)} [options] <command | path>
|
|
||||||
|
|
||||||
${chalk.dim('Commands:')}
|
|
||||||
|
|
||||||
${chalk.dim('Cloud')}
|
|
||||||
|
|
||||||
deploy [path] Performs a deployment ${chalk.bold(
|
|
||||||
'(default)'
|
|
||||||
)}
|
|
||||||
ls | list [app] Lists deployments
|
|
||||||
rm | remove [id] Removes a deployment
|
|
||||||
ln | alias [id] [url] Configures aliases for deployments
|
|
||||||
inspect [id] Displays information related to a deployment
|
|
||||||
domains [name] Manages your domain names
|
|
||||||
certs [cmd] Manages your SSL certificates
|
|
||||||
secrets [name] Manages your secret environment variables
|
|
||||||
dns [name] Manages your DNS records
|
|
||||||
logs [url] Displays the logs for a deployment
|
|
||||||
scale [args] Scales the instance count of a deployment
|
|
||||||
init [example] Initialize an example project
|
|
||||||
help [cmd] Displays complete help for [cmd]
|
|
||||||
|
|
||||||
${chalk.dim('Administrative')}
|
|
||||||
|
|
||||||
billing | cc [cmd] Manages your credit cards and billing methods
|
|
||||||
upgrade | downgrade [plan] Upgrades or downgrades your plan
|
|
||||||
teams Manages your teams
|
|
||||||
switch [scope] Switches between teams and your account
|
|
||||||
login [email] Logs into your account or creates a new one
|
|
||||||
logout Logs out of your account
|
|
||||||
whoami Shows the username of the currently logged in user
|
|
||||||
|
|
||||||
${chalk.dim('Options:')}
|
|
||||||
|
|
||||||
-h, --help Output usage information
|
|
||||||
-v, --version Output the version number
|
|
||||||
-V, --platform-version Set the platform version to deploy to
|
|
||||||
-n, --name Set the project name of the deployment
|
|
||||||
-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]
|
|
||||||
-f, --force Force a new deployment even if nothing has changed
|
|
||||||
-t ${chalk.underline('TOKEN')}, --token=${chalk.underline(
|
|
||||||
'TOKEN'
|
|
||||||
)} Login token
|
|
||||||
-l, --links Copy symlinks without resolving their target
|
|
||||||
-p, --public Deployment is public (${chalk.dim(
|
|
||||||
'`/_src`'
|
|
||||||
)} is exposed) [on for oss, off for premium]
|
|
||||||
-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.
|
|
||||||
-E ${chalk.underline('FILE')}, --dotenv=${chalk.underline(
|
|
||||||
'FILE'
|
|
||||||
)} Include env vars from .env file. Defaults to '.env'
|
|
||||||
-C, --no-clipboard Do not attempt to copy URL to clipboard
|
|
||||||
-N, --forward-npm Forward login information to install private npm modules
|
|
||||||
--session-affinity Session affinity, \`ip\` or \`random\` (default) to control session affinity
|
|
||||||
-S, --scope Set a custom scope
|
|
||||||
--regions Set default regions or DCs to enable the deployment on
|
|
||||||
--no-scale Skip scaling rules deploying with the default presets
|
|
||||||
--no-verify Skip step of waiting until instance count meets given constraints
|
|
||||||
|
|
||||||
${chalk.dim(`Enforceable Types (by default, it's detected automatically):`)}
|
|
||||||
|
|
||||||
--npm Node.js application
|
|
||||||
--docker Docker container
|
|
||||||
--static Static file hosting
|
|
||||||
|
|
||||||
${chalk.dim('Examples:')}
|
|
||||||
|
|
||||||
${chalk.gray('–')} Deploy the current directory
|
|
||||||
|
|
||||||
${chalk.cyan('$ now')}
|
|
||||||
|
|
||||||
${chalk.gray('–')} Deploy a custom path
|
|
||||||
|
|
||||||
${chalk.cyan('$ now /usr/src/project')}
|
|
||||||
|
|
||||||
${chalk.gray('–')} Deploy a GitHub repository
|
|
||||||
|
|
||||||
${chalk.cyan('$ now user/repo#ref')}
|
|
||||||
|
|
||||||
${chalk.gray('–')} Deploy with environment variables
|
|
||||||
|
|
||||||
${chalk.cyan('$ now -e NODE_ENV=production -e SECRET=@mysql-secret')}
|
|
||||||
|
|
||||||
${chalk.gray('–')} Show the usage information for the sub command ${chalk.dim(
|
|
||||||
'`list`'
|
|
||||||
)}
|
|
||||||
|
|
||||||
${chalk.cyan('$ now help list')}
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -1,22 +1,16 @@
|
|||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import { resolve, basename, parse, join } from 'path';
|
import { resolve, basename } from 'path';
|
||||||
import { fileNameSymbol } from '@vercel/client';
|
import { fileNameSymbol } from '@vercel/client';
|
||||||
import Client from '../../util/client.ts';
|
import Client from '../../util/client.ts';
|
||||||
import getScope from '../../util/get-scope.ts';
|
import getScope from '../../util/get-scope.ts';
|
||||||
import createOutput from '../../util/output';
|
import createOutput from '../../util/output';
|
||||||
import code from '../../util/output/code';
|
import code from '../../util/output/code';
|
||||||
import highlight from '../../util/output/highlight';
|
import highlight from '../../util/output/highlight';
|
||||||
import param from '../../util/output/param.ts';
|
|
||||||
import { readLocalConfig } from '../../util/config/files';
|
import { readLocalConfig } from '../../util/config/files';
|
||||||
import getArgs from '../../util/get-args';
|
import getArgs from '../../util/get-args';
|
||||||
import * as parts from './args';
|
|
||||||
import { handleError } from '../../util/error';
|
import { handleError } from '../../util/error';
|
||||||
import readPackage from '../../util/read-package';
|
import { help, args } from './args';
|
||||||
import preferV2Deployment, {
|
import deploy from './latest';
|
||||||
hasDockerfile,
|
|
||||||
hasServerfile,
|
|
||||||
} from '../../util/prefer-v2-deployment';
|
|
||||||
import getProjectName from '../../util/get-project-name';
|
|
||||||
|
|
||||||
export default async ctx => {
|
export default async ctx => {
|
||||||
const {
|
const {
|
||||||
@@ -24,14 +18,12 @@ export default async ctx => {
|
|||||||
config: { currentTeam },
|
config: { currentTeam },
|
||||||
apiUrl,
|
apiUrl,
|
||||||
} = ctx;
|
} = ctx;
|
||||||
const combinedArgs = Object.assign({}, parts.legacyArgs, parts.latestArgs);
|
|
||||||
|
|
||||||
let platformVersion = null;
|
|
||||||
let contextName = currentTeam || 'current user';
|
let contextName = currentTeam || 'current user';
|
||||||
let argv = null;
|
let argv = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
argv = getArgs(ctx.argv.slice(2), combinedArgs);
|
argv = getArgs(ctx.argv.slice(2), args);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error);
|
handleError(error);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -58,12 +50,8 @@ export default async ctx => {
|
|||||||
const debugEnabled = argv['--debug'];
|
const debugEnabled = argv['--debug'];
|
||||||
const output = createOutput({ debug: debugEnabled });
|
const output = createOutput({ debug: debugEnabled });
|
||||||
const stats = {};
|
const stats = {};
|
||||||
const versionFlag = argv['--platform-version'];
|
|
||||||
|
|
||||||
if (argv['--help']) {
|
if (argv['--help']) {
|
||||||
const lastArg = argv._[argv._.length - 1];
|
|
||||||
const help = lastArg === 'deploy-v1' ? parts.legacyHelp : parts.latestHelp;
|
|
||||||
|
|
||||||
output.print(help());
|
output.print(help());
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
@@ -72,28 +60,15 @@ export default async ctx => {
|
|||||||
try {
|
try {
|
||||||
stats[path] = await fs.lstat(path);
|
stats[path] = await fs.lstat(path);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const { ext } = parse(path);
|
output.error(
|
||||||
|
`The specified file or directory "${basename(path)}" does not exist.`
|
||||||
if (versionFlag === 1 && !ext) {
|
);
|
||||||
// This will ensure `-V 1 zeit/serve` (GitHub deployments) work. Since
|
return 1;
|
||||||
// GitHub repositories are never just one file, we need to set
|
|
||||||
// the `isFile` property accordingly.
|
|
||||||
stats[path] = {
|
|
||||||
isFile: () => false,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
output.error(
|
|
||||||
`The specified file or directory "${basename(path)}" does not exist.`
|
|
||||||
);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let client = null;
|
let client = null;
|
||||||
|
|
||||||
const isFile = Object.keys(stats).length === 1 && stats[paths[0]].isFile();
|
|
||||||
|
|
||||||
if (authConfig && authConfig.token) {
|
if (authConfig && authConfig.token) {
|
||||||
client = new Client({
|
client = new Client({
|
||||||
apiUrl,
|
apiUrl,
|
||||||
@@ -102,7 +77,7 @@ export default async ctx => {
|
|||||||
debug: debugEnabled,
|
debug: debugEnabled,
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
({ contextName, platformVersion } = await getScope(client));
|
({ contextName } = await getScope(client));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
|
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
|
||||||
output.error(err.message);
|
output.error(err.message);
|
||||||
@@ -120,17 +95,14 @@ export default async ctx => {
|
|||||||
|
|
||||||
if (version) {
|
if (version) {
|
||||||
if (typeof version === 'number') {
|
if (typeof version === 'number') {
|
||||||
if (version !== 1 && version !== 2) {
|
if (version !== 2) {
|
||||||
const first = code(1);
|
const two = code(2);
|
||||||
const second = code(2);
|
|
||||||
|
|
||||||
output.error(
|
output.error(
|
||||||
`The value of the ${prop} property within ${file} can only be ${first} or ${second}.`
|
`The value of the ${prop} property within ${file} can only be ${two}.`
|
||||||
);
|
);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
platformVersion = version;
|
|
||||||
} else {
|
} else {
|
||||||
output.error(
|
output.error(
|
||||||
`The ${prop} property inside your ${file} file must be a number.`
|
`The ${prop} property inside your ${file} file must be a number.`
|
||||||
@@ -140,61 +112,5 @@ export default async ctx => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (versionFlag) {
|
return deploy(ctx, contextName, output, stats, localConfig, args);
|
||||||
if (versionFlag !== 1 && versionFlag !== 2) {
|
|
||||||
output.error(
|
|
||||||
`The ${param('--platform-version')} option must be either ${code(
|
|
||||||
'1'
|
|
||||||
)} or ${code('2')}.`
|
|
||||||
);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
platformVersion = versionFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
platformVersion === 1 &&
|
|
||||||
versionFlag !== 1 &&
|
|
||||||
!argv['--docker'] &&
|
|
||||||
!argv['--npm']
|
|
||||||
) {
|
|
||||||
// Only check when it was not set via CLI flag
|
|
||||||
const reason = await preferV2Deployment({
|
|
||||||
client,
|
|
||||||
localConfig,
|
|
||||||
projectName: getProjectName({
|
|
||||||
argv,
|
|
||||||
nowConfig: localConfig || {},
|
|
||||||
isFile,
|
|
||||||
paths,
|
|
||||||
}),
|
|
||||||
hasServerfile: await hasServerfile(paths[0]),
|
|
||||||
hasDockerfile: await hasDockerfile(paths[0]),
|
|
||||||
pkg: await readPackage(join(paths[0], 'package.json')),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (reason) {
|
|
||||||
output.note(reason);
|
|
||||||
platformVersion = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (platformVersion === null || platformVersion > 1) {
|
|
||||||
return require('./latest').default(
|
|
||||||
ctx,
|
|
||||||
contextName,
|
|
||||||
output,
|
|
||||||
stats,
|
|
||||||
localConfig,
|
|
||||||
parts.latestArgs
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return require('./legacy').default(
|
|
||||||
ctx,
|
|
||||||
contextName,
|
|
||||||
output,
|
|
||||||
parts.legacyArgsMri
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -162,7 +162,11 @@ const printDeploymentStatus = async (
|
|||||||
prependEmoji(
|
prependEmoji(
|
||||||
`${chalk.dim(indication.payload)}`,
|
`${chalk.dim(indication.payload)}`,
|
||||||
emoji(indication.type)
|
emoji(indication.type)
|
||||||
) + `\n`
|
) +
|
||||||
|
`\n` +
|
||||||
|
(indication.link
|
||||||
|
? `${indication.action || 'Learn More'}: ${indication.link}\n\n`
|
||||||
|
: '')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -669,7 +673,7 @@ export default async function main(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (err instanceof BuildError) {
|
if (err instanceof BuildError) {
|
||||||
output.error('Build failed');
|
output.error(err.message || 'Build failed');
|
||||||
output.error(
|
output.error(
|
||||||
`Check your logs at https://${now.url}/_logs or run ${getCommandName(
|
`Check your logs at https://${now.url}/_logs or run ${getCommandName(
|
||||||
`logs ${now.url}`,
|
`logs ${now.url}`,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -8,12 +8,15 @@ import Client from '../../util/client';
|
|||||||
import { getLinkedProject } from '../../util/projects/link';
|
import { getLinkedProject } from '../../util/projects/link';
|
||||||
import { getFrameworks } from '../../util/get-frameworks';
|
import { getFrameworks } from '../../util/get-frameworks';
|
||||||
import { isSettingValue } from '../../util/is-setting-value';
|
import { isSettingValue } from '../../util/is-setting-value';
|
||||||
import { getCommandName } from '../../util/pkg-name';
|
import { ProjectSettings, ProjectEnvTarget } from '../../types';
|
||||||
import { ProjectSettings } from '../../types';
|
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
|
||||||
|
import { Env } from '@vercel/build-utils';
|
||||||
|
import setupAndLink from '../../util/link/setup-and-link';
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
'--debug'?: boolean;
|
'--debug'?: boolean;
|
||||||
'--listen'?: string;
|
'--listen'?: string;
|
||||||
|
'--confirm': boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function dev(
|
export default async function dev(
|
||||||
@@ -35,25 +38,39 @@ export default async function dev(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// retrieve dev command
|
// retrieve dev command
|
||||||
const [link, frameworks] = await Promise.all([
|
let [link, frameworks] = await Promise.all([
|
||||||
getLinkedProject(output, client, cwd),
|
getLinkedProject(output, client, cwd),
|
||||||
getFrameworks(client),
|
getFrameworks(client),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (link.status === 'not_linked' && !process.env.__VERCEL_SKIP_DEV_CMD) {
|
||||||
|
const autoConfirm = opts['--confirm'];
|
||||||
|
const forceDelete = false;
|
||||||
|
|
||||||
|
link = await setupAndLink(
|
||||||
|
ctx,
|
||||||
|
output,
|
||||||
|
cwd,
|
||||||
|
forceDelete,
|
||||||
|
autoConfirm,
|
||||||
|
'link',
|
||||||
|
'Set up and develop'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (link.status === 'not_linked') {
|
||||||
|
// User aborted project linking questions
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (link.status === 'error') {
|
if (link.status === 'error') {
|
||||||
return link.exitCode;
|
return link.exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (link.status === 'not_linked' && !process.env.__VERCEL_SKIP_DEV_CMD) {
|
|
||||||
output.error(
|
|
||||||
`Your codebase isn’t linked to a project on Vercel. Run ${getCommandName()} to link it.`
|
|
||||||
);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let devCommand: string | undefined;
|
let devCommand: string | undefined;
|
||||||
let frameworkSlug: string | undefined;
|
let frameworkSlug: string | undefined;
|
||||||
let projectSettings: ProjectSettings | undefined;
|
let projectSettings: ProjectSettings | undefined;
|
||||||
|
let environmentVars: Env | undefined;
|
||||||
if (link.status === 'linked') {
|
if (link.status === 'linked') {
|
||||||
const { project, org } = link;
|
const { project, org } = link;
|
||||||
client.currentTeam = org.type === 'team' ? org.id : undefined;
|
client.currentTeam = org.type === 'team' ? org.id : undefined;
|
||||||
@@ -80,6 +97,13 @@ export default async function dev(
|
|||||||
if (project.rootDirectory) {
|
if (project.rootDirectory) {
|
||||||
cwd = join(cwd, project.rootDirectory);
|
cwd = join(cwd, project.rootDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
environmentVars = await getDecryptedEnvRecords(
|
||||||
|
output,
|
||||||
|
client,
|
||||||
|
project,
|
||||||
|
ProjectEnvTarget.Development
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const devServer = new DevServer(cwd, {
|
const devServer = new DevServer(cwd, {
|
||||||
@@ -88,6 +112,7 @@ export default async function dev(
|
|||||||
devCommand,
|
devCommand,
|
||||||
frameworkSlug,
|
frameworkSlug,
|
||||||
projectSettings,
|
projectSettings,
|
||||||
|
environmentVars,
|
||||||
});
|
});
|
||||||
|
|
||||||
process.once('SIGINT', () => devServer.stop());
|
process.once('SIGINT', () => devServer.stop());
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ const help = () => {
|
|||||||
-d, --debug Debug mode [off]
|
-d, --debug Debug mode [off]
|
||||||
-l, --listen [uri] Specify a URI endpoint on which to listen [0.0.0.0:3000]
|
-l, --listen [uri] Specify a URI endpoint on which to listen [0.0.0.0:3000]
|
||||||
-t, --token [token] Specify an Authorization Token
|
-t, --token [token] Specify an Authorization Token
|
||||||
|
--confirm Skip questions and use defaults when setting up a new project
|
||||||
|
|
||||||
${chalk.dim('Examples:')}
|
${chalk.dim('Examples:')}
|
||||||
|
|
||||||
@@ -56,6 +57,7 @@ export default async function main(ctx: NowContext) {
|
|||||||
argv = getArgs(ctx.argv.slice(2), {
|
argv = getArgs(ctx.argv.slice(2), {
|
||||||
'--listen': String,
|
'--listen': String,
|
||||||
'-l': '--listen',
|
'-l': '--listen',
|
||||||
|
'--confirm': Boolean,
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated
|
||||||
'--port': Number,
|
'--port': Number,
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import psl from 'psl';
|
|
||||||
|
|
||||||
import { NowContext } from '../../types';
|
import { NowContext } from '../../types';
|
||||||
import { Output } from '../../util/output';
|
import { Output } from '../../util/output';
|
||||||
import * as ERRORS from '../../util/errors-ts';
|
import * as ERRORS from '../../util/errors-ts';
|
||||||
import addDomain from '../../util/domains/add-domain';
|
|
||||||
import Client from '../../util/client';
|
import Client from '../../util/client';
|
||||||
import cmd from '../../util/output/cmd';
|
|
||||||
import formatDnsTable from '../../util/format-dns-table';
|
|
||||||
import formatNSTable from '../../util/format-ns-table';
|
import formatNSTable from '../../util/format-ns-table';
|
||||||
import getScope from '../../util/get-scope';
|
import getScope from '../../util/get-scope';
|
||||||
import stamp from '../../util/output/stamp';
|
import stamp from '../../util/output/stamp';
|
||||||
import param from '../../util/output/param';
|
import { getCommandName } from '../../util/pkg-name';
|
||||||
import { getCommandName, getTitleName } from '../../util/pkg-name';
|
import { getDomain } from '../../util/domains/get-domain';
|
||||||
|
import { getLinkedProject } from '../../util/projects/link';
|
||||||
|
import { isPublicSuffix } from '../../util/domains/is-public-suffix';
|
||||||
|
import { getDomainConfig } from '../../util/domains/get-domain-config';
|
||||||
|
import { addDomainToProject } from '../../util/projects/add-domain-to-project';
|
||||||
|
import { removeDomainFromProject } from '../../util/projects/remove-domain-from-project';
|
||||||
|
import code from '../../util/output/code';
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
'--cdn': boolean;
|
|
||||||
'--debug': boolean;
|
'--debug': boolean;
|
||||||
'--no-cdn': boolean;
|
'--force': boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function add(
|
export default async function add(
|
||||||
@@ -33,6 +34,7 @@ export default async function add(
|
|||||||
const { currentTeam } = config;
|
const { currentTeam } = config;
|
||||||
const { apiUrl } = ctx;
|
const { apiUrl } = ctx;
|
||||||
const debug = opts['--debug'];
|
const debug = opts['--debug'];
|
||||||
|
const force = opts['--force'];
|
||||||
const client = new Client({ apiUrl, token, currentTeam, debug });
|
const client = new Client({ apiUrl, token, currentTeam, debug });
|
||||||
let contextName = null;
|
let contextName = null;
|
||||||
|
|
||||||
@@ -47,105 +49,116 @@ export default async function add(
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts['--cdn'] !== undefined || opts['--no-cdn'] !== undefined) {
|
const project = await getLinkedProject(output, client).then(result => {
|
||||||
output.error(`Toggling CF from ${getTitleName()} CLI is deprecated.`);
|
if (result.status === 'linked') {
|
||||||
return 1;
|
return result.project;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length !== 1) {
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (project && args.length !== 1) {
|
||||||
output.error(
|
output.error(
|
||||||
`${getCommandName('domains add <domain>')} expects one argument`
|
`${getCommandName('domains add <domain>')} expects one argument.`
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
} else if (!project && args.length !== 2) {
|
||||||
|
output.error(
|
||||||
|
`${getCommandName(
|
||||||
|
'domains add <domain> <project>'
|
||||||
|
)} expects two arguments.`
|
||||||
);
|
);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const domainName = String(args[0]);
|
const domainName = String(args[0]);
|
||||||
const parsedDomain = psl.parse(domainName);
|
const projectName = project ? project.name : String(args[1]);
|
||||||
if (parsedDomain.error) {
|
|
||||||
output.error(`The provided domain name ${param(domainName)} is invalid`);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { domain, subdomain } = parsedDomain;
|
|
||||||
if (!domain) {
|
|
||||||
output.error(`The provided domain '${param(domainName)}' is not valid.`);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subdomain) {
|
|
||||||
output.error(
|
|
||||||
`You are adding '${domainName}' as a domain name containing a subdomain part '${subdomain}'\n` +
|
|
||||||
` This feature is deprecated, please add just the root domain: ${chalk.cyan(
|
|
||||||
`${getCommandName(`domain add ${domain}`)}`
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const addStamp = stamp();
|
const addStamp = stamp();
|
||||||
const addedDomain = await addDomain(client, domainName, contextName);
|
|
||||||
|
|
||||||
if (addedDomain instanceof ERRORS.InvalidDomain) {
|
let aliasTarget = await addDomainToProject(client, projectName, domainName);
|
||||||
output.error(
|
|
||||||
`The provided domain name "${addedDomain.meta.domain}" is invalid`
|
|
||||||
);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addedDomain instanceof ERRORS.DomainAlreadyExists) {
|
if (aliasTarget instanceof Error) {
|
||||||
output.error(
|
if (
|
||||||
`The domain ${chalk.underline(
|
aliasTarget instanceof ERRORS.APIError &&
|
||||||
addedDomain.meta.domain
|
aliasTarget.code === 'ALIAS_DOMAIN_EXIST' &&
|
||||||
)} is already registered by a different account.\n` +
|
aliasTarget.project &&
|
||||||
` If this seems like a mistake, please contact us at support@vercel.com`
|
aliasTarget.project.id
|
||||||
);
|
) {
|
||||||
return 1;
|
if (force) {
|
||||||
|
const removeResponse = await removeDomainFromProject(
|
||||||
|
client,
|
||||||
|
aliasTarget.project.id,
|
||||||
|
domainName
|
||||||
|
);
|
||||||
|
|
||||||
|
if (removeResponse instanceof Error) {
|
||||||
|
output.prettyError(removeResponse);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
aliasTarget = await addDomainToProject(client, projectName, domainName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aliasTarget instanceof Error) {
|
||||||
|
output.prettyError(aliasTarget);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can cast the information because we've just added the domain and it should be there
|
// We can cast the information because we've just added the domain and it should be there
|
||||||
console.log(
|
console.log(
|
||||||
`${chalk.cyan('> Success!')} Domain ${chalk.bold(
|
`${chalk.cyan('> Success!')} Domain ${chalk.bold(
|
||||||
addedDomain.name
|
domainName
|
||||||
)} added correctly. ${addStamp()}\n`
|
)} added to project ${chalk.bold(projectName)}. ${addStamp()}`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!addedDomain.verified) {
|
if (isPublicSuffix(domainName)) {
|
||||||
|
output.log(
|
||||||
|
`The domain will automatically get assigned to your latest production deployment.`
|
||||||
|
);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const domainResponse = await getDomain(client, contextName, domainName);
|
||||||
|
|
||||||
|
if (domainResponse instanceof Error) {
|
||||||
|
output.prettyError(domainResponse);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const domainConfig = await getDomainConfig(client, domainName);
|
||||||
|
|
||||||
|
if (domainConfig.misconfigured) {
|
||||||
output.warn(
|
output.warn(
|
||||||
`The domain was added but it is not verified. To verify it, you should either:`
|
`This domain is not configured properly. To configure it you should either:`
|
||||||
);
|
);
|
||||||
output.print(
|
output.print(
|
||||||
` ${chalk.gray(
|
` ${chalk.grey('a)')} ` +
|
||||||
'a)'
|
`Set the following record on your DNS provider to continue: ` +
|
||||||
)} Change your domain nameservers to the following intended set: ${chalk.gray(
|
`${code(`A ${domainName} 76.76.21.21`)} ` +
|
||||||
'[recommended]'
|
`${chalk.grey('[recommended]')}\n`
|
||||||
)}\n`
|
);
|
||||||
|
output.print(
|
||||||
|
` ${chalk.grey('b)')} ` +
|
||||||
|
`Change your Domains's nameservers to the intended set`
|
||||||
);
|
);
|
||||||
output.print(
|
output.print(
|
||||||
`\n${formatNSTable(
|
`\n${formatNSTable(
|
||||||
addedDomain.intendedNameservers,
|
domainResponse.intendedNameservers,
|
||||||
addedDomain.nameservers,
|
domainResponse.nameservers,
|
||||||
{ extraSpace: ' ' }
|
{ extraSpace: ' ' }
|
||||||
)}\n\n`
|
)}\n\n`
|
||||||
);
|
);
|
||||||
output.print(
|
|
||||||
` ${chalk.gray(
|
|
||||||
'b)'
|
|
||||||
)} Add a DNS TXT record with the name and value shown below.\n`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
`\n${formatDnsTable([['_now', 'TXT', addedDomain.verificationRecord]], {
|
|
||||||
extraSpace: ' ',
|
|
||||||
})}\n\n`
|
|
||||||
);
|
|
||||||
output.print(
|
output.print(
|
||||||
` We will run a verification for you and you will receive an email upon completion.\n`
|
` We will run a verification for you and you will receive an email upon completion.\n`
|
||||||
);
|
);
|
||||||
output.print(
|
output.print(' Read more: https://vercel.link/domain-configuration\n\n');
|
||||||
` If you want to force running a verification, you can run ${cmd(
|
} else {
|
||||||
`${getCommandName('domains verify <domain>')}`
|
output.log(
|
||||||
)}\n`
|
`The domain will automatically get assigned to your latest production deployment.`
|
||||||
);
|
);
|
||||||
output.print(' Read more: https://err.sh/now/domain-verification\n\n');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -71,8 +71,9 @@ export default async function buy(
|
|||||||
|
|
||||||
const availableStamp = stamp();
|
const availableStamp = stamp();
|
||||||
const domainPrice = await getDomainPrice(client, domainName);
|
const domainPrice = await getDomainPrice(client, domainName);
|
||||||
if (domainPrice instanceof ERRORS.UnsupportedTLD) {
|
|
||||||
output.error(`The TLD for ${param(domainName)} is not supported.`);
|
if (domainPrice instanceof Error) {
|
||||||
|
output.prettyError(domainPrice);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import transferIn from './transfer-in';
|
|||||||
import inspect from './inspect';
|
import inspect from './inspect';
|
||||||
import ls from './ls';
|
import ls from './ls';
|
||||||
import rm from './rm';
|
import rm from './rm';
|
||||||
import verify from './verify';
|
|
||||||
import move from './move';
|
import move from './move';
|
||||||
|
import verify from './verify';
|
||||||
import { getPkgName } from '../../util/pkg-name';
|
import { getPkgName } from '../../util/pkg-name';
|
||||||
|
|
||||||
const help = () => {
|
const help = () => {
|
||||||
@@ -25,17 +25,17 @@ const help = () => {
|
|||||||
|
|
||||||
ls Show all domains in a list
|
ls Show all domains in a list
|
||||||
inspect [name] Displays information related to a domain
|
inspect [name] Displays information related to a domain
|
||||||
add [name] Add a new domain that you already own
|
add [name] [project] Add a new domain that you already own
|
||||||
rm [name] Remove a domain
|
rm [name] Remove a domain
|
||||||
buy [name] Buy a domain that you don't yet own
|
buy [name] Buy a domain that you don't yet own
|
||||||
move [name] [destination] Move a domain to another user or team.
|
move [name] [destination] Move a domain to another user or team.
|
||||||
transfer-in [name] Transfer in a domain to Vercel
|
transfer-in [name] Transfer in a domain to Vercel
|
||||||
verify [name] Run a verification for a domain
|
|
||||||
|
|
||||||
${chalk.dim('Options:')}
|
${chalk.dim('Options:')}
|
||||||
|
|
||||||
-h, --help Output usage information
|
-h, --help Output usage information
|
||||||
-d, --debug Debug mode [off]
|
-d, --debug Debug mode [off]
|
||||||
|
-f, --force Force a domain on a project and remove it from an existing one
|
||||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||||
'FILE'
|
'FILE'
|
||||||
)} Path to the local ${'`vercel.json`'} file
|
)} Path to the local ${'`vercel.json`'} file
|
||||||
@@ -90,10 +90,9 @@ export default async function main(ctx: NowContext) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
argv = getArgs(ctx.argv.slice(2), {
|
argv = getArgs(ctx.argv.slice(2), {
|
||||||
'--cdn': Boolean,
|
|
||||||
'--code': String,
|
'--code': String,
|
||||||
'--no-cdn': Boolean,
|
|
||||||
'--yes': Boolean,
|
'--yes': Boolean,
|
||||||
|
'--force': Boolean,
|
||||||
'--next': Number,
|
'--next': Number,
|
||||||
'-N': '--next',
|
'-N': '--next',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,13 +4,18 @@ import { NowContext } from '../../types';
|
|||||||
import { Output } from '../../util/output';
|
import { Output } from '../../util/output';
|
||||||
import Client from '../../util/client';
|
import Client from '../../util/client';
|
||||||
import stamp from '../../util/output/stamp';
|
import stamp from '../../util/output/stamp';
|
||||||
import dnsTable from '../../util/format-dns-table';
|
|
||||||
import formatDate from '../../util/format-date';
|
import formatDate from '../../util/format-date';
|
||||||
import formatNSTable from '../../util/format-ns-table';
|
import formatNSTable from '../../util/format-ns-table';
|
||||||
import getDomainByName from '../../util/domains/get-domain-by-name';
|
import getDomainByName from '../../util/domains/get-domain-by-name';
|
||||||
import getScope from '../../util/get-scope';
|
import getScope from '../../util/get-scope';
|
||||||
|
import formatTable from '../../util/format-table';
|
||||||
|
import { findProjectsForDomain } from '../../util/projects/find-projects-for-domain';
|
||||||
import getDomainPrice from '../../util/domains/get-domain-price';
|
import getDomainPrice from '../../util/domains/get-domain-price';
|
||||||
import { getCommandName } from '../../util/pkg-name';
|
import { getCommandName } from '../../util/pkg-name';
|
||||||
|
import { getDomainConfig } from '../../util/domains/get-domain-config';
|
||||||
|
import code from '../../util/output/code';
|
||||||
|
import wait from '../../util/output/wait';
|
||||||
|
import { getDomainRegistrar } from '../../util/domains/get-domain-registrar';
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
'--debug': boolean;
|
'--debug': boolean;
|
||||||
@@ -64,29 +69,26 @@ export default async function inspect(
|
|||||||
}
|
}
|
||||||
|
|
||||||
output.debug(`Fetching domain info`);
|
output.debug(`Fetching domain info`);
|
||||||
const [domain, renewalPrice] = await Promise.all([
|
|
||||||
getDomainByName(client, contextName, domainName),
|
const cancelWait = wait(
|
||||||
getDomainPrice(client, domainName, 'renewal')
|
`Fetching Domain ${domainName} under ${chalk.bold(contextName)}`
|
||||||
.then(res => (res instanceof Error ? null : res.price))
|
);
|
||||||
.catch(() => null),
|
|
||||||
]);
|
const information = await fetchInformation({
|
||||||
if (domain instanceof DomainNotFound) {
|
output,
|
||||||
output.error(
|
client,
|
||||||
`Domain not found by "${domainName}" under ${chalk.bold(contextName)}`
|
contextName,
|
||||||
);
|
domainName,
|
||||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
cancelWait,
|
||||||
return 1;
|
}).finally(() => {
|
||||||
|
cancelWait();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof information === 'number') {
|
||||||
|
return information;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (domain instanceof DomainPermissionDenied) {
|
const { domain, projects, renewalPrice, domainConfig } = information;
|
||||||
output.error(
|
|
||||||
`You don't have access to the domain ${domainName} under ${chalk.bold(
|
|
||||||
contextName
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.log(
|
output.log(
|
||||||
`Domain ${domainName} found under ${chalk.bold(contextName)} ${chalk.gray(
|
`Domain ${domainName} found under ${chalk.bold(contextName)} ${chalk.gray(
|
||||||
@@ -96,45 +98,25 @@ export default async function inspect(
|
|||||||
output.print('\n');
|
output.print('\n');
|
||||||
output.print(chalk.bold(' General\n\n'));
|
output.print(chalk.bold(' General\n\n'));
|
||||||
output.print(` ${chalk.cyan('Name')}\t\t\t${domain.name}\n`);
|
output.print(` ${chalk.cyan('Name')}\t\t\t${domain.name}\n`);
|
||||||
output.print(` ${chalk.cyan('Service Type')}\t\t${domain.serviceType}\n`);
|
|
||||||
output.print(
|
output.print(
|
||||||
` ${chalk.cyan('Ordered At')}\t\t\t${formatDate(domain.orderedAt)}\n`
|
` ${chalk.cyan('Registrar')}\t\t\t${getDomainRegistrar(domain)}\n`
|
||||||
);
|
);
|
||||||
output.print(
|
output.print(
|
||||||
` ${chalk.cyan('Transfer Started At')}\t\t${formatDate(
|
` ${chalk.cyan('Expiration Date')}\t\t${formatDate(domain.expiresAt)}\n`
|
||||||
domain.transferStartedAt
|
);
|
||||||
)}\n`
|
output.print(
|
||||||
|
` ${chalk.cyan('Creator')}\t\t\t${domain.creator.username}\n`
|
||||||
);
|
);
|
||||||
output.print(
|
output.print(
|
||||||
` ${chalk.cyan('Created At')}\t\t\t${formatDate(domain.createdAt)}\n`
|
` ${chalk.cyan('Created At')}\t\t\t${formatDate(domain.createdAt)}\n`
|
||||||
);
|
);
|
||||||
|
output.print(` ${chalk.cyan('Edge Network')}\t\tyes\n`);
|
||||||
output.print(
|
output.print(
|
||||||
` ${chalk.cyan('Bought At')}\t\t\t${formatDate(domain.boughtAt)}\n`
|
` ${chalk.cyan('Renewal Price')}\t\t${
|
||||||
|
domain.boughtAt && renewalPrice ? `$${renewalPrice} USD` : chalk.gray('-')
|
||||||
|
}\n`
|
||||||
);
|
);
|
||||||
output.print(
|
|
||||||
` ${chalk.cyan('Transferred At')}\t\t${formatDate(
|
|
||||||
domain.transferredAt
|
|
||||||
)}\n`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
` ${chalk.cyan('Expires At')}\t\t\t${formatDate(domain.expiresAt)}\n`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
` ${chalk.cyan('NS Verified At')}\t\t${formatDate(
|
|
||||||
domain.nsVerifiedAt
|
|
||||||
)}\n`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
` ${chalk.cyan('TXT Verified At')}\t\t${formatDate(
|
|
||||||
domain.txtVerifiedAt
|
|
||||||
)}\n`
|
|
||||||
);
|
|
||||||
if (renewalPrice && domain.boughtAt) {
|
|
||||||
output.print(
|
|
||||||
` ${chalk.cyan('Renewal Price')}\t\t$${renewalPrice} USD\n`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
output.print(` ${chalk.cyan('CDN Enabled')}\t\t\t${true}\n`);
|
|
||||||
output.print('\n');
|
output.print('\n');
|
||||||
|
|
||||||
output.print(chalk.bold(' Nameservers\n\n'));
|
output.print(chalk.bold(' Nameservers\n\n'));
|
||||||
@@ -145,38 +127,139 @@ export default async function inspect(
|
|||||||
);
|
);
|
||||||
output.print('\n');
|
output.print('\n');
|
||||||
|
|
||||||
output.print(chalk.bold(' Verification Record\n\n'));
|
if (Array.isArray(projects) && projects.length > 0) {
|
||||||
output.print(
|
output.print(chalk.bold(' Projects\n'));
|
||||||
`${dnsTable([['_now', 'TXT', domain.verificationRecord]], {
|
|
||||||
extraSpace: ' ',
|
const table = formatTable(
|
||||||
})}\n`
|
['Project', 'Domains'],
|
||||||
);
|
['l', 'l'],
|
||||||
output.print('\n');
|
[
|
||||||
|
{
|
||||||
|
rows: projects.map(project => {
|
||||||
|
const name = project.name;
|
||||||
|
|
||||||
|
const domains = (project.alias || [])
|
||||||
|
.map(target => target.domain)
|
||||||
|
.filter(alias => alias.endsWith(domainName));
|
||||||
|
|
||||||
|
const cols = domains.length ? domains.join(', ') : '-';
|
||||||
|
|
||||||
|
return [name, cols];
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
if (!domain.verified) {
|
|
||||||
output.warn(`This domain is not verified. To verify it you should either:`);
|
|
||||||
output.print(
|
output.print(
|
||||||
` ${chalk.gray(
|
table
|
||||||
'a)'
|
.split('\n')
|
||||||
)} Change your domain nameservers to the intended set detailed above. ${chalk.gray(
|
.map(line => ` ${line}`)
|
||||||
'[recommended]'
|
.join('\n')
|
||||||
)}\n`
|
);
|
||||||
|
|
||||||
|
output.print('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domainConfig.misconfigured) {
|
||||||
|
output.warn(
|
||||||
|
`This Domain is not configured properly. To configure it you should either:`,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
boxen: {
|
||||||
|
margin: {
|
||||||
|
left: 2,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
top: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
);
|
);
|
||||||
output.print(
|
output.print(
|
||||||
` ${chalk.gray(
|
` ${chalk.grey('a)')} ` +
|
||||||
'b)'
|
`Set the following record on your DNS provider to continue: ` +
|
||||||
)} Add a DNS TXT record with the name and value shown above.\n\n`
|
`${code(`A ${domainName} 76.76.21.21`)} ` +
|
||||||
|
`${chalk.grey('[recommended]')}\n`
|
||||||
|
);
|
||||||
|
output.print(
|
||||||
|
` ${chalk.grey('b)')} ` +
|
||||||
|
`Change your Domains's nameservers to the intended set detailed above.\n\n`
|
||||||
);
|
);
|
||||||
output.print(
|
output.print(
|
||||||
` We will run a verification for you and you will receive an email upon completion.\n`
|
` We will run a verification for you and you will receive an email upon completion.\n`
|
||||||
);
|
);
|
||||||
output.print(
|
|
||||||
` If you want to force running a verification, you can run ${getCommandName(
|
const contextNameConst = contextName;
|
||||||
`domains verify <domain>`
|
const projectNames = Array.from(
|
||||||
)}\n`
|
new Set(projects.map(project => project.name))
|
||||||
);
|
);
|
||||||
output.print(' Read more: https://err.sh/now/domain-verification\n\n');
|
|
||||||
|
if (projectNames.length) {
|
||||||
|
projectNames.forEach((name, index) => {
|
||||||
|
const prefix = index === 0 ? ' Read more:' : ' '.repeat(12);
|
||||||
|
output.print(
|
||||||
|
`${prefix} https://vercel.com/${contextNameConst}/${name}/settings/domains\n`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
output.print(` Read more: https://vercel.link/domain-configuration\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
output.print('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchInformation({
|
||||||
|
output,
|
||||||
|
client,
|
||||||
|
contextName,
|
||||||
|
domainName,
|
||||||
|
cancelWait,
|
||||||
|
}: {
|
||||||
|
output: Output;
|
||||||
|
client: Client;
|
||||||
|
contextName: string;
|
||||||
|
domainName: string;
|
||||||
|
cancelWait: () => void;
|
||||||
|
}) {
|
||||||
|
const [domain, renewalPrice] = await Promise.all([
|
||||||
|
getDomainByName(client, contextName, domainName, { ignoreWait: true }),
|
||||||
|
getDomainPrice(client, domainName, 'renewal')
|
||||||
|
.then(res => (res instanceof Error ? null : res.price))
|
||||||
|
.catch(() => null),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (domain instanceof DomainNotFound) {
|
||||||
|
cancelWait();
|
||||||
|
output.prettyError(domain);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domain instanceof DomainPermissionDenied) {
|
||||||
|
cancelWait();
|
||||||
|
output.prettyError(domain);
|
||||||
|
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const projects = await findProjectsForDomain(client, domainName);
|
||||||
|
|
||||||
|
if (projects instanceof Error) {
|
||||||
|
cancelWait();
|
||||||
|
output.prettyError(projects);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const domainConfig = await getDomainConfig(client, domainName);
|
||||||
|
|
||||||
|
return {
|
||||||
|
domain,
|
||||||
|
projects,
|
||||||
|
renewalPrice,
|
||||||
|
domainConfig,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
import ms from 'ms';
|
import ms from 'ms';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import table from 'text-table';
|
import plural from 'pluralize';
|
||||||
|
|
||||||
|
import wait from '../../util/output/wait';
|
||||||
import Client from '../../util/client';
|
import Client from '../../util/client';
|
||||||
import getDomains from '../../util/domains/get-domains';
|
import getDomains from '../../util/domains/get-domains';
|
||||||
import getScope from '../../util/get-scope';
|
import getScope from '../../util/get-scope';
|
||||||
import stamp from '../../util/output/stamp';
|
import stamp from '../../util/output/stamp';
|
||||||
import strlen from '../../util/strlen';
|
|
||||||
import { Output } from '../../util/output';
|
import { Output } from '../../util/output';
|
||||||
|
import formatTable from '../../util/format-table';
|
||||||
|
import { formatDateWithoutTime } from '../../util/format-date';
|
||||||
import { Domain, NowContext } from '../../types';
|
import { Domain, NowContext } from '../../types';
|
||||||
import getCommandFlags from '../../util/get-command-flags';
|
import getCommandFlags from '../../util/get-command-flags';
|
||||||
import { getCommandName } from '../../util/pkg-name';
|
import { getCommandName } from '../../util/pkg-name';
|
||||||
|
import isDomainExternal from '../../util/domains/is-domain-external';
|
||||||
|
import { getDomainRegistrar } from '../../util/domains/get-domain-registrar';
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
'--debug': boolean;
|
'--debug': boolean;
|
||||||
@@ -60,22 +64,29 @@ export default async function ls(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { domains, pagination } = await getDomains(
|
const cancelWait = wait(`Fetching Domains under ${chalk.bold(contextName)}`);
|
||||||
client,
|
|
||||||
contextName,
|
const { domains, pagination } = await getDomains(client).finally(() => {
|
||||||
nextTimestamp
|
cancelWait();
|
||||||
);
|
});
|
||||||
|
|
||||||
output.log(
|
output.log(
|
||||||
`Domains found under ${chalk.bold(contextName)} ${chalk.gray(lsStamp())}\n`
|
`${plural('Domain', domains.length, true)} found under ${chalk.bold(
|
||||||
|
contextName
|
||||||
|
)} ${chalk.gray(lsStamp())}`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (domains.length > 0) {
|
if (domains.length > 0) {
|
||||||
console.log(`${formatDomainsTable(domains)}\n`);
|
output.print(
|
||||||
|
formatDomainsTable(domains).replace(/^(.*)/gm, `${' '.repeat(1)}$1`)
|
||||||
|
);
|
||||||
|
output.print('\n\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pagination && pagination.count === 20) {
|
if (pagination && pagination.count === 20) {
|
||||||
const flags = getCommandFlags(opts, ['_', '--next']);
|
const flags = getCommandFlags(opts, ['_', '--next']);
|
||||||
output.log(
|
output.log(
|
||||||
`To display the next page run ${getCommandName(
|
`To display the next page, run ${getCommandName(
|
||||||
`domains ls${flags} --next ${pagination.next}`
|
`domains ls${flags} --next ${pagination.next}`
|
||||||
)}`
|
)}`
|
||||||
);
|
);
|
||||||
@@ -85,27 +96,25 @@ export default async function ls(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function formatDomainsTable(domains: Domain[]) {
|
function formatDomainsTable(domains: Domain[]) {
|
||||||
const current = new Date();
|
const current = Date.now();
|
||||||
return table(
|
|
||||||
[
|
const rows: string[][] = domains.map(domain => {
|
||||||
[
|
const expiration = formatDateWithoutTime(domain.expiresAt);
|
||||||
'',
|
const age = domain.createdAt ? ms(current - domain.createdAt) : '-';
|
||||||
chalk.gray('domain'),
|
|
||||||
chalk.gray('serviceType'),
|
return [
|
||||||
chalk.gray('verified'),
|
domain.name,
|
||||||
chalk.gray('cdn'),
|
getDomainRegistrar(domain),
|
||||||
chalk.gray('age'),
|
isDomainExternal(domain) ? 'Third Party' : 'Vercel',
|
||||||
].map(s => chalk.dim(s)),
|
expiration,
|
||||||
...domains.map(domain => {
|
domain.creator.username,
|
||||||
const url = chalk.bold(domain.name);
|
chalk.gray(age),
|
||||||
const time = chalk.gray(ms(current.getTime() - domain.createdAt));
|
];
|
||||||
return ['', url, domain.serviceType, domain.verified, true, time];
|
});
|
||||||
}),
|
|
||||||
],
|
return formatTable(
|
||||||
{
|
['Domain', 'Registrar', 'Nameservers', 'Expiration Date', 'Creator', 'Age'],
|
||||||
align: ['l', 'l', 'l', 'l', 'l'],
|
['l', 'l', 'l', 'l', 'l', 'l'],
|
||||||
hsep: ' '.repeat(4),
|
[{ rows }]
|
||||||
stringLength: strlen,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import * as ERRORS from '../../util/errors-ts';
|
|||||||
import param from '../../util/output/param';
|
import param from '../../util/output/param';
|
||||||
import promptBool from '../../util/input/prompt-bool';
|
import promptBool from '../../util/input/prompt-bool';
|
||||||
import setCustomSuffix from '../../util/domains/set-custom-suffix';
|
import setCustomSuffix from '../../util/domains/set-custom-suffix';
|
||||||
|
import { findProjectsForDomain } from '../../util/projects/find-projects-for-domain';
|
||||||
import { getCommandName } from '../../util/pkg-name';
|
import { getCommandName } from '../../util/pkg-name';
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
@@ -67,7 +68,7 @@ export default async function rm(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const domain = await getDomainByName(client, contextName, domainName);
|
const domain = await getDomainByName(client, contextName, domainName);
|
||||||
if (domain instanceof DomainNotFound) {
|
if (domain instanceof DomainNotFound || domain.name !== domainName) {
|
||||||
output.error(
|
output.error(
|
||||||
`Domain not found by "${domainName}" under ${chalk.bold(contextName)}`
|
`Domain not found by "${domainName}" under ${chalk.bold(contextName)}`
|
||||||
);
|
);
|
||||||
@@ -85,6 +86,18 @@ export default async function rm(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projects = await findProjectsForDomain(client, domain.name);
|
||||||
|
|
||||||
|
if (Array.isArray(projects) && projects.length > 0) {
|
||||||
|
output.warn(
|
||||||
|
`The domain is currently used by ${plural(
|
||||||
|
'project',
|
||||||
|
projects.length,
|
||||||
|
true
|
||||||
|
)}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const skipConfirmation = opts['--yes'];
|
const skipConfirmation = opts['--yes'];
|
||||||
if (
|
if (
|
||||||
!skipConfirmation &&
|
!skipConfirmation &&
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ export default async function transferIn(
|
|||||||
checkTransfer(client, domainName),
|
checkTransfer(client, domainName),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (domainPrice instanceof ERRORS.UnsupportedTLD) {
|
if (domainPrice instanceof Error) {
|
||||||
output.error(`The TLD for ${param(domainName)} is not supported.`);
|
output.prettyError(domainPrice);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,147 +1,33 @@
|
|||||||
import chalk from 'chalk';
|
|
||||||
import { NowContext } from '../../types';
|
import { NowContext } from '../../types';
|
||||||
import { Output } from '../../util/output';
|
import { Output } from '../../util/output';
|
||||||
import * as ERRORS from '../../util/errors-ts';
|
import { NowBuildError } from '@vercel/build-utils';
|
||||||
import Client from '../../util/client';
|
|
||||||
import formatDnsTable from '../../util/format-dns-table';
|
|
||||||
import formatNSTable from '../../util/format-ns-table';
|
|
||||||
import getDomainByName from '../../util/domains/get-domain-by-name';
|
|
||||||
import getScope from '../../util/get-scope';
|
|
||||||
import stamp from '../../util/output/stamp';
|
|
||||||
import verifyDomain from '../../util/domains/verify-domain';
|
|
||||||
import { getCommandName } from '../../util/pkg-name';
|
import { getCommandName } from '../../util/pkg-name';
|
||||||
|
|
||||||
type Options = {
|
|
||||||
'--debug': boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default async function verify(
|
export default async function verify(
|
||||||
ctx: NowContext,
|
_ctx: NowContext,
|
||||||
opts: Options,
|
_opts: {},
|
||||||
args: string[],
|
args: string[],
|
||||||
output: Output
|
output: Output
|
||||||
) {
|
) {
|
||||||
const {
|
|
||||||
authConfig: { token },
|
|
||||||
config,
|
|
||||||
} = ctx;
|
|
||||||
const { currentTeam } = config;
|
|
||||||
const { apiUrl } = ctx;
|
|
||||||
const debug = opts['--debug'];
|
|
||||||
const client = new Client({ apiUrl, token, currentTeam, debug });
|
|
||||||
|
|
||||||
let contextName = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
({ contextName } = await getScope(client));
|
|
||||||
} catch (err) {
|
|
||||||
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
|
|
||||||
output.error(err.message);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [domainName] = args;
|
const [domainName] = args;
|
||||||
|
|
||||||
if (!domainName) {
|
if (!domainName) {
|
||||||
output.error(
|
output.error(
|
||||||
`${getCommandName(`domains verify <domain>`)} expects one argument`
|
`${getCommandName(`domains verify <domain>`)} expects one argument`
|
||||||
);
|
);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length !== 1) {
|
const error = new NowBuildError({
|
||||||
output.error(
|
code: 'domains_verify_command_deprecated',
|
||||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
message: `It's not necessary to verify Domains anymore. Instead, you can run ${getCommandName(
|
||||||
`${getCommandName('domains verify <domain>')}`
|
`domains inspect ${domainName}`
|
||||||
)}`
|
)} to see what you need to do in order to configure it properly.`,
|
||||||
);
|
link: 'https://vercel.link/domain-verification-via-cli',
|
||||||
return 1;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const domain = await getDomainByName(client, contextName, domainName);
|
output.prettyError(error);
|
||||||
if (domain instanceof ERRORS.DomainNotFound) {
|
|
||||||
output.error(
|
|
||||||
`Domain not found by "${domainName}" under ${chalk.bold(contextName)}`
|
|
||||||
);
|
|
||||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (domain instanceof ERRORS.DomainPermissionDenied) {
|
|
||||||
output.error(
|
|
||||||
`You don't have access to the domain ${domainName} under ${chalk.bold(
|
|
||||||
contextName
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const verifyStamp = stamp();
|
|
||||||
const result = await verifyDomain(client, domain.name, contextName);
|
|
||||||
if (result instanceof ERRORS.DomainVerificationFailed) {
|
|
||||||
const { nsVerification, txtVerification } = result.meta;
|
|
||||||
output.error(
|
|
||||||
`The domain ${
|
|
||||||
domain.name
|
|
||||||
} could not be verified due to the following reasons: ${verifyStamp()}\n`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
` ${chalk.gray(
|
|
||||||
'a)'
|
|
||||||
)} Nameservers verification failed since we see a different set than the intended set:`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
`\n${formatNSTable(
|
|
||||||
nsVerification.intendedNameservers,
|
|
||||||
nsVerification.nameservers,
|
|
||||||
{ extraSpace: ' ' }
|
|
||||||
)}\n\n`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
` ${chalk.gray(
|
|
||||||
'b)'
|
|
||||||
)} DNS TXT verification failed since found no matching records.`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
`\n${formatDnsTable(
|
|
||||||
[['_now', 'TXT', txtVerification.verificationRecord]],
|
|
||||||
{ extraSpace: ' ' }
|
|
||||||
)}\n\n`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
` Once your domain uses either the nameservers or the TXT DNS record from above, run again ${getCommandName(
|
|
||||||
`domains verify <domain>`
|
|
||||||
)}.\n`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
` We will also periodically run a verification check for you and you will receive an email once your domain is verified.\n`
|
|
||||||
);
|
|
||||||
output.print(' Read more: https://err.sh/now/domain-verification\n\n');
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.nsVerifiedAt) {
|
|
||||||
console.log(
|
|
||||||
`${chalk.cyan('> Success!')} Domain ${chalk.bold(
|
|
||||||
domain.name
|
|
||||||
)} was verified using nameservers. ${verifyStamp()}`
|
|
||||||
);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`${chalk.cyan('> Success!')} Domain ${chalk.bold(
|
|
||||||
domain.name
|
|
||||||
)} was verified using DNS TXT record. ${verifyStamp()}`
|
|
||||||
);
|
|
||||||
output.print(
|
|
||||||
` You can verify with nameservers too. Run ${getCommandName(
|
|
||||||
`domains inspect ${domain.name}`
|
|
||||||
)} to find out the intended set.\n`
|
|
||||||
);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
4
packages/now-cli/src/commands/env/index.ts
vendored
4
packages/now-cli/src/commands/env/index.ts
vendored
@@ -124,7 +124,9 @@ export default async function main(ctx: NowContext) {
|
|||||||
return link.exitCode;
|
return link.exitCode;
|
||||||
} else if (link.status === 'not_linked') {
|
} else if (link.status === 'not_linked') {
|
||||||
output.error(
|
output.error(
|
||||||
`Your codebase isn’t linked to a project on Vercel. Run ${getCommandName()} to link it.`
|
`Your codebase isn’t linked to a project on Vercel. Run ${getCommandName(
|
||||||
|
'link'
|
||||||
|
)} to begin.`
|
||||||
);
|
);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
60
packages/now-cli/src/commands/env/pull.ts
vendored
60
packages/now-cli/src/commands/env/pull.ts
vendored
@@ -4,8 +4,7 @@ import { Output } from '../../util/output';
|
|||||||
import promptBool from '../../util/prompt-bool';
|
import promptBool from '../../util/prompt-bool';
|
||||||
import Client from '../../util/client';
|
import Client from '../../util/client';
|
||||||
import stamp from '../../util/output/stamp';
|
import stamp from '../../util/output/stamp';
|
||||||
import getEnvVariables from '../../util/env/get-env-records';
|
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
|
||||||
import getDecryptedSecret from '../../util/env/get-decrypted-secret';
|
|
||||||
import param from '../../util/output/param';
|
import param from '../../util/output/param';
|
||||||
import withSpinner from '../../util/with-spinner';
|
import withSpinner from '../../util/with-spinner';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
@@ -13,6 +12,7 @@ import { promises, openSync, closeSync, readSync } from 'fs';
|
|||||||
import { emoji, prependEmoji } from '../../util/emoji';
|
import { emoji, prependEmoji } from '../../util/emoji';
|
||||||
import { getCommandName } from '../../util/pkg-name';
|
import { getCommandName } from '../../util/pkg-name';
|
||||||
const { writeFile } = promises;
|
const { writeFile } = promises;
|
||||||
|
import { Env } from '@vercel/build-utils';
|
||||||
|
|
||||||
const CONTENTS_PREFIX = '# Created by Vercel CLI\n';
|
const CONTENTS_PREFIX = '# Created by Vercel CLI\n';
|
||||||
|
|
||||||
@@ -84,45 +84,21 @@ export default async function pull(
|
|||||||
);
|
);
|
||||||
const pullStamp = stamp();
|
const pullStamp = stamp();
|
||||||
|
|
||||||
const records = await withSpinner('Downloading', async () => {
|
const records: Env = await withSpinner(
|
||||||
const dev = ProjectEnvTarget.Development;
|
'Downloading',
|
||||||
const envs = await getEnvVariables(output, client, project.id, 4, dev);
|
async () =>
|
||||||
const decryptedValues = await Promise.all(
|
await getDecryptedEnvRecords(
|
||||||
envs.map(async env => {
|
output,
|
||||||
try {
|
client,
|
||||||
const value = await getDecryptedSecret(output, client, env.value);
|
project,
|
||||||
return { value, found: true };
|
ProjectEnvTarget.Development
|
||||||
} catch (error) {
|
)
|
||||||
if (error && error.status === 404) {
|
);
|
||||||
return { value: '', found: false };
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
const results: { key: string; value: string; found: boolean }[] = [];
|
|
||||||
for (let i = 0; i < decryptedValues.length; i++) {
|
|
||||||
const { key } = envs[i];
|
|
||||||
const { value, found } = decryptedValues[i];
|
|
||||||
results.push({ key, value, found });
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
});
|
|
||||||
|
|
||||||
const contents =
|
const contents =
|
||||||
CONTENTS_PREFIX +
|
CONTENTS_PREFIX +
|
||||||
records
|
Object.entries(records)
|
||||||
.filter(obj => {
|
.map(([key, value]) => `${key}="${escapeValue(value)}"`)
|
||||||
if (!obj.found) {
|
|
||||||
output.print('');
|
|
||||||
output.warn(
|
|
||||||
`Unable to download variable ${obj.key} because associated secret was deleted`
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.map(({ key, value }) => `${key}="${escapeValue(value)}"`)
|
|
||||||
.join('\n') +
|
.join('\n') +
|
||||||
'\n';
|
'\n';
|
||||||
|
|
||||||
@@ -139,8 +115,10 @@ export default async function pull(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeValue(value: string) {
|
function escapeValue(value: string | undefined) {
|
||||||
return value
|
return value
|
||||||
.replace(new RegExp('\n', 'g'), '\\n') // combine newlines (unix) into one line
|
? value
|
||||||
.replace(new RegExp('\r', 'g'), '\\r'); // combine newlines (windows) into one line
|
.replace(new RegExp('\n', 'g'), '\\n') // combine newlines (unix) into one line
|
||||||
|
.replace(new RegExp('\r', 'g'), '\\r') // combine newlines (windows) into one line
|
||||||
|
: '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ export default new Map([
|
|||||||
['cert', 'certs'],
|
['cert', 'certs'],
|
||||||
['certs', 'certs'],
|
['certs', 'certs'],
|
||||||
['deploy', 'deploy'],
|
['deploy', 'deploy'],
|
||||||
['deploy-v1', 'deploy'],
|
|
||||||
['deploy-v2', 'deploy'],
|
|
||||||
['dev', 'dev'],
|
['dev', 'dev'],
|
||||||
['dns', 'dns'],
|
['dns', 'dns'],
|
||||||
['domain', 'domains'],
|
['domain', 'domains'],
|
||||||
@@ -17,6 +15,7 @@ export default new Map([
|
|||||||
['help', 'help'],
|
['help', 'help'],
|
||||||
['init', 'init'],
|
['init', 'init'],
|
||||||
['inspect', 'inspect'],
|
['inspect', 'inspect'],
|
||||||
|
['link', 'link'],
|
||||||
['list', 'list'],
|
['list', 'list'],
|
||||||
['ln', 'alias'],
|
['ln', 'alias'],
|
||||||
['log', 'logs'],
|
['log', 'logs'],
|
||||||
@@ -28,7 +27,6 @@ export default new Map([
|
|||||||
['projects', 'projects'],
|
['projects', 'projects'],
|
||||||
['remove', 'remove'],
|
['remove', 'remove'],
|
||||||
['rm', 'remove'],
|
['rm', 'remove'],
|
||||||
['scale', 'scale'],
|
|
||||||
['secret', 'secrets'],
|
['secret', 'secrets'],
|
||||||
['secrets', 'secrets'],
|
['secrets', 'secrets'],
|
||||||
['switch', 'teams'],
|
['switch', 'teams'],
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import table from 'text-table';
|
|
||||||
import getArgs from '../util/get-args';
|
import getArgs from '../util/get-args';
|
||||||
import buildsList from '../util/output/builds';
|
import buildsList from '../util/output/builds';
|
||||||
import routesList from '../util/output/routes';
|
import routesList from '../util/output/routes';
|
||||||
@@ -9,13 +8,10 @@ import Now from '../util';
|
|||||||
import logo from '../util/output/logo';
|
import logo from '../util/output/logo';
|
||||||
import elapsed from '../util/output/elapsed.ts';
|
import elapsed from '../util/output/elapsed.ts';
|
||||||
import { handleError } from '../util/error';
|
import { handleError } from '../util/error';
|
||||||
import strlen from '../util/strlen.ts';
|
|
||||||
import Client from '../util/client.ts';
|
import Client from '../util/client.ts';
|
||||||
import getScope from '../util/get-scope.ts';
|
import getScope from '../util/get-scope.ts';
|
||||||
import { getPkgName, getCommandName } from '../util/pkg-name.ts';
|
import { getPkgName, getCommandName } from '../util/pkg-name.ts';
|
||||||
|
|
||||||
const STATIC = 'STATIC';
|
|
||||||
|
|
||||||
const help = () => {
|
const help = () => {
|
||||||
console.log(`
|
console.log(`
|
||||||
${chalk.bold(`${logo} ${getPkgName()} inspect`)} <url>
|
${chalk.bold(`${logo} ${getPkgName()} inspect`)} <url>
|
||||||
@@ -48,7 +44,6 @@ const help = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default async function main(ctx) {
|
export default async function main(ctx) {
|
||||||
let id;
|
|
||||||
let deployment;
|
let deployment;
|
||||||
let argv;
|
let argv;
|
||||||
|
|
||||||
@@ -70,7 +65,7 @@ export default async function main(ctx) {
|
|||||||
const { print, log, error } = output;
|
const { print, log, error } = output;
|
||||||
|
|
||||||
// extract the first parameter
|
// extract the first parameter
|
||||||
id = argv._[1];
|
const [, deploymentIdOrHost] = argv._;
|
||||||
|
|
||||||
if (argv._.length !== 2) {
|
if (argv._.length !== 2) {
|
||||||
error(`${getCommandName('inspect <url>')} expects exactly one argument`);
|
error(`${getCommandName('inspect <url>')} expects exactly one argument`);
|
||||||
@@ -107,20 +102,24 @@ export default async function main(ctx) {
|
|||||||
// resolve the deployment, since we might have been given an alias
|
// resolve the deployment, since we might have been given an alias
|
||||||
const depFetchStart = Date.now();
|
const depFetchStart = Date.now();
|
||||||
const cancelWait = output.spinner(
|
const cancelWait = output.spinner(
|
||||||
`Fetching deployment "${id}" in ${chalk.bold(contextName)}`
|
`Fetching deployment "${deploymentIdOrHost}" in ${chalk.bold(contextName)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
deployment = await now.findDeployment(id);
|
deployment = await now.findDeployment(deploymentIdOrHost);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
cancelWait();
|
cancelWait();
|
||||||
if (err.status === 404) {
|
if (err.status === 404) {
|
||||||
error(`Failed to find deployment "${id}" in ${chalk.bold(contextName)}`);
|
error(
|
||||||
|
`Failed to find deployment "${deploymentIdOrHost}" in ${chalk.bold(
|
||||||
|
contextName
|
||||||
|
)}`
|
||||||
|
);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (err.status === 403) {
|
if (err.status === 403) {
|
||||||
error(
|
error(
|
||||||
`No permission to access deployment "${id}" in ${chalk.bold(
|
`No permission to access deployment "${deploymentIdOrHost}" in ${chalk.bold(
|
||||||
contextName
|
contextName
|
||||||
)}`
|
)}`
|
||||||
);
|
);
|
||||||
@@ -130,35 +129,12 @@ export default async function main(ctx) {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const { id, name, url, created, routes, readyState } = deployment;
|
||||||
id: finalId,
|
|
||||||
name,
|
|
||||||
state,
|
|
||||||
type,
|
|
||||||
slot,
|
|
||||||
sessionAffinity,
|
|
||||||
url,
|
|
||||||
created,
|
|
||||||
limits,
|
|
||||||
version,
|
|
||||||
routes,
|
|
||||||
readyState,
|
|
||||||
} = deployment;
|
|
||||||
|
|
||||||
const isBuilds = version === 2;
|
const { builds } =
|
||||||
const buildsUrl = `/v1/now/deployments/${finalId}/builds`;
|
deployment.version === 2
|
||||||
|
? await now.fetch(`/v1/now/deployments/${id}/builds`)
|
||||||
const [scale, events, { builds }] = await Promise.all([
|
: { builds: [] };
|
||||||
caught(
|
|
||||||
now.fetch(`/v3/now/deployments/${encodeURIComponent(finalId)}/instances`)
|
|
||||||
),
|
|
||||||
type === STATIC
|
|
||||||
? null
|
|
||||||
: caught(
|
|
||||||
now.fetch(`/v1/now/deployments/${encodeURIComponent(finalId)}/events`)
|
|
||||||
),
|
|
||||||
isBuilds ? now.fetch(buildsUrl) : { builds: [] },
|
|
||||||
]);
|
|
||||||
|
|
||||||
cancelWait();
|
cancelWait();
|
||||||
log(
|
log(
|
||||||
@@ -169,21 +145,9 @@ export default async function main(ctx) {
|
|||||||
|
|
||||||
print('\n');
|
print('\n');
|
||||||
print(chalk.bold(' General\n\n'));
|
print(chalk.bold(' General\n\n'));
|
||||||
print(` ${chalk.cyan('version')}\t${version}\n`);
|
print(` ${chalk.cyan('id')}\t\t${id}\n`);
|
||||||
print(` ${chalk.cyan('id')}\t\t${finalId}\n`);
|
|
||||||
print(` ${chalk.cyan('name')}\t${name}\n`);
|
print(` ${chalk.cyan('name')}\t${name}\n`);
|
||||||
print(
|
print(` ${chalk.cyan('readyState')}\t${stateString(readyState)}\n`);
|
||||||
` ${chalk.cyan('readyState')}\t${stateString(state || readyState)}\n`
|
|
||||||
);
|
|
||||||
if (!isBuilds) {
|
|
||||||
print(` ${chalk.cyan('type')}\t${type}\n`);
|
|
||||||
}
|
|
||||||
if (slot) {
|
|
||||||
print(` ${chalk.cyan('slot')}\t${slot}\n`);
|
|
||||||
}
|
|
||||||
if (sessionAffinity) {
|
|
||||||
print(` ${chalk.cyan('affinity')}\t${sessionAffinity}\n`);
|
|
||||||
}
|
|
||||||
print(` ${chalk.cyan('url')}\t\t${url}\n`);
|
print(` ${chalk.cyan('url')}\t\t${url}\n`);
|
||||||
if (created) {
|
if (created) {
|
||||||
print(
|
print(
|
||||||
@@ -214,95 +178,7 @@ export default async function main(ctx) {
|
|||||||
print(`\n\n`);
|
print(`\n\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (limits) {
|
return 0;
|
||||||
print(chalk.bold(' Limits\n\n'));
|
|
||||||
print(
|
|
||||||
` ${chalk.dim('duration')}\t\t${limits.duration} ${elapsed(
|
|
||||||
limits.duration
|
|
||||||
)}\n`
|
|
||||||
);
|
|
||||||
print(
|
|
||||||
` ${chalk.dim('maxConcurrentReqs')}\t${limits.maxConcurrentReqs}\n`
|
|
||||||
);
|
|
||||||
print(
|
|
||||||
` ${chalk.dim('timeout')}\t\t${limits.timeout} ${elapsed(
|
|
||||||
limits.timeout
|
|
||||||
)}\n`
|
|
||||||
);
|
|
||||||
print('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === STATIC || isBuilds) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
print(chalk.bold(' Scale\n\n'));
|
|
||||||
|
|
||||||
let exitCode = 0;
|
|
||||||
|
|
||||||
if (scale instanceof Error) {
|
|
||||||
error(`Scale information unavailable: ${scale}`);
|
|
||||||
exitCode = 1;
|
|
||||||
} else {
|
|
||||||
const dcs = Object.keys(scale);
|
|
||||||
const t = [['dc', 'min', 'max', 'current'].map(v => chalk.gray(v))];
|
|
||||||
for (const dc of dcs) {
|
|
||||||
const { instances } = scale[dc];
|
|
||||||
const cfg = deployment.scale[dc] || {};
|
|
||||||
t.push([dc, cfg.min || 0, cfg.max || 0, instances.length]);
|
|
||||||
}
|
|
||||||
print(
|
|
||||||
`${table(t, {
|
|
||||||
align: ['l', 'c', 'c', 'c'],
|
|
||||||
hsep: ' '.repeat(8),
|
|
||||||
stringLength: strlen,
|
|
||||||
}).replace(/^(.*)/gm, ' $1')}\n`
|
|
||||||
);
|
|
||||||
print('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
print(chalk.bold(' Events\n\n'));
|
|
||||||
if (events instanceof Error) {
|
|
||||||
error(`Events unavailable: ${scale}`);
|
|
||||||
exitCode = 1;
|
|
||||||
} else if (events) {
|
|
||||||
events.forEach(data => {
|
|
||||||
if (!data.event) return; // keepalive
|
|
||||||
print(
|
|
||||||
` ${chalk.gray(new Date(data.created).toISOString())} ${
|
|
||||||
data.event
|
|
||||||
} ${getEventMetadata(data)}\n`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
print('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
return exitCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// gets the metadata that should be printed next to
|
|
||||||
// each event
|
|
||||||
|
|
||||||
function getEventMetadata({ event, payload }) {
|
|
||||||
if (event === 'state') {
|
|
||||||
return chalk.bold(payload.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event === 'instance-start' || event === 'instance-stop') {
|
|
||||||
if (payload.dc != null) {
|
|
||||||
return chalk.green(`(${payload.dc})`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// makes sure the promise never rejects, exposing the error
|
|
||||||
// as the resolved value instead
|
|
||||||
function caught(p) {
|
|
||||||
return new Promise(r => {
|
|
||||||
p.then(r).catch(r);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// renders the state string
|
// renders the state string
|
||||||
@@ -318,6 +194,6 @@ function stateString(s) {
|
|||||||
return s;
|
return s;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return chalk.gray('UNKNOWN');
|
return chalk.gray(s || 'UNKNOWN');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
98
packages/now-cli/src/commands/link/index.ts
Normal file
98
packages/now-cli/src/commands/link/index.ts
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import chalk from 'chalk';
|
||||||
|
import { NowContext } from '../../types';
|
||||||
|
import createOutput from '../../util/output';
|
||||||
|
import getArgs from '../../util/get-args';
|
||||||
|
import getSubcommand from '../../util/get-subcommand';
|
||||||
|
import handleError from '../../util/handle-error';
|
||||||
|
import logo from '../../util/output/logo';
|
||||||
|
import { getPkgName } from '../../util/pkg-name';
|
||||||
|
import setupAndLink from '../../util/link/setup-and-link';
|
||||||
|
|
||||||
|
const help = () => {
|
||||||
|
console.log(`
|
||||||
|
${chalk.bold(`${logo} ${getPkgName()} link`)} [options]
|
||||||
|
|
||||||
|
${chalk.dim('Options:')}
|
||||||
|
|
||||||
|
-h, --help Output usage information
|
||||||
|
-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]
|
||||||
|
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
||||||
|
'TOKEN'
|
||||||
|
)} Login token
|
||||||
|
--confirm Confirm default options and skip questions
|
||||||
|
|
||||||
|
${chalk.dim('Examples:')}
|
||||||
|
|
||||||
|
${chalk.gray('–')} Link current directory to a Vercel Project
|
||||||
|
|
||||||
|
${chalk.cyan(`$ ${getPkgName()} link`)}
|
||||||
|
|
||||||
|
${chalk.gray(
|
||||||
|
'–'
|
||||||
|
)} Link current directory with default options and skip questions
|
||||||
|
|
||||||
|
${chalk.cyan(`$ ${getPkgName()} link --confirm`)}
|
||||||
|
|
||||||
|
${chalk.gray('–')} Link a specific directory to a Vercel Project
|
||||||
|
|
||||||
|
${chalk.cyan(`$ ${getPkgName()} link /usr/src/project`)}
|
||||||
|
`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const COMMAND_CONFIG = {
|
||||||
|
// No subcommands yet
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function main(ctx: NowContext) {
|
||||||
|
let argv;
|
||||||
|
|
||||||
|
try {
|
||||||
|
argv = getArgs(ctx.argv.slice(2), {
|
||||||
|
'--confirm': Boolean,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv['--help']) {
|
||||||
|
help();
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const debug = argv['--debug'];
|
||||||
|
const output = createOutput({ debug });
|
||||||
|
const { args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
|
||||||
|
const path = args[0] || process.cwd();
|
||||||
|
const autoConfirm = argv['--confirm'];
|
||||||
|
const forceDelete = true;
|
||||||
|
|
||||||
|
const link = await setupAndLink(
|
||||||
|
ctx,
|
||||||
|
output,
|
||||||
|
path,
|
||||||
|
forceDelete,
|
||||||
|
autoConfirm,
|
||||||
|
'success',
|
||||||
|
'Set up'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (link.status === 'error') {
|
||||||
|
return link.exitCode;
|
||||||
|
} else if (link.status === 'not_linked') {
|
||||||
|
// User aborted project linking questions
|
||||||
|
return 0;
|
||||||
|
} else if (link.status === 'linked') {
|
||||||
|
// Successfully linked
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
const err: never = link;
|
||||||
|
throw new Error('Unknown link status: ' + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,7 @@ import chalk from 'chalk';
|
|||||||
import ms from 'ms';
|
import ms from 'ms';
|
||||||
import table from 'text-table';
|
import table from 'text-table';
|
||||||
import Now from '../util';
|
import Now from '../util';
|
||||||
import getAliases from '../util/alias/get-aliases';
|
|
||||||
import getArgs from '../util/get-args';
|
import getArgs from '../util/get-args';
|
||||||
import getDeploymentInstances from '../util/deploy/get-deployment-instances';
|
|
||||||
import createOutput from '../util/output';
|
import createOutput from '../util/output';
|
||||||
import { handleError } from '../util/error';
|
import { handleError } from '../util/error';
|
||||||
import cmd from '../util/output/cmd.ts';
|
import cmd from '../util/output/cmd.ts';
|
||||||
@@ -38,7 +36,6 @@ const help = () => {
|
|||||||
'TOKEN'
|
'TOKEN'
|
||||||
)} Login token
|
)} Login token
|
||||||
-S, --scope Set a custom scope
|
-S, --scope Set a custom scope
|
||||||
-a, --all See all instances for each deployment (requires [app])
|
|
||||||
-m, --meta Filter deployments by metadata (e.g.: ${chalk.dim(
|
-m, --meta Filter deployments by metadata (e.g.: ${chalk.dim(
|
||||||
'`-m KEY=value`'
|
'`-m KEY=value`'
|
||||||
)}). Can appear many times.
|
)}). Can appear many times.
|
||||||
@@ -72,16 +69,12 @@ const help = () => {
|
|||||||
`);
|
`);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Options
|
|
||||||
// $FlowFixMe
|
|
||||||
export default async function main(ctx) {
|
export default async function main(ctx) {
|
||||||
let argv;
|
let argv;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
argv = getArgs(ctx.argv.slice(2), {
|
argv = getArgs(ctx.argv.slice(2), {
|
||||||
'--all': Boolean,
|
|
||||||
'--meta': [String],
|
'--meta': [String],
|
||||||
'-a': '--all',
|
|
||||||
'-m': '--meta',
|
'-m': '--meta',
|
||||||
'--next': Number,
|
'--next': Number,
|
||||||
'-N': '--next',
|
'-N': '--next',
|
||||||
@@ -151,11 +144,6 @@ export default async function main(ctx) {
|
|||||||
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
|
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
|
||||||
const start = new Date();
|
const start = new Date();
|
||||||
|
|
||||||
if (argv['--all'] && !app) {
|
|
||||||
error('You must define an app when using `-a` / `--all`');
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (app && !isValidName(app)) {
|
if (app && !isValidName(app)) {
|
||||||
error(`The provided argument "${app}" is not a valid project name`);
|
error(`The provided argument "${app}" is not a valid project name`);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -222,51 +210,8 @@ export default async function main(ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app && !deployments.length) {
|
|
||||||
debug(
|
|
||||||
'No deployments: attempting to find aliases that matches supplied app name'
|
|
||||||
);
|
|
||||||
const { aliases } = await getAliases(now);
|
|
||||||
const item = aliases.find(e => e.uid === app || e.alias === app);
|
|
||||||
|
|
||||||
if (item) {
|
|
||||||
debug(`Found alias that matches app name: ${item.alias}`);
|
|
||||||
|
|
||||||
if (Array.isArray(item.rules)) {
|
|
||||||
now.close();
|
|
||||||
stopSpinner();
|
|
||||||
log(`Found matching path alias: ${chalk.cyan(item.alias)}`);
|
|
||||||
log(`Please run ${getCommandName(`alias ls ${item.alias}`)} instead`);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const match = await now.findDeployment(item.deploymentId);
|
|
||||||
const instances = await getDeploymentInstances(
|
|
||||||
now,
|
|
||||||
item.deploymentId,
|
|
||||||
'now_cli_alias_instances'
|
|
||||||
);
|
|
||||||
match.instanceCount = Object.keys(instances).reduce(
|
|
||||||
(count, dc) => count + instances[dc].instances.length,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
if (match !== null && typeof match !== 'undefined') {
|
|
||||||
deployments = Array.of(match);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
now.close();
|
now.close();
|
||||||
|
|
||||||
if (argv['--all']) {
|
|
||||||
await Promise.all(
|
|
||||||
deployments.map(async ({ uid, instanceCount }, i) => {
|
|
||||||
deployments[i].instances =
|
|
||||||
instanceCount > 0 ? await now.listInstances(uid) : [];
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (host) {
|
if (host) {
|
||||||
deployments = deployments.filter(deployment => deployment.url === host);
|
deployments = deployments.filter(deployment => deployment.url === host);
|
||||||
}
|
}
|
||||||
@@ -290,12 +235,6 @@ export default async function main(ctx) {
|
|||||||
`${getCommandName('ls [project]')}`
|
`${getCommandName('ls [project]')}`
|
||||||
)}`
|
)}`
|
||||||
);
|
);
|
||||||
} else if (!argv['--all']) {
|
|
||||||
log(
|
|
||||||
`To list deployment instances run ${cmd(
|
|
||||||
`${getCommandName('ls --all [project]')}`
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
print('\n');
|
print('\n');
|
||||||
@@ -316,15 +255,6 @@ export default async function main(ctx) {
|
|||||||
chalk.gray(ms(Date.now() - new Date(dep.createdAt))),
|
chalk.gray(ms(Date.now() - new Date(dep.createdAt))),
|
||||||
dep.creator.username,
|
dep.creator.username,
|
||||||
],
|
],
|
||||||
...(argv['--all']
|
|
||||||
? dep.instances.map(i => [
|
|
||||||
'',
|
|
||||||
` ${chalk.gray('-')} ${i.url} `,
|
|
||||||
'',
|
|
||||||
'',
|
|
||||||
'',
|
|
||||||
])
|
|
||||||
: []),
|
|
||||||
])
|
])
|
||||||
// flatten since the previous step returns a nested
|
// flatten since the previous step returns a nested
|
||||||
// array of the deployment and (optionally) its instances
|
// array of the deployment and (optionally) its instances
|
||||||
|
|||||||
@@ -1,341 +0,0 @@
|
|||||||
import ms from 'ms';
|
|
||||||
import chalk from 'chalk';
|
|
||||||
|
|
||||||
import cmd from '../util/output/cmd.ts';
|
|
||||||
import createOutput from '../util/output';
|
|
||||||
import logo from '../util/output/logo';
|
|
||||||
import stamp from '../util/output/stamp.ts';
|
|
||||||
|
|
||||||
import Now from '../util';
|
|
||||||
import getArgs from '../util/get-args';
|
|
||||||
import Client from '../util/client.ts';
|
|
||||||
import getScope from '../util/get-scope.ts';
|
|
||||||
import getDCsFromArgs from '../util/scale/get-dcs-from-args';
|
|
||||||
import getDeploymentByIdOrHost from '../util/deploy/get-deployment-by-id-or-host';
|
|
||||||
import getDeploymentByIdOrThrow from '../util/deploy/get-deployment-by-id-or-throw';
|
|
||||||
import getMaxFromArgs from '../util/scale/get-max-from-args';
|
|
||||||
import getMinFromArgs from '../util/scale/get-min-from-args';
|
|
||||||
import patchDeploymentScale from '../util/scale/patch-deployment-scale';
|
|
||||||
import waitVerifyDeploymentScale from '../util/scale/wait-verify-deployment-scale';
|
|
||||||
import { handleError } from '../util/error';
|
|
||||||
import {
|
|
||||||
VerifyScaleTimeout,
|
|
||||||
DeploymentTypeUnsupported,
|
|
||||||
} from '../util/errors-ts';
|
|
||||||
import {
|
|
||||||
DeploymentNotFound,
|
|
||||||
DeploymentPermissionDenied,
|
|
||||||
ForbiddenScaleMaxInstances,
|
|
||||||
ForbiddenScaleMinInstances,
|
|
||||||
InvalidArgsForMinMaxScale,
|
|
||||||
InvalidMaxForScale,
|
|
||||||
InvalidMinForScale,
|
|
||||||
InvalidScaleMinMaxRelation,
|
|
||||||
NotSupportedMinScaleSlots,
|
|
||||||
} from '../util/errors-ts';
|
|
||||||
import { InvalidAllForScale, InvalidRegionOrDCForScale } from '../util/errors';
|
|
||||||
import handleCertError from '../util/certs/handle-cert-error';
|
|
||||||
import { getPkgName } from '../util/pkg-name.ts';
|
|
||||||
|
|
||||||
const help = () => {
|
|
||||||
console.log(`
|
|
||||||
${chalk.bold(`${logo} ${getPkgName()} scale`)} <url> <dc> [min] [max]
|
|
||||||
|
|
||||||
${chalk.dim('Options:')}
|
|
||||||
|
|
||||||
-h, --help Output usage information
|
|
||||||
-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
|
|
||||||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
|
||||||
'TOKEN'
|
|
||||||
)} Login token
|
|
||||||
-d, --debug Debug mode [off]
|
|
||||||
-S, --scope Set a custom scope
|
|
||||||
-n, --no-verify Skip step of waiting until instance count meets given constraints
|
|
||||||
-t, --verify-timeout How long to wait for verification to complete [5m]
|
|
||||||
|
|
||||||
${chalk.dim('Examples:')}
|
|
||||||
|
|
||||||
${chalk.gray(
|
|
||||||
'–'
|
|
||||||
)} Enable your deployment in all datacenters (min: 0, max: auto)
|
|
||||||
|
|
||||||
${chalk.cyan(`$ ${getPkgName()} scale my-deployment-123.now.sh all`)}
|
|
||||||
|
|
||||||
${chalk.gray(
|
|
||||||
'-'
|
|
||||||
)} Enable your deployment in the SFO datacenter (min: 0, max: auto)
|
|
||||||
|
|
||||||
${chalk.cyan(`$ ${getPkgName()} scale my-deployment-123.now.sh sfo`)}
|
|
||||||
|
|
||||||
${chalk.gray(
|
|
||||||
'–'
|
|
||||||
)} Scale a deployment in all datacenters to 3 instances at all times (no sleep)
|
|
||||||
|
|
||||||
${chalk.cyan(`$ ${getPkgName()} scale my-deployment-123.now.sh all 3`)}
|
|
||||||
|
|
||||||
${chalk.gray(
|
|
||||||
'–'
|
|
||||||
)} Enable your deployment in all datacenters, with auto-scaling
|
|
||||||
|
|
||||||
${chalk.cyan(`$ ${getPkgName()} scale my-deployment-123.now.sh all auto`)}
|
|
||||||
`);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default async function main(ctx) {
|
|
||||||
let argv;
|
|
||||||
|
|
||||||
try {
|
|
||||||
argv = getArgs(ctx.argv.slice(2), {
|
|
||||||
'--verify-timeout': Number,
|
|
||||||
'--no-verify': Boolean,
|
|
||||||
'-n': '--no-verify',
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
handleError(err);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argv['--help']) {
|
|
||||||
help();
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare the context
|
|
||||||
const {
|
|
||||||
authConfig: { token },
|
|
||||||
config,
|
|
||||||
} = ctx;
|
|
||||||
const { currentTeam } = config;
|
|
||||||
const { apiUrl } = ctx;
|
|
||||||
const debug = argv['--debug'];
|
|
||||||
|
|
||||||
// $FlowFixMe
|
|
||||||
const now = new Now({ apiUrl, token, debug, currentTeam });
|
|
||||||
const output = createOutput({ debug });
|
|
||||||
const client = new Client({ apiUrl, token, currentTeam, debug });
|
|
||||||
let contextName = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
({ contextName } = await getScope(client));
|
|
||||||
} catch (err) {
|
|
||||||
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
|
|
||||||
output.error(err.message);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fail if the user is providing an old command
|
|
||||||
if (argv._[1] === 'ls') {
|
|
||||||
output.error(
|
|
||||||
`${cmd(`${getPkgName()} scale ls`)} has been deprecated. Use ${cmd(
|
|
||||||
'now ls'
|
|
||||||
)} and ${cmd(`${getPkgName()} inspect <url>`)}`
|
|
||||||
);
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the number of arguments is between the allower range
|
|
||||||
if (argv._.length < 3 || argv._.length > 5) {
|
|
||||||
output.error(
|
|
||||||
`${cmd(
|
|
||||||
`${getPkgName()} scale <url> <dc> [min] [max]`
|
|
||||||
)} expects at least two arguments`
|
|
||||||
);
|
|
||||||
help();
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dcs = getDCsFromArgs(argv._);
|
|
||||||
if (dcs instanceof InvalidAllForScale) {
|
|
||||||
output.error(
|
|
||||||
'The region value "all" was used, but it cannot be used alongside other region or dc identifiers'
|
|
||||||
);
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (dcs instanceof InvalidRegionOrDCForScale) {
|
|
||||||
output.error(
|
|
||||||
`The value "${dcs.meta.regionOrDC}" is not a valid region or DC identifier`
|
|
||||||
);
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const min = getMinFromArgs(argv._);
|
|
||||||
if (min instanceof InvalidMinForScale) {
|
|
||||||
output.error(
|
|
||||||
`Invalid <min> parameter "${min.meta.value}". A number or "auto" were expected`
|
|
||||||
);
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const max = getMaxFromArgs(argv._);
|
|
||||||
if (max instanceof InvalidMinForScale) {
|
|
||||||
output.error(
|
|
||||||
`Invalid <min> parameter "${max.meta.value}". A number or "auto" were expected`
|
|
||||||
);
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (max instanceof InvalidArgsForMinMaxScale) {
|
|
||||||
output.error(
|
|
||||||
`Invalid number of arguments: expected <min> ("${max.meta.min}") and [max]`
|
|
||||||
);
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (max instanceof InvalidMaxForScale) {
|
|
||||||
output.error(
|
|
||||||
`Invalid <max> parameter "${max.meta.value}". A number or "auto" were expected`
|
|
||||||
);
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch the deployment
|
|
||||||
const deploymentStamp = stamp();
|
|
||||||
const deployment = handleCertError(
|
|
||||||
output,
|
|
||||||
await getDeploymentByIdOrHost(now, contextName, argv._[1])
|
|
||||||
);
|
|
||||||
|
|
||||||
if (deployment === 1) {
|
|
||||||
return deployment;
|
|
||||||
}
|
|
||||||
if (deployment instanceof DeploymentPermissionDenied) {
|
|
||||||
output.error(
|
|
||||||
`No permission to access deployment ${chalk.dim(
|
|
||||||
deployment.meta.id
|
|
||||||
)} under ${chalk.bold(deployment.meta.context)}`
|
|
||||||
);
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (deployment instanceof DeploymentNotFound) {
|
|
||||||
output.error(
|
|
||||||
`Failed to find deployment "${argv._[1]}" in ${chalk.bold(contextName)}`
|
|
||||||
);
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.log(`Fetched deployment "${deployment.url}" ${deploymentStamp()}`);
|
|
||||||
|
|
||||||
// Make sure the deployment can be scaled
|
|
||||||
if (deployment.type === 'STATIC') {
|
|
||||||
output.error('Scaling rules cannot be set on static deployments');
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (deployment.state === 'ERROR') {
|
|
||||||
output.error('Cannot scale a deployment in the ERROR state');
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (deployment.version === 2) {
|
|
||||||
output.error('Cannot scale a Now 2.0 deployment');
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scaleArgs = dcs.reduce(
|
|
||||||
(result, dc) => ({ ...result, [dc]: { min, max } }),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
output.debug(
|
|
||||||
`Setting scale deployment presets to ${JSON.stringify(scaleArgs)}`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set the deployment scale
|
|
||||||
const scaleStamp = stamp();
|
|
||||||
const result = await patchDeploymentScale(
|
|
||||||
output,
|
|
||||||
now,
|
|
||||||
deployment.uid,
|
|
||||||
scaleArgs,
|
|
||||||
deployment.url
|
|
||||||
);
|
|
||||||
if (result instanceof ForbiddenScaleMinInstances) {
|
|
||||||
output.error(
|
|
||||||
`You can't scale to more than ${result.meta.max} min instances with your current plan.`
|
|
||||||
);
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (result instanceof ForbiddenScaleMaxInstances) {
|
|
||||||
output.error(
|
|
||||||
`You can't scale to more than ${result.meta.max} max instances with your current plan.`
|
|
||||||
);
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (result instanceof InvalidScaleMinMaxRelation) {
|
|
||||||
output.error(`Min number of instances can't be higher than max.`);
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (result instanceof NotSupportedMinScaleSlots) {
|
|
||||||
output.error(
|
|
||||||
`Cloud v2 does not yet support setting a non-zero min number of instances.`
|
|
||||||
);
|
|
||||||
output.log('Read more: https://err.sh/now/v2-no-min');
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (result instanceof DeploymentTypeUnsupported) {
|
|
||||||
output.error(`This region only accepts Serverless Docker Deployments.`);
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`${chalk.gray('>')} Scale rules for ${dcs
|
|
||||||
.map(d => chalk.bold(d))
|
|
||||||
.join(', ')} (min: ${chalk.bold(min)}, max: ${chalk.bold(
|
|
||||||
max
|
|
||||||
)}) saved ${scaleStamp()}`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (argv['--no-verify']) {
|
|
||||||
now.close();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that the scale presets are there
|
|
||||||
const verifyStamp = stamp();
|
|
||||||
const updatedDeployment = await getDeploymentByIdOrThrow(
|
|
||||||
now,
|
|
||||||
contextName,
|
|
||||||
deployment.uid
|
|
||||||
);
|
|
||||||
if (updatedDeployment.type === 'NPM' || updatedDeployment.type === 'DOCKER') {
|
|
||||||
const result = await waitVerifyDeploymentScale(
|
|
||||||
output,
|
|
||||||
now,
|
|
||||||
deployment.uid,
|
|
||||||
updatedDeployment.scale
|
|
||||||
);
|
|
||||||
if (result instanceof VerifyScaleTimeout) {
|
|
||||||
output.error(
|
|
||||||
`Instance verification timed out (${ms(result.meta.timeout)})`,
|
|
||||||
'verification-timeout'
|
|
||||||
);
|
|
||||||
now.close();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
output.success(`Scale state verified ${verifyStamp()}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
now.close();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { email as regexEmail } from '../../util/input/regexes';
|
import { email as regexEmail } from '../../util/input/regexes';
|
||||||
import wait from '../../util/output/wait';
|
import wait from '../../util/output/wait';
|
||||||
import fatalError from '../../util/fatal-error';
|
|
||||||
import cmd from '../../util/output/cmd.ts';
|
import cmd from '../../util/output/cmd.ts';
|
||||||
import info from '../../util/output/info';
|
import info from '../../util/output/info';
|
||||||
import stamp from '../../util/output/stamp.ts';
|
import stamp from '../../util/output/stamp.ts';
|
||||||
@@ -15,6 +14,7 @@ import success from '../../util/output/success';
|
|||||||
import getUser from '../../util/get-user.ts';
|
import getUser from '../../util/get-user.ts';
|
||||||
import Client from '../../util/client.ts';
|
import Client from '../../util/client.ts';
|
||||||
import { getCommandName } from '../../util/pkg-name.ts';
|
import { getCommandName } from '../../util/pkg-name.ts';
|
||||||
|
import createOutput from '../../util/output';
|
||||||
|
|
||||||
const validateEmail = data => regexEmail.test(data.trim()) || data.length === 0;
|
const validateEmail = data => regexEmail.test(data.trim()) || data.length === 0;
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ const emailAutoComplete = (value, teamSlug) => {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function({
|
export default async function ({
|
||||||
teams,
|
teams,
|
||||||
args,
|
args,
|
||||||
config,
|
config,
|
||||||
@@ -66,6 +66,7 @@ export default async function({
|
|||||||
apiUrl,
|
apiUrl,
|
||||||
token,
|
token,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
|
const output = createOutput();
|
||||||
const { currentTeam: currentTeamId } = config;
|
const { currentTeam: currentTeamId } = config;
|
||||||
|
|
||||||
const stopSpinner = wait('Fetching teams');
|
const stopSpinner = wait('Fetching teams');
|
||||||
@@ -100,7 +101,8 @@ export default async function({
|
|||||||
)}.\nPlease select a team scope using ${getCommandName(
|
)}.\nPlease select a team scope using ${getCommandName(
|
||||||
`switch`
|
`switch`
|
||||||
)} or use ${cmd('--scope')}`;
|
)} or use ${cmd('--scope')}`;
|
||||||
return fatalError(err);
|
output.error(err);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
|
|||||||
@@ -13,15 +13,13 @@ try {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
import 'core-js/modules/es7.symbol.async-iterator';
|
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { existsSync, lstatSync } from 'fs';
|
import { existsSync, lstatSync } from 'fs';
|
||||||
import sourceMap from '@zeit/source-map-support';
|
import sourceMap from '@zeit/source-map-support';
|
||||||
import { mkdirp } from 'fs-extra';
|
import { mkdirp } from 'fs-extra';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import epipebomb from 'epipebomb';
|
import epipebomb from 'epipebomb';
|
||||||
import checkForUpdate from 'update-check';
|
import updateNotifier from 'update-notifier';
|
||||||
import ms from 'ms';
|
|
||||||
import { URL } from 'url';
|
import { URL } from 'url';
|
||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from '@sentry/node';
|
||||||
import { NowBuildError } from '@vercel/build-utils';
|
import { NowBuildError } from '@vercel/build-utils';
|
||||||
@@ -52,6 +50,14 @@ import getUpdateCommand from './util/get-update-command';
|
|||||||
import { metrics, shouldCollectMetrics } from './util/metrics.ts';
|
import { metrics, shouldCollectMetrics } from './util/metrics.ts';
|
||||||
import { getCommandName, getTitleName } from './util/pkg-name.ts';
|
import { getCommandName, getTitleName } from './util/pkg-name.ts';
|
||||||
|
|
||||||
|
const isCanary = pkg.version.includes('canary');
|
||||||
|
|
||||||
|
// Checks for available update and returns an instance
|
||||||
|
const notifier = updateNotifier({
|
||||||
|
pkg,
|
||||||
|
distTag: isCanary ? 'canary' : 'latest',
|
||||||
|
});
|
||||||
|
|
||||||
const VERCEL_DIR = getGlobalPathConfig();
|
const VERCEL_DIR = getGlobalPathConfig();
|
||||||
const VERCEL_CONFIG_PATH = configFiles.getConfigFilePath();
|
const VERCEL_CONFIG_PATH = configFiles.getConfigFilePath();
|
||||||
const VERCEL_AUTH_CONFIG_PATH = configFiles.getAuthConfigFilePath();
|
const VERCEL_AUTH_CONFIG_PATH = configFiles.getAuthConfigFilePath();
|
||||||
@@ -66,7 +72,7 @@ sourceMap.install();
|
|||||||
Sentry.init({
|
Sentry.init({
|
||||||
dsn: SENTRY_DSN,
|
dsn: SENTRY_DSN,
|
||||||
release: `vercel-cli@${pkg.version}`,
|
release: `vercel-cli@${pkg.version}`,
|
||||||
environment: pkg.version.includes('canary') ? 'canary' : 'stable',
|
environment: isCanary ? 'canary' : 'stable',
|
||||||
});
|
});
|
||||||
|
|
||||||
let debug = () => {};
|
let debug = () => {};
|
||||||
@@ -128,38 +134,20 @@ const main = async argv_ => {
|
|||||||
// (as in: `vercel ls`)
|
// (as in: `vercel ls`)
|
||||||
const targetOrSubcommand = argv._[2];
|
const targetOrSubcommand = argv._[2];
|
||||||
|
|
||||||
let update = null;
|
if (notifier.update && notifier.update.latest !== pkg.version && isTTY) {
|
||||||
|
const { latest } = notifier.update;
|
||||||
try {
|
|
||||||
if (targetOrSubcommand !== 'update') {
|
|
||||||
update = await checkForUpdate(pkg, {
|
|
||||||
interval: ms('1d'),
|
|
||||||
distTag: pkg.version.includes('canary') ? 'canary' : 'latest',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(
|
|
||||||
error(`Checking for updates failed${isDebugging ? ':' : ''}`)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isDebugging) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (update && isTTY) {
|
|
||||||
console.log(
|
console.log(
|
||||||
info(
|
info(
|
||||||
`${chalk.bgRed('UPDATE AVAILABLE')} ` +
|
`${chalk.bgRed('UPDATE AVAILABLE')} ` +
|
||||||
`Run ${cmd(
|
`Run ${cmd(
|
||||||
await getUpdateCommand()
|
await getUpdateCommand()
|
||||||
)} to install ${getTitleName()} CLI ${update.latest}`
|
)} to install ${getTitleName()} CLI ${latest}`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
info(
|
info(
|
||||||
`Changelog: https://github.com/vercel/vercel/releases/tag/vercel@${update.latest}`
|
`Changelog: https://github.com/vercel/vercel/releases/tag/vercel@${latest}`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -169,7 +157,7 @@ const main = async argv_ => {
|
|||||||
`${getTitleName()} CLI ${pkg.version}${
|
`${getTitleName()} CLI ${pkg.version}${
|
||||||
targetOrSubcommand === 'dev' ? ' dev (beta)' : ''
|
targetOrSubcommand === 'dev' ? ' dev (beta)' : ''
|
||||||
}${
|
}${
|
||||||
pkg.version.includes('canary') || targetOrSubcommand === 'dev'
|
isCanary || targetOrSubcommand === 'dev'
|
||||||
? ' — https://vercel.com/feedback'
|
? ' — https://vercel.com/feedback'
|
||||||
: ''
|
: ''
|
||||||
}`
|
}`
|
||||||
@@ -191,9 +179,7 @@ const main = async argv_ => {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(
|
console.error(
|
||||||
error(
|
error(
|
||||||
`An unexpected error occurred while trying to find the global directory: ${
|
`An unexpected error occurred while trying to find the global directory: ${err.message}`
|
||||||
err.message
|
|
||||||
}`
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -653,6 +639,11 @@ const main = async argv_ => {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
|
||||||
|
output.prettyError(err);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (err instanceof APIError && 400 <= err.status && err.status <= 499) {
|
if (err instanceof APIError && 400 <= err.status && err.status <= 499) {
|
||||||
err.message = err.serverMessage;
|
err.message = err.serverMessage;
|
||||||
output.prettyError(err);
|
output.prettyError(err);
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ export type User = {
|
|||||||
bio?: string;
|
bio?: string;
|
||||||
date: number;
|
date: number;
|
||||||
email: string;
|
email: string;
|
||||||
platformVersion: number;
|
|
||||||
username: string;
|
username: string;
|
||||||
website?: string;
|
website?: string;
|
||||||
billingChecked: boolean;
|
billingChecked: boolean;
|
||||||
@@ -56,7 +55,6 @@ export type Team = {
|
|||||||
creatorId: string;
|
creatorId: string;
|
||||||
membership: { uid: string; role: 'MEMBER' | 'OWNER'; created: number };
|
membership: { uid: string; role: 'MEMBER' | 'OWNER'; created: number };
|
||||||
name: string;
|
name: string;
|
||||||
platformVersion: number;
|
|
||||||
slug: string;
|
slug: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -83,6 +81,16 @@ export type Domain = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DomainConfig = {
|
||||||
|
configuredBy: null | 'CNAME' | 'A' | 'http';
|
||||||
|
misconfigured: boolean;
|
||||||
|
serviceType: 'zeit.world' | 'external' | 'na';
|
||||||
|
nameservers: string[];
|
||||||
|
cnames: string[] & { traceString?: string };
|
||||||
|
aValues: string[] & { traceString?: string };
|
||||||
|
dnssecEnabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type Cert = {
|
export type Cert = {
|
||||||
uid: string;
|
uid: string;
|
||||||
autoRenew: boolean;
|
autoRenew: boolean;
|
||||||
@@ -92,59 +100,23 @@ export type Cert = {
|
|||||||
expiration: string;
|
expiration: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DeploymentScale = {
|
export type Deployment = {
|
||||||
[dc: string]: {
|
|
||||||
min: number;
|
|
||||||
max: number;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type NpmDeployment = {
|
|
||||||
uid: string;
|
uid: string;
|
||||||
url: string;
|
url: string;
|
||||||
name: string;
|
name: string;
|
||||||
type: 'NPM';
|
type: 'LAMBDAS';
|
||||||
state: 'INITIALIZING' | 'FROZEN' | 'READY' | 'ERROR';
|
state:
|
||||||
|
| 'BUILDING'
|
||||||
|
| 'ERROR'
|
||||||
|
| 'INITIALIZING'
|
||||||
|
| 'QUEUED'
|
||||||
|
| 'READY'
|
||||||
|
| 'CANCELED';
|
||||||
version?: number;
|
version?: number;
|
||||||
created: number;
|
created: number;
|
||||||
creator: { uid: string };
|
creator: { uid: string };
|
||||||
sessionAffinity: string;
|
|
||||||
scale: DeploymentScale;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StaticDeployment = {
|
|
||||||
uid: string;
|
|
||||||
url: string;
|
|
||||||
name: string;
|
|
||||||
type: 'STATIC';
|
|
||||||
state: 'INITIALIZING' | 'FROZEN' | 'READY' | 'ERROR';
|
|
||||||
version?: number;
|
|
||||||
created: number;
|
|
||||||
creator: { uid: string };
|
|
||||||
sessionAffinity: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type DockerDeployment = {
|
|
||||||
uid: string;
|
|
||||||
url: string;
|
|
||||||
name: string;
|
|
||||||
type: 'DOCKER';
|
|
||||||
state: 'INITIALIZING' | 'FROZEN' | 'READY' | 'ERROR';
|
|
||||||
version?: number;
|
|
||||||
created: number;
|
|
||||||
creator: { uid: string };
|
|
||||||
sessionAffinity: string;
|
|
||||||
scale: DeploymentScale;
|
|
||||||
limits?: {
|
|
||||||
maxConcurrentReqs: number;
|
|
||||||
timeout: number;
|
|
||||||
duration: number;
|
|
||||||
};
|
|
||||||
slot?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Deployment = NpmDeployment | StaticDeployment | DockerDeployment;
|
|
||||||
|
|
||||||
type PathAliasRule = {
|
type PathAliasRule = {
|
||||||
pathname: string;
|
pathname: string;
|
||||||
method: Array<'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'>;
|
method: Array<'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'>;
|
||||||
@@ -217,6 +189,16 @@ export type DNSRecordData =
|
|||||||
| SRVRecordData
|
| SRVRecordData
|
||||||
| MXRecordData;
|
| MXRecordData;
|
||||||
|
|
||||||
|
export interface ProjectAliasTarget {
|
||||||
|
createdAt?: number;
|
||||||
|
domain: string;
|
||||||
|
redirect?: string | null;
|
||||||
|
target: 'PRODUCTION' | 'STAGING';
|
||||||
|
configuredBy?: null | 'CNAME' | 'A';
|
||||||
|
configuredChangedAt?: null | number;
|
||||||
|
configuredChangeAttempts?: [number, number];
|
||||||
|
}
|
||||||
|
|
||||||
export interface Secret {
|
export interface Secret {
|
||||||
uid: string;
|
uid: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -258,6 +240,10 @@ export interface Project extends ProjectSettings {
|
|||||||
accountId: string;
|
accountId: string;
|
||||||
updatedAt: number;
|
updatedAt: number;
|
||||||
createdAt: number;
|
createdAt: number;
|
||||||
|
alias?: ProjectAliasTarget[];
|
||||||
|
devCommand?: string | null;
|
||||||
|
framework?: string | null;
|
||||||
|
rootDirectory?: string | null;
|
||||||
latestDeployments?: Partial<Deployment>[];
|
latestDeployments?: Partial<Deployment>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,3 +263,8 @@ export interface PaginationOptions {
|
|||||||
count: number;
|
count: number;
|
||||||
next?: number;
|
next?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ProjectLinkResult =
|
||||||
|
| { status: 'linked'; org: Org; project: Project }
|
||||||
|
| { status: 'not_linked'; org: null; project: null }
|
||||||
|
| { status: 'error'; exitCode: number };
|
||||||
|
|||||||
@@ -1,89 +1,20 @@
|
|||||||
import { Deployment } from '../../types';
|
import { Deployment } from '../../types';
|
||||||
import { Output } from '../output';
|
import { Output } from '../output';
|
||||||
import * as ERRORS from '../errors-ts';
|
|
||||||
import Client from '../client';
|
import Client from '../client';
|
||||||
import createAlias from './create-alias';
|
import createAlias from './create-alias';
|
||||||
import deploymentShouldCopyScale from './deployment-should-copy-scale';
|
|
||||||
import deploymentShouldDownscale from './deployment-should-downscale';
|
|
||||||
import findAliasByAliasOrId from './find-alias-by-alias-or-id';
|
|
||||||
import getDeploymentDownscalePresets from './get-deployment-downscale-presets';
|
|
||||||
import getDeploymentFromAlias from './get-deployment-from-alias';
|
|
||||||
import isDomainExternal from '../domains/is-domain-external';
|
import isDomainExternal from '../domains/is-domain-external';
|
||||||
import setDeploymentScale from '../scale/set-deployment-scale';
|
|
||||||
import setupDomain from '../domains/setup-domain';
|
import setupDomain from '../domains/setup-domain';
|
||||||
import stamp from '../output/stamp';
|
|
||||||
import waitForScale from '../scale/wait-verify-deployment-scale';
|
|
||||||
|
|
||||||
export default async function assignAlias(
|
export default async function assignAlias(
|
||||||
output: Output,
|
output: Output,
|
||||||
client: Client,
|
client: Client,
|
||||||
deployment: Deployment,
|
deployment: Deployment,
|
||||||
alias: string,
|
alias: string,
|
||||||
contextName: string,
|
contextName: string
|
||||||
noVerify: boolean
|
|
||||||
) {
|
) {
|
||||||
const prevAlias = await findAliasByAliasOrId(output, client, alias);
|
|
||||||
let externalDomain = false;
|
let externalDomain = false;
|
||||||
|
|
||||||
// If there was a previous deployment, we should fetch it to scale and downscale later
|
// Check if the alias is a custom domain, because then
|
||||||
let prevDeployment = await getDeploymentFromAlias(
|
|
||||||
client,
|
|
||||||
contextName,
|
|
||||||
prevAlias,
|
|
||||||
deployment
|
|
||||||
);
|
|
||||||
|
|
||||||
// If there is an alias laying around that points to a deleted
|
|
||||||
// deployment, we need to account for it here.
|
|
||||||
if (prevDeployment instanceof ERRORS.DeploymentNotFound) {
|
|
||||||
prevDeployment = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevDeployment instanceof Error) {
|
|
||||||
return prevDeployment;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there was a prev deployment that wasn't static we have to check if we should scale
|
|
||||||
if (
|
|
||||||
prevDeployment !== null &&
|
|
||||||
prevDeployment.type !== 'STATIC' &&
|
|
||||||
deployment.type !== 'STATIC'
|
|
||||||
) {
|
|
||||||
if (deploymentShouldCopyScale(prevDeployment, deployment)) {
|
|
||||||
const scaleStamp = stamp();
|
|
||||||
const result = await setDeploymentScale(
|
|
||||||
output,
|
|
||||||
client,
|
|
||||||
deployment.uid,
|
|
||||||
prevDeployment.scale,
|
|
||||||
deployment.url
|
|
||||||
);
|
|
||||||
if (result instanceof Error) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.log(
|
|
||||||
`Scale rules copied from previous alias ${
|
|
||||||
prevDeployment.url
|
|
||||||
} ${scaleStamp()}`
|
|
||||||
);
|
|
||||||
if (!noVerify) {
|
|
||||||
const result = await waitForScale(
|
|
||||||
output,
|
|
||||||
client,
|
|
||||||
deployment.uid,
|
|
||||||
prevDeployment.scale
|
|
||||||
);
|
|
||||||
if (result instanceof ERRORS.VerifyScaleTimeout) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
output.debug(`Both deployments have the same scaling rules.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the alias is a custom domain and if case we have a positive
|
|
||||||
// we have to configure the DNS records and certificate
|
// we have to configure the DNS records and certificate
|
||||||
if (
|
if (
|
||||||
alias.indexOf('.') !== -1 &&
|
alias.indexOf('.') !== -1 &&
|
||||||
@@ -109,23 +40,6 @@ export default async function assignAlias(
|
|||||||
alias,
|
alias,
|
||||||
externalDomain
|
externalDomain
|
||||||
);
|
);
|
||||||
if (record instanceof Error) {
|
|
||||||
return record;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Downscale if the previous deployment is not static and doesn't have the minimal presets
|
|
||||||
if (prevDeployment !== null && prevDeployment.type !== 'STATIC') {
|
|
||||||
if (await deploymentShouldDownscale(output, client, prevDeployment)) {
|
|
||||||
await setDeploymentScale(
|
|
||||||
output,
|
|
||||||
client,
|
|
||||||
prevDeployment.uid,
|
|
||||||
getDeploymentDownscalePresets(prevDeployment),
|
|
||||||
prevDeployment.url
|
|
||||||
);
|
|
||||||
output.log(`Previous deployment ${prevDeployment.url} downscaled`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import { Deployment } from '../../types';
|
|
||||||
import Client from '../client';
|
|
||||||
import getAliases from './get-aliases';
|
|
||||||
|
|
||||||
export default async function deploymentIsAliased(
|
|
||||||
client: Client,
|
|
||||||
deployment: Deployment
|
|
||||||
) {
|
|
||||||
const { aliases } = await getAliases(client);
|
|
||||||
return aliases.some(alias => alias.deploymentId === deployment.uid);
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user