mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-29 11:49:14 +00:00
Compare commits
3 Commits
@vercel/ne
...
loopless-3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc49bc335b | ||
|
|
58d3502f5b | ||
|
|
29775f8e05 |
@@ -19,11 +19,6 @@ indent_style = space
|
||||
[*.py]
|
||||
indent_size = 4
|
||||
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
|
||||
[*.asm]
|
||||
indent_size = 8
|
||||
|
||||
|
||||
@@ -34,6 +34,3 @@ packages/now-node-bridge/bridge.*
|
||||
|
||||
# now-static-build
|
||||
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
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- uses: styfle/cancel-workflow-action@0.4.1
|
||||
- uses: styfle/cancel-workflow-action@0.3.2
|
||||
with:
|
||||
workflow_id: 849295, 849296, 849297, 849298
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
@@ -2,14 +2,16 @@
|
||||
|
||||
#### Why This Error Occurred
|
||||
|
||||
The domain you supplied cannot be verified using the intended nameservers.
|
||||
The domain you supplied cannot be verified using either the intended set of nameservers or the given verification TXT record.
|
||||
|
||||
#### Possible Way to Fix It
|
||||
#### Possible Ways to Fix It
|
||||
|
||||
Apply the intended set of nameservers to your domain.
|
||||
Apply the intended set of nameservers to your domain or add the given TXT verification record through your domain provider.
|
||||
|
||||
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.
|
||||
|
||||
If you would not like to verify your domain, you can remove it from your account using `vercel domains rm <domain>`.
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
# 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
|
||||
@@ -1,7 +0,0 @@
|
||||
# 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
10
examples/redwoodjs/.gitignore
vendored
@@ -1,10 +0,0 @@
|
||||
.DS_Store
|
||||
.env
|
||||
.netlify
|
||||
dev.db
|
||||
dist
|
||||
dist-babel
|
||||
node_modules
|
||||
yarn-error.log
|
||||
|
||||
.vercel
|
||||
@@ -1 +0,0 @@
|
||||
lts/*
|
||||
@@ -1,21 +0,0 @@
|
||||
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.
|
||||
@@ -1,29 +0,0 @@
|
||||

|
||||
|
||||
# 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 +0,0 @@
|
||||
module.exports = { extends: '../babel.config.js' }
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"src/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": "api",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@redwoodjs/api": "0.15.0"
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
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?
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/* 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()
|
||||
})
|
||||
@@ -1,19 +0,0 @@
|
||||
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,
|
||||
})
|
||||
@@ -1,6 +0,0 @@
|
||||
// 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()
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
presets: ['@redwoodjs/core/config/babel-preset'],
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"private": true,
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"api",
|
||||
"web"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@redwoodjs/core": "0.15.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "@redwoodjs/eslint-config"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12",
|
||||
"yarn": ">=1.15"
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// https://prettier.io/docs/en/options.html
|
||||
module.exports = {
|
||||
trailingComma: 'es5',
|
||||
bracketSpacing: true,
|
||||
tabWidth: 2,
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
arrowParens: 'always',
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
[web]
|
||||
port = 8910
|
||||
apiProxyPath = "/api"
|
||||
[api]
|
||||
port = 8911
|
||||
[browser]
|
||||
open = false
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = { extends: '../babel.config.js' }
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"src/*": ["./src/*"]
|
||||
},
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
# 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).
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,2 +0,0 @@
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -1,22 +0,0 @@
|
||||
// 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
|
||||
@@ -1,12 +0,0 @@
|
||||
<!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>
|
||||
@@ -1,16 +0,0 @@
|
||||
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')
|
||||
)
|
||||
@@ -1,44 +0,0 @@
|
||||
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>
|
||||
)
|
||||
@@ -1,53 +0,0 @@
|
||||
// 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>
|
||||
)
|
||||
@@ -1,44 +0,0 @@
|
||||
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>
|
||||
)
|
||||
@@ -1,44 +0,0 @@
|
||||
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>
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,17 +2,17 @@
|
||||
"name": "svelte-app",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^13.0.0",
|
||||
"@rollup/plugin-node-resolve": "^8.1.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"rollup": "^2.18.0",
|
||||
"rollup": "^1.10.1",
|
||||
"rollup-plugin-commonjs": "^9.3.4",
|
||||
"rollup-plugin-livereload": "^1.0.0",
|
||||
"rollup-plugin-node-resolve": "^4.2.3",
|
||||
"rollup-plugin-svelte": "^5.0.3",
|
||||
"rollup-plugin-terser": "^6.1.0",
|
||||
"rollup-plugin-terser": "^4.0.4",
|
||||
"svelte": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"sirv-cli": "^1.0.1"
|
||||
"sirv-cli": "^0.4.4"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import svelte from 'rollup-plugin-svelte';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import livereload from 'rollup-plugin-livereload';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
|
||||
|
||||
@@ -682,34 +682,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"detectors": {
|
||||
"every": [
|
||||
{
|
||||
"path": "package.json",
|
||||
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@redwoodjs\\/core\":\\s*\".+?\"[^}]*}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"value": "yarn rw db up --no-db-client --auto-approve && yarn rw build"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "yarn rw dev --fwd=\"--port=$PORT --open=false\""
|
||||
},
|
||||
"outputDirectory": {
|
||||
"value": "RedwoodJS default"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Hugo",
|
||||
"slug": "hugo",
|
||||
@@ -832,7 +804,7 @@
|
||||
"description": "No framework or a unoptimized framework.",
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run vercel-build` or `npm run build`"
|
||||
"placeholder": "`npm run now-build` or `npm run build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"placeholder": "None"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 3.0 KiB |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/frameworks",
|
||||
"version": "0.0.18-canary.3",
|
||||
"version": "0.0.17",
|
||||
"main": "frameworks.json",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "2.4.3-canary.2",
|
||||
"version": "2.4.2",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -309,13 +309,6 @@ export async function detectBuilders(
|
||||
options
|
||||
);
|
||||
|
||||
if (frontendBuilder && framework === 'redwoodjs') {
|
||||
// RedwoodJS uses the /api directory differently so we must
|
||||
// clear any existing builders and only use `@vercel/redwood`.
|
||||
builders.length = 0;
|
||||
builders.push(frontendBuilder);
|
||||
}
|
||||
|
||||
return {
|
||||
warnings,
|
||||
builders: builders.length ? builders : null,
|
||||
@@ -475,10 +468,6 @@ function detectFrontBuilder(
|
||||
return { src: 'package.json', use: `@vercel/next${withTag}`, config };
|
||||
}
|
||||
|
||||
if (framework === 'redwoodjs') {
|
||||
return { src: 'package.json', use: `@vercel/redwood${withTag}`, config };
|
||||
}
|
||||
|
||||
// Entrypoints for other frameworks
|
||||
// TODO - What if just a build script is provided, but no entrypoint.
|
||||
const entrypoints = new Set([
|
||||
|
||||
@@ -15,9 +15,7 @@ async function readFileOrNull(file: string) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function readConfigFile<T>(
|
||||
files: string | string[]
|
||||
): Promise<T | null> {
|
||||
export async function readConfigFile<T>(files: string | string[]) {
|
||||
files = Array.isArray(files) ? files : [files];
|
||||
|
||||
for (const name of files) {
|
||||
@@ -26,11 +24,11 @@ export async function readConfigFile<T>(
|
||||
if (data) {
|
||||
const str = data.toString('utf8');
|
||||
if (name.endsWith('.json')) {
|
||||
return JSON.parse(str) as T;
|
||||
return JSON.parse(str);
|
||||
} else if (name.endsWith('.toml')) {
|
||||
return (toml.parse(str) as unknown) as T;
|
||||
} else if (name.endsWith('.yaml') || name.endsWith('.yml')) {
|
||||
return yaml.safeLoad(str, { filename: name }) as T;
|
||||
return yaml.safeLoad(str, { filename: name });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1808,36 +1808,6 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('RedwoodJS should only use redwood builder', async () => {
|
||||
const files = [
|
||||
'package.json',
|
||||
'web/index.html',
|
||||
'api/one.js',
|
||||
'api/two.js',
|
||||
];
|
||||
const projectSettings = {
|
||||
framework: 'redwoodjs',
|
||||
};
|
||||
|
||||
const { builders, errorRoutes } = await detectBuilders(files, null, {
|
||||
projectSettings,
|
||||
featHandleMiss,
|
||||
});
|
||||
|
||||
expect(builders).toEqual([
|
||||
{
|
||||
use: '@vercel/redwood',
|
||||
src: 'package.json',
|
||||
config: {
|
||||
zeroConfig: true,
|
||||
framework: 'redwoodjs',
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(errorRoutes!.length).toBe(1);
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('No framework, only package.json', async () => {
|
||||
const files = ['package.json'];
|
||||
const pkg = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "20.0.0-canary.13",
|
||||
"version": "19.2.0",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -62,14 +62,13 @@
|
||||
"node": ">= 10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.4.3-canary.2",
|
||||
"@vercel/go": "1.1.5-canary.0",
|
||||
"@vercel/next": "2.6.19",
|
||||
"@vercel/node": "1.7.5-canary.1",
|
||||
"@vercel/build-utils": "2.4.2",
|
||||
"@vercel/go": "1.1.4",
|
||||
"@vercel/next": "2.6.13",
|
||||
"@vercel/node": "1.7.3",
|
||||
"@vercel/python": "1.2.2",
|
||||
"@vercel/redwood": "0.0.2-canary.3",
|
||||
"@vercel/ruby": "1.2.3",
|
||||
"@vercel/static-build": "0.17.7-canary.1",
|
||||
"@vercel/static-build": "0.17.6",
|
||||
"update-notifier": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -121,7 +120,7 @@
|
||||
"chalk": "2.4.2",
|
||||
"chokidar": "3.3.1",
|
||||
"clipboardy": "2.1.0",
|
||||
"codecov": "3.7.1",
|
||||
"codecov": "3.6.5",
|
||||
"cpy": "7.2.0",
|
||||
"credit-card": "3.0.1",
|
||||
"date-fns": "1.29.0",
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as ERRORS from '../../util/errors-ts';
|
||||
import { Output } from '../../util/output';
|
||||
import deleteCertById from '../../util/certs/delete-cert-by-id';
|
||||
import getCertById from '../../util/certs/get-cert-by-id';
|
||||
import { getCustomCertsForDomain } from '../../util/certs/get-custom-certs-for-domain';
|
||||
import getCertsForDomain from '../../util/certs/get-certs-for-domain';
|
||||
import Client from '../../util/client';
|
||||
import getScope from '../../util/get-scope';
|
||||
import stamp from '../../util/output/stamp';
|
||||
@@ -66,17 +66,9 @@ async function rm(
|
||||
}
|
||||
|
||||
if (certs.length === 0) {
|
||||
if (id.includes('.')) {
|
||||
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)}`
|
||||
);
|
||||
}
|
||||
output.error(
|
||||
`No certificates found by id "${id}" under ${chalk.bold(contextName)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -109,7 +101,7 @@ async function getCertsToDelete(
|
||||
) {
|
||||
const cert = await getCertById(client, id);
|
||||
if (cert instanceof ERRORS.CertNotFound) {
|
||||
const certs = await getCustomCertsForDomain(client, contextName, id);
|
||||
const certs = await getCertsForDomain(output, client, contextName, id);
|
||||
if (certs instanceof ERRORS.CertsPermissionDenied) {
|
||||
return certs;
|
||||
}
|
||||
@@ -133,7 +125,12 @@ function readConfirmation(output: Output, msg: string, certs: Cert[]) {
|
||||
process.stdin
|
||||
.on('data', d => {
|
||||
process.stdin.pause();
|
||||
resolve(d.toString().trim().toLowerCase() === 'y');
|
||||
resolve(
|
||||
d
|
||||
.toString()
|
||||
.trim()
|
||||
.toLowerCase() === 'y'
|
||||
);
|
||||
})
|
||||
.resume();
|
||||
});
|
||||
|
||||
@@ -669,7 +669,7 @@ export default async function main(
|
||||
}
|
||||
|
||||
if (err instanceof BuildError) {
|
||||
output.error(err.message || 'Build failed');
|
||||
output.error('Build failed');
|
||||
output.error(
|
||||
`Check your logs at https://${now.url}/_logs or run ${getCommandName(
|
||||
`logs ${now.url}`,
|
||||
|
||||
@@ -8,15 +8,14 @@ import Client from '../../util/client';
|
||||
import { getLinkedProject } from '../../util/projects/link';
|
||||
import { getFrameworks } from '../../util/get-frameworks';
|
||||
import { isSettingValue } from '../../util/is-setting-value';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import { ProjectSettings, ProjectEnvTarget } 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 = {
|
||||
'--debug'?: boolean;
|
||||
'--listen'?: string;
|
||||
'--confirm': boolean;
|
||||
};
|
||||
|
||||
export default async function dev(
|
||||
@@ -38,35 +37,22 @@ export default async function dev(
|
||||
});
|
||||
|
||||
// retrieve dev command
|
||||
let [link, frameworks] = await Promise.all([
|
||||
const [link, frameworks] = await Promise.all([
|
||||
getLinkedProject(output, client, cwd),
|
||||
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') {
|
||||
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 frameworkSlug: string | undefined;
|
||||
let projectSettings: ProjectSettings | undefined;
|
||||
|
||||
@@ -32,7 +32,6 @@ const help = () => {
|
||||
-d, --debug Debug mode [off]
|
||||
-l, --listen [uri] Specify a URI endpoint on which to listen [0.0.0.0:3000]
|
||||
-t, --token [token] Specify an Authorization Token
|
||||
--confirm Skip questions and use defaults when setting up a new project
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
@@ -57,7 +56,6 @@ export default async function main(ctx: NowContext) {
|
||||
argv = getArgs(ctx.argv.slice(2), {
|
||||
'--listen': String,
|
||||
'-l': '--listen',
|
||||
'--confirm': Boolean,
|
||||
|
||||
// Deprecated
|
||||
'--port': Number,
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
import chalk from 'chalk';
|
||||
import psl from 'psl';
|
||||
|
||||
import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import addDomain from '../../util/domains/add-domain';
|
||||
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 getScope from '../../util/get-scope';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import { getCommandName } 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';
|
||||
import param from '../../util/output/param';
|
||||
import { getCommandName, getTitleName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
'--cdn': boolean;
|
||||
'--debug': boolean;
|
||||
'--force': boolean;
|
||||
'--no-cdn': boolean;
|
||||
};
|
||||
|
||||
export default async function add(
|
||||
@@ -34,7 +33,6 @@ export default async function add(
|
||||
const { currentTeam } = config;
|
||||
const { apiUrl } = ctx;
|
||||
const debug = opts['--debug'];
|
||||
const force = opts['--force'];
|
||||
const client = new Client({ apiUrl, token, currentTeam, debug });
|
||||
let contextName = null;
|
||||
|
||||
@@ -49,116 +47,105 @@ export default async function add(
|
||||
throw err;
|
||||
}
|
||||
|
||||
const project = await getLinkedProject(output, client).then(result => {
|
||||
if (result.status === 'linked') {
|
||||
return result.project;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
if (project && args.length !== 1) {
|
||||
output.error(
|
||||
`${getCommandName('domains add <domain>')} expects one argument.`
|
||||
);
|
||||
if (opts['--cdn'] !== undefined || opts['--no-cdn'] !== undefined) {
|
||||
output.error(`Toggling CF from ${getTitleName()} CLI is deprecated.`);
|
||||
return 1;
|
||||
} else if (!project && args.length !== 2) {
|
||||
}
|
||||
|
||||
if (args.length !== 1) {
|
||||
output.error(
|
||||
`${getCommandName(
|
||||
'domains add <domain> <project>'
|
||||
)} expects two arguments.`
|
||||
`${getCommandName('domains add <domain>')} expects one argument`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const domainName = String(args[0]);
|
||||
const projectName = project ? project.name : String(args[1]);
|
||||
const parsedDomain = psl.parse(domainName);
|
||||
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 addedDomain = await addDomain(client, domainName, contextName);
|
||||
|
||||
let aliasTarget = await addDomainToProject(client, projectName, domainName);
|
||||
if (addedDomain instanceof ERRORS.InvalidDomain) {
|
||||
output.error(
|
||||
`The provided domain name "${addedDomain.meta.domain}" is invalid`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (aliasTarget instanceof Error) {
|
||||
if (
|
||||
aliasTarget instanceof ERRORS.APIError &&
|
||||
aliasTarget.code === 'ALIAS_DOMAIN_EXIST' &&
|
||||
aliasTarget.project &&
|
||||
aliasTarget.project.id
|
||||
) {
|
||||
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;
|
||||
}
|
||||
if (addedDomain instanceof ERRORS.DomainAlreadyExists) {
|
||||
output.error(
|
||||
`The domain ${chalk.underline(
|
||||
addedDomain.meta.domain
|
||||
)} is already registered by a different account.\n` +
|
||||
` If this seems like a mistake, please contact us at support@vercel.com`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// We can cast the information because we've just added the domain and it should be there
|
||||
console.log(
|
||||
`${chalk.cyan('> Success!')} Domain ${chalk.bold(
|
||||
domainName
|
||||
)} added to project ${chalk.bold(projectName)}. ${addStamp()}`
|
||||
addedDomain.name
|
||||
)} added correctly. ${addStamp()}\n`
|
||||
);
|
||||
|
||||
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) {
|
||||
if (!addedDomain.verified) {
|
||||
output.warn(
|
||||
`This domain is not configured properly. To configure it you should either:`
|
||||
`The domain was added but it is not verified. To verify it, you should either:`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.grey('a)')} ` +
|
||||
`Set the following record on your DNS provider to continue: ` +
|
||||
`${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`
|
||||
` ${chalk.gray(
|
||||
'a)'
|
||||
)} Change your domain nameservers to the following intended set: ${chalk.gray(
|
||||
'[recommended]'
|
||||
)}\n`
|
||||
);
|
||||
output.print(
|
||||
`\n${formatNSTable(
|
||||
domainResponse.intendedNameservers,
|
||||
domainResponse.nameservers,
|
||||
addedDomain.intendedNameservers,
|
||||
addedDomain.nameservers,
|
||||
{ extraSpace: ' ' }
|
||||
)}\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(
|
||||
` We will run a verification for you and you will receive an email upon completion.\n`
|
||||
);
|
||||
output.print(' Read more: https://vercel.link/domain-configuration\n\n');
|
||||
} else {
|
||||
output.log(
|
||||
`The domain will automatically get assigned to your latest production deployment.`
|
||||
output.print(
|
||||
` If you want to force running a verification, you can run ${cmd(
|
||||
`${getCommandName('domains verify <domain>')}`
|
||||
)}\n`
|
||||
);
|
||||
output.print(' Read more: https://err.sh/now/domain-verification\n\n');
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -71,9 +71,8 @@ export default async function buy(
|
||||
|
||||
const availableStamp = stamp();
|
||||
const domainPrice = await getDomainPrice(client, domainName);
|
||||
|
||||
if (domainPrice instanceof Error) {
|
||||
output.prettyError(domainPrice);
|
||||
if (domainPrice instanceof ERRORS.UnsupportedTLD) {
|
||||
output.error(`The TLD for ${param(domainName)} is not supported.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ import transferIn from './transfer-in';
|
||||
import inspect from './inspect';
|
||||
import ls from './ls';
|
||||
import rm from './rm';
|
||||
import move from './move';
|
||||
import verify from './verify';
|
||||
import move from './move';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
const help = () => {
|
||||
@@ -25,17 +25,17 @@ const help = () => {
|
||||
|
||||
ls Show all domains in a list
|
||||
inspect [name] Displays information related to a domain
|
||||
add [name] [project] Add a new domain that you already own
|
||||
add [name] Add a new domain that you already own
|
||||
rm [name] Remove a domain
|
||||
buy [name] Buy a domain that you don't yet own
|
||||
move [name] [destination] Move a domain to another user or team.
|
||||
transfer-in [name] Transfer in a domain to Vercel
|
||||
verify [name] Run a verification for a domain
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-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(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
@@ -90,9 +90,10 @@ export default async function main(ctx: NowContext) {
|
||||
|
||||
try {
|
||||
argv = getArgs(ctx.argv.slice(2), {
|
||||
'--cdn': Boolean,
|
||||
'--code': String,
|
||||
'--no-cdn': Boolean,
|
||||
'--yes': Boolean,
|
||||
'--force': Boolean,
|
||||
'--next': Number,
|
||||
'-N': '--next',
|
||||
});
|
||||
|
||||
@@ -4,18 +4,13 @@ import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import Client from '../../util/client';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import dnsTable from '../../util/format-dns-table';
|
||||
import formatDate from '../../util/format-date';
|
||||
import formatNSTable from '../../util/format-ns-table';
|
||||
import getDomainByName from '../../util/domains/get-domain-by-name';
|
||||
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 { 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 = {
|
||||
'--debug': boolean;
|
||||
@@ -69,26 +64,29 @@ export default async function inspect(
|
||||
}
|
||||
|
||||
output.debug(`Fetching domain info`);
|
||||
|
||||
const cancelWait = wait(
|
||||
`Fetching domain ${domainName} under ${chalk.bold(contextName)}`
|
||||
);
|
||||
|
||||
const information = await fetchInformation({
|
||||
output,
|
||||
client,
|
||||
contextName,
|
||||
domainName,
|
||||
cancelWait,
|
||||
}).finally(() => {
|
||||
cancelWait();
|
||||
});
|
||||
|
||||
if (typeof information === 'number') {
|
||||
return information;
|
||||
const [domain, renewalPrice] = await Promise.all([
|
||||
getDomainByName(client, contextName, domainName),
|
||||
getDomainPrice(client, domainName, 'renewal')
|
||||
.then(res => (res instanceof Error ? null : res.price))
|
||||
.catch(() => null),
|
||||
]);
|
||||
if (domain instanceof DomainNotFound) {
|
||||
output.error(
|
||||
`Domain not found by "${domainName}" under ${chalk.bold(contextName)}`
|
||||
);
|
||||
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const { domain, projects, renewalPrice, domainConfig } = information;
|
||||
if (domain instanceof 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;
|
||||
}
|
||||
|
||||
output.log(
|
||||
`Domain ${domainName} found under ${chalk.bold(contextName)} ${chalk.gray(
|
||||
@@ -98,27 +96,45 @@ export default async function inspect(
|
||||
output.print('\n');
|
||||
output.print(chalk.bold(' General\n\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(
|
||||
` ${chalk.cyan('Registrar')}\t\t\t${getDomainRegistrar(domain)}\n`
|
||||
` ${chalk.cyan('Ordered At')}\t\t\t${formatDate(domain.orderedAt)}\n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan('Transfer Started At')}\t\t${formatDate(
|
||||
domain.transferStartedAt
|
||||
)}\n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan('Created At')}\t\t\t${formatDate(domain.createdAt)}\n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan('Bought At')}\t\t\t${formatDate(domain.boughtAt)}\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('Edge Network')}\t\t${true}\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('Creator')}\t\t\t${domain.creator.username}\n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.cyan('Created At')}\t\t\t${formatDate(domain.createdAt)}\n`
|
||||
);
|
||||
|
||||
output.print(` ${chalk.cyan('CDN Enabled')}\t\t\t${true}\n`);
|
||||
output.print('\n');
|
||||
|
||||
output.print(chalk.bold(' Nameservers\n\n'));
|
||||
@@ -129,139 +145,38 @@ export default async function inspect(
|
||||
);
|
||||
output.print('\n');
|
||||
|
||||
if (Array.isArray(projects) && projects.length > 0) {
|
||||
output.print(chalk.bold(' Projects\n'));
|
||||
|
||||
const table = formatTable(
|
||||
['Project', 'Domains'],
|
||||
['l', 'l'],
|
||||
[
|
||||
{
|
||||
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];
|
||||
}),
|
||||
},
|
||||
]
|
||||
);
|
||||
output.print(chalk.bold(' Verification Record\n\n'));
|
||||
output.print(
|
||||
`${dnsTable([['_now', 'TXT', domain.verificationRecord]], {
|
||||
extraSpace: ' ',
|
||||
})}\n`
|
||||
);
|
||||
output.print('\n');
|
||||
|
||||
if (!domain.verified) {
|
||||
output.warn(`This domain is not verified. To verify it you should either:`);
|
||||
output.print(
|
||||
table
|
||||
.split('\n')
|
||||
.map(line => ` ${line}`)
|
||||
.join('\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,
|
||||
},
|
||||
},
|
||||
}
|
||||
` ${chalk.gray(
|
||||
'a)'
|
||||
)} Change your domain nameservers to the intended set detailed above. ${chalk.gray(
|
||||
'[recommended]'
|
||||
)}\n`
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.grey('a)')} ` +
|
||||
`Set the following record on your DNS provider to continue: ` +
|
||||
`${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`
|
||||
` ${chalk.gray(
|
||||
'b)'
|
||||
)} Add a DNS TXT record with the name and value shown above.\n\n`
|
||||
);
|
||||
output.print(
|
||||
` We will run a verification for you and you will receive an email upon completion.\n`
|
||||
);
|
||||
|
||||
const contextNameConst = contextName;
|
||||
const projectNames = Array.from(
|
||||
new Set(projects.map(project => project.name))
|
||||
output.print(
|
||||
` If you want to force running a verification, you can run ${getCommandName(
|
||||
`domains verify <domain>`
|
||||
)}\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');
|
||||
output.print(' Read more: https://err.sh/now/domain-verification\n\n');
|
||||
}
|
||||
|
||||
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,20 +1,16 @@
|
||||
import ms from 'ms';
|
||||
import chalk from 'chalk';
|
||||
import plural from 'pluralize';
|
||||
import table from 'text-table';
|
||||
|
||||
import wait from '../../util/output/wait';
|
||||
import Client from '../../util/client';
|
||||
import getDomains from '../../util/domains/get-domains';
|
||||
import getScope from '../../util/get-scope';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import strlen from '../../util/strlen';
|
||||
import { Output } from '../../util/output';
|
||||
import formatTable from '../../util/format-table';
|
||||
import { formatDateWithoutTime } from '../../util/format-date';
|
||||
import { Domain, NowContext } from '../../types';
|
||||
import getCommandFlags from '../../util/get-command-flags';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import isDomainExternal from '../../util/domains/is-domain-external';
|
||||
import { getDomainRegistrar } from '../../util/domains/get-domain-registrar';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -64,29 +60,22 @@ export default async function ls(
|
||||
return 1;
|
||||
}
|
||||
|
||||
const cancelWait = wait(`Fetching domains under ${chalk.bold(contextName)}`);
|
||||
|
||||
const { domains, pagination } = await getDomains(client).finally(() => {
|
||||
cancelWait();
|
||||
});
|
||||
|
||||
output.log(
|
||||
`${plural('Domain', domains.length, true)} found under ${chalk.bold(
|
||||
contextName
|
||||
)} ${chalk.gray(lsStamp())}`
|
||||
const { domains, pagination } = await getDomains(
|
||||
client,
|
||||
contextName,
|
||||
nextTimestamp
|
||||
);
|
||||
output.log(
|
||||
`Domains found under ${chalk.bold(contextName)} ${chalk.gray(lsStamp())}\n`
|
||||
);
|
||||
|
||||
if (domains.length > 0) {
|
||||
output.print(
|
||||
formatDomainsTable(domains).replace(/^(.*)/gm, `${' '.repeat(1)}$1`)
|
||||
);
|
||||
output.print('\n\n');
|
||||
console.log(`${formatDomainsTable(domains)}\n`);
|
||||
}
|
||||
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(opts, ['_', '--next']);
|
||||
output.log(
|
||||
`To display the next page, run ${getCommandName(
|
||||
`To display the next page run ${getCommandName(
|
||||
`domains ls${flags} --next ${pagination.next}`
|
||||
)}`
|
||||
);
|
||||
@@ -96,25 +85,27 @@ export default async function ls(
|
||||
}
|
||||
|
||||
function formatDomainsTable(domains: Domain[]) {
|
||||
const current = Date.now();
|
||||
|
||||
const rows: string[][] = domains.map(domain => {
|
||||
const expiration = formatDateWithoutTime(domain.expiresAt);
|
||||
const age = domain.createdAt ? ms(current - domain.createdAt) : '-';
|
||||
|
||||
return [
|
||||
domain.name,
|
||||
getDomainRegistrar(domain),
|
||||
isDomainExternal(domain) ? 'Third Party' : 'Vercel',
|
||||
expiration,
|
||||
domain.creator.username,
|
||||
chalk.gray(age),
|
||||
];
|
||||
});
|
||||
|
||||
return formatTable(
|
||||
['Domain', 'Registrar', 'Nameservers', 'Expiration', 'Creator', 'Age'],
|
||||
['l', 'l', 'l', 'l', 'l', 'l'],
|
||||
[{ rows }]
|
||||
const current = new Date();
|
||||
return table(
|
||||
[
|
||||
[
|
||||
'',
|
||||
chalk.gray('domain'),
|
||||
chalk.gray('serviceType'),
|
||||
chalk.gray('verified'),
|
||||
chalk.gray('cdn'),
|
||||
chalk.gray('age'),
|
||||
].map(s => chalk.dim(s)),
|
||||
...domains.map(domain => {
|
||||
const url = chalk.bold(domain.name);
|
||||
const time = chalk.gray(ms(current.getTime() - domain.createdAt));
|
||||
return ['', url, domain.serviceType, domain.verified, true, time];
|
||||
}),
|
||||
],
|
||||
{
|
||||
align: ['l', 'l', 'l', 'l', 'l'],
|
||||
hsep: ' '.repeat(4),
|
||||
stringLength: strlen,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import * as ERRORS from '../../util/errors-ts';
|
||||
import param from '../../util/output/param';
|
||||
import promptBool from '../../util/input/prompt-bool';
|
||||
import setCustomSuffix from '../../util/domains/set-custom-suffix';
|
||||
import { findProjectsForDomain } from '../../util/projects/find-projects-for-domain';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
type Options = {
|
||||
@@ -68,7 +67,7 @@ export default async function rm(
|
||||
}
|
||||
|
||||
const domain = await getDomainByName(client, contextName, domainName);
|
||||
if (domain instanceof DomainNotFound || domain.name !== domainName) {
|
||||
if (domain instanceof DomainNotFound) {
|
||||
output.error(
|
||||
`Domain not found by "${domainName}" under ${chalk.bold(contextName)}`
|
||||
);
|
||||
@@ -86,18 +85,6 @@ export default async function rm(
|
||||
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'];
|
||||
if (
|
||||
!skipConfirmation &&
|
||||
|
||||
@@ -71,8 +71,8 @@ export default async function transferIn(
|
||||
checkTransfer(client, domainName),
|
||||
]);
|
||||
|
||||
if (domainPrice instanceof Error) {
|
||||
output.prettyError(domainPrice);
|
||||
if (domainPrice instanceof ERRORS.UnsupportedTLD) {
|
||||
output.error(`The TLD for ${param(domainName)} is not supported.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,33 +1,147 @@
|
||||
import chalk from 'chalk';
|
||||
import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import { NowBuildError } from '@vercel/build-utils';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
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';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
};
|
||||
|
||||
export default async function verify(
|
||||
_ctx: NowContext,
|
||||
_opts: {},
|
||||
ctx: NowContext,
|
||||
opts: Options,
|
||||
args: string[],
|
||||
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;
|
||||
|
||||
if (!domainName) {
|
||||
output.error(
|
||||
`${getCommandName(`domains verify <domain>`)} expects one argument`
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const error = new NowBuildError({
|
||||
code: 'domains_verify_command_deprecated',
|
||||
message: `It's not necessary to verify Domains anymore. Instead, you can run ${getCommandName(
|
||||
`domains inspect ${domainName}`
|
||||
)} to see what you need to do in order to configure it properly.`,
|
||||
link: 'https://vercel.link/domain-verification-via-cli',
|
||||
});
|
||||
if (args.length !== 1) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${chalk.cyan(
|
||||
`${getCommandName('domains verify <domain>')}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
output.prettyError(error);
|
||||
const domain = await getDomainByName(client, contextName, domainName);
|
||||
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;
|
||||
}
|
||||
|
||||
4
packages/now-cli/src/commands/env/index.ts
vendored
4
packages/now-cli/src/commands/env/index.ts
vendored
@@ -124,9 +124,7 @@ export default async function main(ctx: NowContext) {
|
||||
return link.exitCode;
|
||||
} else if (link.status === 'not_linked') {
|
||||
output.error(
|
||||
`Your codebase isn’t linked to a project on Vercel. Run ${getCommandName(
|
||||
'link'
|
||||
)} to begin.`
|
||||
`Your codebase isn’t linked to a project on Vercel. Run ${getCommandName()} to link it.`
|
||||
);
|
||||
return 1;
|
||||
} else {
|
||||
|
||||
@@ -17,7 +17,6 @@ export default new Map([
|
||||
['help', 'help'],
|
||||
['init', 'init'],
|
||||
['inspect', 'inspect'],
|
||||
['link', 'link'],
|
||||
['list', 'list'],
|
||||
['ln', 'alias'],
|
||||
['log', 'logs'],
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -640,11 +640,6 @@ const main = async argv_ => {
|
||||
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) {
|
||||
err.message = err.serverMessage;
|
||||
output.prettyError(err);
|
||||
|
||||
@@ -83,16 +83,6 @@ 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 = {
|
||||
uid: string;
|
||||
autoRenew: boolean;
|
||||
@@ -227,16 +217,6 @@ export type DNSRecordData =
|
||||
| SRVRecordData
|
||||
| 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 {
|
||||
uid: string;
|
||||
name: string;
|
||||
@@ -278,10 +258,6 @@ export interface Project extends ProjectSettings {
|
||||
accountId: string;
|
||||
updatedAt: number;
|
||||
createdAt: number;
|
||||
alias?: ProjectAliasTarget[];
|
||||
devCommand?: string | null;
|
||||
framework?: string | null;
|
||||
rootDirectory?: string | null;
|
||||
latestDeployments?: Partial<Deployment>[];
|
||||
}
|
||||
|
||||
@@ -301,8 +277,3 @@ export interface PaginationOptions {
|
||||
count: number;
|
||||
next?: number;
|
||||
}
|
||||
|
||||
export type ProjectLinkResult =
|
||||
| { status: 'linked'; org: Org; project: Project }
|
||||
| { status: 'not_linked'; org: null; project: null }
|
||||
| { status: 'error'; exitCode: number };
|
||||
|
||||
@@ -6,7 +6,7 @@ export default async function deleteCertById(
|
||||
client: Client,
|
||||
id: string
|
||||
) {
|
||||
return client.fetch(`/v5/now/certs/${id}`, {
|
||||
method: 'DELETE',
|
||||
return client.fetch(`/v3/now/certs/${id}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as ERRORS from '../errors-ts';
|
||||
|
||||
export default async function getCertById(client: Client, id: string) {
|
||||
try {
|
||||
return await client.fetch<Cert>(`/v5/now/certs/${id}`);
|
||||
return await client.fetch<Cert>(`/v3/now/certs/${id}`);
|
||||
} catch (error) {
|
||||
if (error.code === 'cert_not_found') {
|
||||
return new ERRORS.CertNotFound(id);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { stringify } from 'querystring';
|
||||
import { Cert } from '../../types';
|
||||
import { Output } from '../output';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
import Client from '../client';
|
||||
|
||||
@@ -7,14 +8,15 @@ type Response = {
|
||||
certs: Cert[];
|
||||
};
|
||||
|
||||
export async function getCustomCertsForDomain(
|
||||
export default async function getCertsForDomain(
|
||||
output: Output,
|
||||
client: Client,
|
||||
context: string,
|
||||
domain: string
|
||||
) {
|
||||
try {
|
||||
const { certs } = await client.fetch<Response>(
|
||||
`/v5/now/certs?${stringify({ domain, custom: true })}`
|
||||
`/v3/now/certs?${stringify({ domain })}`
|
||||
);
|
||||
return certs;
|
||||
} catch (error) {
|
||||
@@ -118,16 +118,7 @@ export async function devRouter(
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the destination is an external URL (rewrite or redirect)
|
||||
const isDestUrl = isURL(destPath);
|
||||
|
||||
if (
|
||||
routeConfig.check &&
|
||||
devServer &&
|
||||
nowConfig &&
|
||||
phase !== 'hit' &&
|
||||
!isDestUrl
|
||||
) {
|
||||
if (routeConfig.check && devServer && nowConfig && phase !== 'hit') {
|
||||
const { pathname = '/' } = url.parse(destPath);
|
||||
const hasDestFile = await devServer.hasFilesystem(
|
||||
pathname,
|
||||
@@ -165,6 +156,7 @@ export async function devRouter(
|
||||
}
|
||||
}
|
||||
|
||||
const isDestUrl = isURL(destPath);
|
||||
if (isDestUrl) {
|
||||
result = {
|
||||
found: true,
|
||||
|
||||
@@ -1512,9 +1512,9 @@ export default class DevServer {
|
||||
const { dest, headers, uri_args } = routeResult;
|
||||
|
||||
// Set any headers defined in the matched `route` config
|
||||
for (const [name, value] of Object.entries(headers)) {
|
||||
Object.entries(headers).forEach(([name, value]) => {
|
||||
res.setHeader(name, value);
|
||||
}
|
||||
});
|
||||
|
||||
if (statusCode) {
|
||||
// Set the `statusCode` as read-only so that `http-proxy`
|
||||
@@ -1537,13 +1537,6 @@ export default class DevServer {
|
||||
if (this.devProcessPort) {
|
||||
const upstream = `http://localhost:${this.devProcessPort}`;
|
||||
debug(`Proxying to frontend dev server: ${upstream}`);
|
||||
|
||||
// Add the Vercel platform proxy request headers
|
||||
const headers = this.getNowProxyHeaders(req, nowRequestId, false);
|
||||
for (const [name, value] of Object.entries(headers)) {
|
||||
req.headers[name] = value;
|
||||
}
|
||||
|
||||
this.setResponseHeaders(res, nowRequestId);
|
||||
const origUrl = url.parse(req.url || '/', true);
|
||||
delete origUrl.search;
|
||||
@@ -1578,13 +1571,6 @@ export default class DevServer {
|
||||
debug(
|
||||
`Checking build result's ${buildResult.routes.length} \`routes\` to match ${newUrl}`
|
||||
);
|
||||
for (const r of buildResult.routes) {
|
||||
// This replace is necessary for `@vercel/redwood` but might be relevant
|
||||
// for builders that wish to return routes and work with zero config.
|
||||
if (r.dest) {
|
||||
r.dest = r.dest.replace(/\$PORT/g, `${this.devProcessPort}`);
|
||||
}
|
||||
}
|
||||
const matchedRoute = await devRouter(
|
||||
newUrl,
|
||||
req.method,
|
||||
@@ -1706,13 +1692,6 @@ export default class DevServer {
|
||||
(!foundAsset || (foundAsset && foundAsset.asset.type !== 'Lambda'))
|
||||
) {
|
||||
debug('Proxying to frontend dev server');
|
||||
|
||||
// Add the Vercel platform proxy request headers
|
||||
const headers = this.getNowProxyHeaders(req, nowRequestId, false);
|
||||
for (const [name, value] of Object.entries(headers)) {
|
||||
req.headers[name] = value;
|
||||
}
|
||||
|
||||
this.setResponseHeaders(res, nowRequestId);
|
||||
return proxyPass(
|
||||
req,
|
||||
|
||||
@@ -4,8 +4,6 @@ import { Output } from '../output';
|
||||
import Client from '../client';
|
||||
import getDomainDNSRecords from './get-domain-dns-records';
|
||||
import getDomains from '../domains/get-domains';
|
||||
import wait from '../output/wait';
|
||||
import chalk from 'chalk';
|
||||
|
||||
export type DomainRecordsItem = {
|
||||
domainName: string;
|
||||
@@ -60,11 +58,6 @@ async function getDomainNames(
|
||||
contextName: string,
|
||||
next?: number
|
||||
) {
|
||||
const cancelWait = wait(`Fetching domains under ${chalk.bold(contextName)}`);
|
||||
try {
|
||||
const { domains, pagination } = await getDomains(client, next);
|
||||
return { domainNames: domains.map(domain => domain.name), pagination };
|
||||
} finally {
|
||||
cancelWait();
|
||||
}
|
||||
const { domains, pagination } = await getDomains(client, contextName, next);
|
||||
return { domainNames: domains.map(domain => domain.name), pagination };
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export default async function importZonefile(
|
||||
} catch (error) {
|
||||
cancelWait();
|
||||
if (error.code === 'not_found') {
|
||||
return new DomainNotFound(domain, contextName);
|
||||
return new DomainNotFound(domain);
|
||||
}
|
||||
|
||||
if (error.code === 'invalid_domain') {
|
||||
|
||||
@@ -8,26 +8,24 @@ type Response = {
|
||||
domain: Domain;
|
||||
};
|
||||
|
||||
export default async function getDomainByName(
|
||||
async function getDomainByName(
|
||||
client: Client,
|
||||
contextName: string,
|
||||
domainName: string,
|
||||
options: {
|
||||
ignoreWait?: boolean;
|
||||
} = {}
|
||||
domainName: string
|
||||
) {
|
||||
const cancelWait = options.ignoreWait
|
||||
? null
|
||||
: wait(`Fetching domain ${domainName} under ${chalk.bold(contextName)}`);
|
||||
const cancelWait = wait(
|
||||
`Fetching domain ${domainName} under ${chalk.bold(contextName)}`
|
||||
);
|
||||
try {
|
||||
const { domain } = await client.fetch<Response>(
|
||||
`/v4/domains/${encodeURIComponent(domainName)}`
|
||||
);
|
||||
|
||||
cancelWait();
|
||||
return domain;
|
||||
} catch (error) {
|
||||
cancelWait();
|
||||
if (error.status === 404) {
|
||||
return new DomainNotFound(domainName, contextName);
|
||||
return new DomainNotFound(domainName);
|
||||
}
|
||||
|
||||
if (error.status === 403) {
|
||||
@@ -35,7 +33,7 @@ export default async function getDomainByName(
|
||||
}
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
cancelWait?.();
|
||||
}
|
||||
}
|
||||
|
||||
export default getDomainByName;
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import Client from '../client';
|
||||
import { DomainConfig } from '../../types';
|
||||
|
||||
export async function getDomainConfig(client: Client, domainName: string) {
|
||||
try {
|
||||
const config = await client.fetch<DomainConfig>(
|
||||
`/v4/domains/${domainName}/config`
|
||||
);
|
||||
|
||||
return config;
|
||||
} catch (error) {
|
||||
if (error.status < 500) {
|
||||
return error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -19,11 +19,6 @@ export default async function getDomainPrice(
|
||||
if (error.code === 'unsupported_tld') {
|
||||
return new UnsupportedTLD(name);
|
||||
}
|
||||
|
||||
if (error.status < 500) {
|
||||
return error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { Domain } from '../../types';
|
||||
|
||||
export type DomainRegistrar = 'Vercel' | 'Purchase in Process' | 'Third Party';
|
||||
|
||||
export function getDomainRegistrar(domain: Domain): DomainRegistrar {
|
||||
if (domain.boughtAt) {
|
||||
return 'Vercel';
|
||||
}
|
||||
|
||||
if (typeof domain.orderedAt === 'number' && !domain.boughtAt) {
|
||||
return 'Purchase in Process';
|
||||
}
|
||||
|
||||
return 'Third Party';
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import chalk from 'chalk';
|
||||
import Client from '../client';
|
||||
import wait from '../output/wait';
|
||||
import { Domain } from '../../types';
|
||||
|
||||
type Response = {
|
||||
domain: Domain;
|
||||
};
|
||||
|
||||
export async function getDomain(
|
||||
client: Client,
|
||||
contextName: string,
|
||||
domainName: string
|
||||
) {
|
||||
const cancelWait = wait(
|
||||
`Fetching domain ${domainName} under ${chalk.bold(contextName)}`
|
||||
);
|
||||
try {
|
||||
const { domain } = await client.fetch<Response>(
|
||||
`/v4/domains/${domainName}`
|
||||
);
|
||||
|
||||
return domain;
|
||||
} catch (error) {
|
||||
if (error.status < 500) {
|
||||
return error;
|
||||
}
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
cancelWait();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,24 @@
|
||||
import chalk from 'chalk';
|
||||
import { Domain, PaginationOptions } from '../../types';
|
||||
import Client from '../client';
|
||||
import wait from '../output/wait';
|
||||
|
||||
type Response = {
|
||||
domains: Domain[];
|
||||
pagination: PaginationOptions;
|
||||
};
|
||||
|
||||
export default async function getDomains(client: Client, next?: number) {
|
||||
export default async function getDomains(
|
||||
client: Client,
|
||||
contextName: string,
|
||||
next?: number
|
||||
) {
|
||||
let domainUrl = `/v5/domains?limit=20`;
|
||||
if (next) {
|
||||
domainUrl += `&until=${next}`;
|
||||
}
|
||||
return await client.fetch<Response>(domainUrl);
|
||||
const cancelWait = wait(`Fetching domains under ${chalk.bold(contextName)}`);
|
||||
const domains = await client.fetch<Response>(domainUrl);
|
||||
cancelWait();
|
||||
return domains;
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export function isPublicSuffix(domainName: string) {
|
||||
return domainName.endsWith('.vercel.app') || domainName.endsWith('.now.sh');
|
||||
}
|
||||
@@ -30,19 +30,12 @@ export default async function purchaseDomainIfAvailable(
|
||||
}
|
||||
|
||||
output.debug(`Domain ${domain} is available to be purchased`);
|
||||
|
||||
const domainPrice = await getDomainPrice(client, domain).finally(() => {
|
||||
cancelWait();
|
||||
});
|
||||
|
||||
const domainPrice = await getDomainPrice(client, domain);
|
||||
cancelWait();
|
||||
if (domainPrice instanceof ERRORS.UnsupportedTLD) {
|
||||
return domainPrice;
|
||||
}
|
||||
|
||||
if (domainPrice instanceof Error) {
|
||||
throw domainPrice;
|
||||
}
|
||||
|
||||
const { price, period } = domainPrice;
|
||||
output.log(
|
||||
`Domain not found, but you can buy it under ${chalk.bold(
|
||||
@@ -75,5 +68,6 @@ export default async function purchaseDomainIfAvailable(
|
||||
}
|
||||
|
||||
output.debug(`Domain ${domain} is not available to be purchased`);
|
||||
cancelWait();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { NowBuildError } from '@vercel/build-utils';
|
||||
import { NowError } from './now-error';
|
||||
import code from './output/code';
|
||||
import { getCommandName } from './pkg-name';
|
||||
import chalk from 'chalk';
|
||||
|
||||
/**
|
||||
* This error is thrown when there is an API error with a payload. The error
|
||||
@@ -69,9 +68,7 @@ export class InvalidToken extends NowError<'NOT_AUTHORIZED', {}> {
|
||||
constructor() {
|
||||
super({
|
||||
code: `NOT_AUTHORIZED`,
|
||||
message: `The specified token is not valid. Use ${getCommandName(
|
||||
`login`
|
||||
)} to generate a new token.`,
|
||||
message: `The specified token is not valid`,
|
||||
meta: {},
|
||||
});
|
||||
}
|
||||
@@ -186,13 +183,11 @@ export class DomainNotFound extends NowError<
|
||||
'DOMAIN_NOT_FOUND',
|
||||
{ domain: string }
|
||||
> {
|
||||
constructor(domain: string, contextName?: string) {
|
||||
constructor(domain: string) {
|
||||
super({
|
||||
code: 'DOMAIN_NOT_FOUND',
|
||||
meta: { domain },
|
||||
message: `Domain not found by "${domain}"${
|
||||
contextName ? ` under ${chalk.bold(contextName)}` : ''
|
||||
}.`,
|
||||
message: `The domain ${domain} can't be found.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,18 +18,3 @@ export default function formatDate(dateStrOrNumber?: number | string | null) {
|
||||
`[in ${ms(diff)}]`
|
||||
)}`;
|
||||
}
|
||||
|
||||
export function formatDateWithoutTime(
|
||||
dateStrOrNumber?: number | string | null
|
||||
) {
|
||||
if (!dateStrOrNumber) {
|
||||
return chalk.gray('-');
|
||||
}
|
||||
|
||||
const date = new Date(dateStrOrNumber);
|
||||
const diff = date.getTime() - Date.now();
|
||||
|
||||
return diff < 0
|
||||
? `${format(date, 'MMM DD YYYY')} ${chalk.gray(`[${ms(-diff)} ago]`)}`
|
||||
: `${format(date, 'MMM DD YYYY')} ${chalk.gray(`[in ${ms(diff)}]`)}`;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import strlen from './strlen';
|
||||
export default function formatTable(
|
||||
header: string[],
|
||||
align: Array<'l' | 'r' | 'c' | '.'>,
|
||||
blocks: { name?: string; rows: string[][] }[],
|
||||
blocks: { name: string; rows: string[][] }[],
|
||||
hsep = ' '
|
||||
) {
|
||||
const nrCols = header.length;
|
||||
@@ -50,10 +50,8 @@ export default function formatTable(
|
||||
for (let j = 0; j < nrCols; j++) {
|
||||
const col = `${row[j]}`;
|
||||
const al = align[j] || 'l';
|
||||
|
||||
const repeat = padding[j] > 1 ? padding[j] * 8 - strlen(col) : 0;
|
||||
const pad = repeat > 0 ? ' '.repeat(repeat) : '';
|
||||
|
||||
const spaces = Math.max(padding[j] * 8 - strlen(col), 0);
|
||||
const pad = ' '.repeat(spaces);
|
||||
rows[i][j] = al === 'l' ? col + pad : pad + col;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,6 @@ export default async function getTeamById(
|
||||
team = await client.fetch<Team>(`/teams/${teamId}`);
|
||||
teamCache.set(teamId, team);
|
||||
}
|
||||
|
||||
|
||||
return team;
|
||||
}
|
||||
|
||||
@@ -4,15 +4,18 @@ import chalk from 'chalk';
|
||||
import { Output } from '../output';
|
||||
import { Framework } from '@vercel/frameworks';
|
||||
import { isSettingValue } from '../is-setting-value';
|
||||
import { ProjectSettings } from '../../types';
|
||||
|
||||
export interface PartialProjectSettings {
|
||||
export interface ProjectSettings {
|
||||
buildCommand: string | null;
|
||||
outputDirectory: string | null;
|
||||
devCommand: string | null;
|
||||
}
|
||||
|
||||
const fields: { name: string; value: keyof PartialProjectSettings }[] = [
|
||||
export interface ProjectSettingsWithFramework extends ProjectSettings {
|
||||
framework: string | null;
|
||||
}
|
||||
|
||||
const fields: { name: string; value: keyof ProjectSettings }[] = [
|
||||
{ name: 'Build Command', value: 'buildCommand' },
|
||||
{ name: 'Output Directory', value: 'outputDirectory' },
|
||||
{ name: 'Development Command', value: 'devCommand' },
|
||||
@@ -20,15 +23,13 @@ const fields: { name: string; value: keyof PartialProjectSettings }[] = [
|
||||
|
||||
export default async function editProjectSettings(
|
||||
output: Output,
|
||||
projectSettings: PartialProjectSettings | null,
|
||||
framework: Framework | null,
|
||||
autoConfirm: boolean
|
||||
): Promise<ProjectSettings> {
|
||||
projectSettings: ProjectSettings | null,
|
||||
framework: Framework | null
|
||||
) {
|
||||
// create new settings object, missing values will be filled with `null`
|
||||
const settings: ProjectSettings = Object.assign(
|
||||
{ framework: null },
|
||||
projectSettings
|
||||
);
|
||||
const settings: Partial<ProjectSettingsWithFramework> = {
|
||||
...projectSettings,
|
||||
};
|
||||
|
||||
for (let field of fields) {
|
||||
settings[field.value] =
|
||||
@@ -63,10 +64,7 @@ export default async function editProjectSettings(
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
autoConfirm ||
|
||||
!(await confirm(`Want to override the settings?`, false))
|
||||
) {
|
||||
if (!(await confirm(`Want to override the settings?`, false))) {
|
||||
return settings;
|
||||
}
|
||||
|
||||
@@ -77,7 +75,7 @@ export default async function editProjectSettings(
|
||||
choices: fields,
|
||||
});
|
||||
|
||||
for (let setting of settingFields as (keyof PartialProjectSettings)[]) {
|
||||
for (let setting of settingFields as (keyof ProjectSettings)[]) {
|
||||
const field = fields.find(f => f.value === setting);
|
||||
const name = `${Date.now()}`;
|
||||
const answers = await inquirer.prompt({
|
||||
|
||||
@@ -15,6 +15,10 @@ export default async function inputProject(
|
||||
detectedProjectName: string,
|
||||
autoConfirm: boolean
|
||||
): Promise<Project | string> {
|
||||
if (autoConfirm) {
|
||||
return detectedProjectName;
|
||||
}
|
||||
|
||||
const slugifiedName = slugify(detectedProjectName);
|
||||
|
||||
// attempt to auto-detect a project to link
|
||||
@@ -38,10 +42,6 @@ export default async function inputProject(
|
||||
} catch (error) {}
|
||||
existingProjectSpinner();
|
||||
|
||||
if (autoConfirm) {
|
||||
return detectedProject || detectedProjectName;
|
||||
}
|
||||
|
||||
let shouldLinkProject;
|
||||
|
||||
if (!detectedProject) {
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
import { join, basename } from 'path';
|
||||
import chalk from 'chalk';
|
||||
import { remove } from 'fs-extra';
|
||||
import { NowContext, ProjectLinkResult, ProjectSettings } from '../../types';
|
||||
import { NowConfig } from '../dev/types';
|
||||
import { Output } from '../output';
|
||||
import {
|
||||
getLinkedProject,
|
||||
linkFolderToProject,
|
||||
getVercelDirectory,
|
||||
} from '../projects/link';
|
||||
import createProject from '../projects/create-project';
|
||||
import updateProject from '../projects/update-project';
|
||||
import Client from '../client';
|
||||
import handleError from '../handle-error';
|
||||
import confirm from '../input/confirm';
|
||||
import toHumanPath from '../humanize-path';
|
||||
import { isDirectory } from '../config/global-path';
|
||||
import selectOrg from '../input/select-org';
|
||||
import inputProject from '../input/input-project';
|
||||
import { validateRootDirectory } from '../validate-paths';
|
||||
import { inputRootDirectory } from '../input/input-root-directory';
|
||||
import editProjectSettings from '../input/edit-project-settings';
|
||||
import stamp from '../output/stamp';
|
||||
import { EmojiLabel } from '../emoji';
|
||||
//@ts-expect-error
|
||||
import createDeploy from '../deploy/create-deploy';
|
||||
//@ts-expect-error
|
||||
import Now from '../index';
|
||||
|
||||
export default async function setupAndLink(
|
||||
ctx: NowContext,
|
||||
output: Output,
|
||||
path: string,
|
||||
forceDelete: boolean,
|
||||
autoConfirm: boolean,
|
||||
successEmoji: EmojiLabel,
|
||||
setupMsg: string
|
||||
): Promise<ProjectLinkResult> {
|
||||
const {
|
||||
authConfig: { token },
|
||||
config,
|
||||
} = ctx;
|
||||
const { apiUrl } = ctx;
|
||||
const debug = output.isDebugEnabled();
|
||||
const client = new Client({
|
||||
apiUrl,
|
||||
token,
|
||||
currentTeam: config.currentTeam,
|
||||
debug,
|
||||
});
|
||||
|
||||
const isFile = !isDirectory(path);
|
||||
if (isFile) {
|
||||
output.error(`Expected directory but found file: ${path}`);
|
||||
return { status: 'error', exitCode: 1 };
|
||||
}
|
||||
const link = await getLinkedProject(output, client, path);
|
||||
const isTTY = process.stdout.isTTY;
|
||||
const quiet = !isTTY;
|
||||
let rootDirectory: string | null = null;
|
||||
let newProjectName: string;
|
||||
let org;
|
||||
|
||||
if (!forceDelete && link.status === 'linked') {
|
||||
return link;
|
||||
}
|
||||
|
||||
if (forceDelete) {
|
||||
const vercelDir = getVercelDirectory(path);
|
||||
remove(vercelDir);
|
||||
}
|
||||
|
||||
const shouldStartSetup =
|
||||
autoConfirm ||
|
||||
(await confirm(
|
||||
`${setupMsg} ${chalk.cyan(`“${toHumanPath(path)}”`)}?`,
|
||||
true
|
||||
));
|
||||
|
||||
if (!shouldStartSetup) {
|
||||
output.print(`Aborted. Project not set up.\n`);
|
||||
return { status: 'not_linked', org: null, project: null };
|
||||
}
|
||||
|
||||
try {
|
||||
org = await selectOrg(
|
||||
output,
|
||||
'Which scope should contain your project?',
|
||||
client,
|
||||
config.currentTeam,
|
||||
autoConfirm
|
||||
);
|
||||
} catch (err) {
|
||||
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
|
||||
output.prettyError(err);
|
||||
return { status: 'error', exitCode: 1 };
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
const detectedProjectName = basename(path);
|
||||
|
||||
const projectOrNewProjectName = await inputProject(
|
||||
output,
|
||||
client,
|
||||
org,
|
||||
detectedProjectName,
|
||||
autoConfirm
|
||||
);
|
||||
|
||||
if (typeof projectOrNewProjectName === 'string') {
|
||||
newProjectName = projectOrNewProjectName;
|
||||
rootDirectory = await inputRootDirectory(path, output, autoConfirm);
|
||||
} else {
|
||||
const project = projectOrNewProjectName;
|
||||
|
||||
await linkFolderToProject(
|
||||
output,
|
||||
path,
|
||||
{
|
||||
projectId: project.id,
|
||||
orgId: org.id,
|
||||
},
|
||||
project.name,
|
||||
org.slug,
|
||||
successEmoji
|
||||
);
|
||||
return { status: 'linked', org, project };
|
||||
}
|
||||
const sourcePath = rootDirectory ? join(path, rootDirectory) : path;
|
||||
|
||||
if (
|
||||
rootDirectory &&
|
||||
!(await validateRootDirectory(output, path, sourcePath, ''))
|
||||
) {
|
||||
return { status: 'error', exitCode: 1 };
|
||||
}
|
||||
|
||||
let localConfig: NowConfig = {};
|
||||
if (ctx.localConfig && !(ctx.localConfig instanceof Error)) {
|
||||
localConfig = ctx.localConfig;
|
||||
}
|
||||
|
||||
client.currentTeam = org.type === 'team' ? org.id : undefined;
|
||||
const isZeroConfig = !localConfig.builds || localConfig.builds.length === 0;
|
||||
|
||||
try {
|
||||
let settings: ProjectSettings = {};
|
||||
|
||||
if (isZeroConfig) {
|
||||
const now = new Now({
|
||||
apiUrl,
|
||||
token,
|
||||
debug,
|
||||
currentTeam: client.currentTeam,
|
||||
});
|
||||
const createArgs: any = {
|
||||
name: newProjectName,
|
||||
env: {},
|
||||
build: { env: {} },
|
||||
forceNew: undefined,
|
||||
withCache: undefined,
|
||||
quiet,
|
||||
wantsPublic: localConfig.public,
|
||||
isFile,
|
||||
type: null,
|
||||
nowConfig: localConfig,
|
||||
regions: undefined,
|
||||
meta: {},
|
||||
deployStamp: stamp(),
|
||||
target: undefined,
|
||||
skipAutoDetectionConfirmation: false,
|
||||
};
|
||||
|
||||
const deployment = await createDeploy(
|
||||
output,
|
||||
now,
|
||||
client.currentTeam || 'current user',
|
||||
[sourcePath],
|
||||
createArgs,
|
||||
org,
|
||||
!isFile,
|
||||
path
|
||||
);
|
||||
|
||||
if (
|
||||
!deployment ||
|
||||
!('code' in deployment) ||
|
||||
deployment.code !== 'missing_project_settings'
|
||||
) {
|
||||
output.error('Failed to detect project settings. Please try again.');
|
||||
if (output.isDebugEnabled()) {
|
||||
console.log(deployment);
|
||||
}
|
||||
return { status: 'error', exitCode: 1 };
|
||||
}
|
||||
|
||||
const { projectSettings, framework } = deployment;
|
||||
|
||||
settings = await editProjectSettings(
|
||||
output,
|
||||
projectSettings,
|
||||
framework,
|
||||
autoConfirm
|
||||
);
|
||||
}
|
||||
|
||||
if (rootDirectory) {
|
||||
settings.rootDirectory = rootDirectory;
|
||||
}
|
||||
|
||||
const project = await createProject(client, newProjectName);
|
||||
await updateProject(client, project.id, settings);
|
||||
Object.assign(project, settings);
|
||||
|
||||
await linkFolderToProject(
|
||||
output,
|
||||
path,
|
||||
{
|
||||
projectId: project.id,
|
||||
orgId: org.id,
|
||||
},
|
||||
project.name,
|
||||
org.slug,
|
||||
successEmoji
|
||||
);
|
||||
|
||||
return { status: 'linked', org, project };
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
return { status: 'error', exitCode: 1 };
|
||||
}
|
||||
}
|
||||
@@ -28,11 +28,15 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
|
||||
str: string,
|
||||
slug: string | null = null,
|
||||
link: string | null = null,
|
||||
action: string | null = 'Learn More',
|
||||
options?: {
|
||||
boxen?: boxen.Options;
|
||||
}
|
||||
action: string = 'Learn More'
|
||||
) {
|
||||
const prevTerm = process.env.TERM;
|
||||
|
||||
if (!prevTerm) {
|
||||
// workaround for https://github.com/sindresorhus/term-size/issues/13
|
||||
process.env.TERM = 'xterm';
|
||||
}
|
||||
|
||||
const details = slug ? `https://err.sh/now/${slug}` : link;
|
||||
|
||||
print(
|
||||
@@ -48,11 +52,12 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
|
||||
right: 1,
|
||||
},
|
||||
borderColor: 'yellow',
|
||||
...options?.boxen,
|
||||
}
|
||||
)
|
||||
);
|
||||
print('\n');
|
||||
|
||||
process.env.TERM = prevTerm;
|
||||
}
|
||||
|
||||
function note(str: string) {
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import chalk from 'chalk';
|
||||
import Client from '../client';
|
||||
import wait from '../output/wait';
|
||||
import { ProjectAliasTarget } from '../../types';
|
||||
|
||||
export async function addDomainToProject(
|
||||
client: Client,
|
||||
projectNameOrId: string,
|
||||
domain: string
|
||||
) {
|
||||
const cancelWait = wait(
|
||||
`Adding domain ${domain} to project ${chalk.bold(projectNameOrId)}`
|
||||
);
|
||||
try {
|
||||
const response = await client.fetch<ProjectAliasTarget[]>(
|
||||
`/projects/${encodeURIComponent(projectNameOrId)}/alias`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
target: 'PRODUCTION',
|
||||
domain,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
const aliasTarget: ProjectAliasTarget | undefined = response.find(
|
||||
aliasTarget => aliasTarget.domain === domain
|
||||
);
|
||||
|
||||
if (!aliasTarget) {
|
||||
throw new Error(
|
||||
`Unexpected error when adding the domain "${domain}" to project "${projectNameOrId}".`
|
||||
);
|
||||
}
|
||||
|
||||
return aliasTarget;
|
||||
} catch (err) {
|
||||
if (err.status < 500) {
|
||||
return err;
|
||||
}
|
||||
|
||||
throw err;
|
||||
} finally {
|
||||
cancelWait();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import Client from '../client';
|
||||
import { Project } from '../../types';
|
||||
|
||||
export default async function createProject(
|
||||
client: Client,
|
||||
projectName: string
|
||||
) {
|
||||
const project = await client.fetch<Project>('/v1/projects', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ name: projectName }),
|
||||
});
|
||||
return project;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import Client from '../client';
|
||||
import { Project } from '../../types';
|
||||
import { URLSearchParams } from 'url';
|
||||
|
||||
export async function findProjectsForDomain(
|
||||
client: Client,
|
||||
domainName: string
|
||||
): Promise<Project[] | Error> {
|
||||
try {
|
||||
const limit = 50;
|
||||
let result: Project[] = [];
|
||||
|
||||
const query = new URLSearchParams({
|
||||
hasProductionDomains: '1',
|
||||
limit: limit.toString(),
|
||||
domain: domainName,
|
||||
});
|
||||
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const response = await client.fetch<Project[]>(`/v2/projects/?${query}`);
|
||||
result.push(...response);
|
||||
|
||||
if (response.length !== limit) {
|
||||
break;
|
||||
}
|
||||
|
||||
const [latest] = response.sort((a, b) => b.updatedAt - a.updatedAt);
|
||||
query.set('from', latest.updatedAt.toString());
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (err) {
|
||||
if (err.status < 500) {
|
||||
return err;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,10 @@ import { ProjectNotFound } from '../errors-ts';
|
||||
import getUser from '../get-user';
|
||||
import getTeamById from '../get-team-by-id';
|
||||
import { Output } from '../output';
|
||||
import { Project, ProjectLinkResult } from '../../types';
|
||||
import { Project } from '../../types';
|
||||
import { Org, ProjectLink } from '../../types';
|
||||
import chalk from 'chalk';
|
||||
import { prependEmoji, emoji, EmojiLabel } from '../emoji';
|
||||
import { prependEmoji, emoji } from '../emoji';
|
||||
import AJV from 'ajv';
|
||||
import { isDirectory } from '../config/global-path';
|
||||
import { NowBuildError, getPlatformEnv } from '@vercel/build-utils';
|
||||
@@ -112,7 +112,11 @@ export async function getLinkedProject(
|
||||
output: Output,
|
||||
client: Client,
|
||||
path?: string
|
||||
): Promise<ProjectLinkResult> {
|
||||
): Promise<
|
||||
| { status: 'linked'; org: Org; project: Project }
|
||||
| { status: 'not_linked'; org: null; project: null }
|
||||
| { status: 'error'; exitCode: number }
|
||||
> {
|
||||
const VERCEL_ORG_ID = getPlatformEnv('ORG_ID');
|
||||
const VERCEL_PROJECT_ID = getPlatformEnv('PROJECT_ID');
|
||||
const shouldUseEnv = Boolean(VERCEL_ORG_ID && VERCEL_PROJECT_ID);
|
||||
@@ -150,7 +154,7 @@ export async function getLinkedProject(
|
||||
spinner();
|
||||
throw new NowBuildError({
|
||||
message: `Could not retrieve Project Settings. To link your Project, remove the ${outputCode(
|
||||
VERCEL_DIR
|
||||
'.vercel'
|
||||
)} directory and deploy again.`,
|
||||
code: 'PROJECT_UNAUTHORIZED',
|
||||
link: 'https://vercel.link/cannot-load-project-settings',
|
||||
@@ -192,8 +196,7 @@ export async function linkFolderToProject(
|
||||
path: string,
|
||||
projectLink: ProjectLink,
|
||||
projectName: string,
|
||||
orgSlug: string,
|
||||
successEmoji: EmojiLabel = 'link'
|
||||
orgSlug: string
|
||||
) {
|
||||
const VERCEL_ORG_ID = getPlatformEnv('ORG_ID');
|
||||
const VERCEL_PROJECT_ID = getPlatformEnv('PROJECT_ID');
|
||||
@@ -244,12 +247,11 @@ export async function linkFolderToProject(
|
||||
const gitIgnore = await readFile(gitIgnorePath)
|
||||
.then(buf => buf.toString())
|
||||
.catch(() => null);
|
||||
const EOL = gitIgnore && gitIgnore.includes('\r\n') ? '\r\n' : '\n';
|
||||
|
||||
if (!gitIgnore || !gitIgnore.split(EOL).includes(VERCEL_DIR)) {
|
||||
if (!gitIgnore || !gitIgnore.split('\n').includes(VERCEL_DIR)) {
|
||||
await writeFile(
|
||||
gitIgnorePath,
|
||||
gitIgnore ? `${gitIgnore}${EOL}${VERCEL_DIR}${EOL}` : `${VERCEL_DIR}${EOL}`
|
||||
gitIgnore ? `${gitIgnore}\n${VERCEL_DIR}` : VERCEL_DIR
|
||||
);
|
||||
isGitIgnoreUpdated = true;
|
||||
}
|
||||
@@ -264,7 +266,7 @@ export async function linkFolderToProject(
|
||||
)} (created ${VERCEL_DIR}${
|
||||
isGitIgnoreUpdated ? ' and added it to .gitignore' : ''
|
||||
})`,
|
||||
emoji(successEmoji)
|
||||
emoji('link')
|
||||
) + '\n'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import chalk from 'chalk';
|
||||
import Client from '../client';
|
||||
import wait from '../output/wait';
|
||||
import { ProjectAliasTarget } from '../../types';
|
||||
|
||||
export async function removeDomainFromProject(
|
||||
client: Client,
|
||||
projectNameOrId: string,
|
||||
domain: string
|
||||
) {
|
||||
const cancelWait = wait(
|
||||
`Removing domain ${domain} from project ${chalk.bold(projectNameOrId)}`
|
||||
);
|
||||
try {
|
||||
const response = await client.fetch<ProjectAliasTarget[]>(
|
||||
`/projects/${encodeURIComponent(
|
||||
projectNameOrId
|
||||
)}/alias?domain=${encodeURIComponent(domain)}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
}
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (err) {
|
||||
if (err.status < 500) {
|
||||
return err;
|
||||
}
|
||||
|
||||
throw err;
|
||||
} finally {
|
||||
cancelWait();
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import Client from '../client';
|
||||
import { ProjectSettings } from '../../types';
|
||||
|
||||
interface ProjectSettingsResponse extends ProjectSettings {
|
||||
id: string;
|
||||
name: string;
|
||||
updatedAt: number;
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
export default async function updateProject(
|
||||
client: Client,
|
||||
prjNameOrId: string,
|
||||
settings: ProjectSettings
|
||||
) {
|
||||
const res = await client.fetch<ProjectSettingsResponse>(
|
||||
`/v2/projects/${encodeURIComponent(prjNameOrId)}`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(settings),
|
||||
}
|
||||
);
|
||||
return res;
|
||||
}
|
||||
@@ -8,7 +8,7 @@ export default async function responseError(
|
||||
) {
|
||||
let bodyError;
|
||||
|
||||
if (!res.ok) {
|
||||
if (res.status >= 400 && res.status < 500) {
|
||||
let body;
|
||||
|
||||
try {
|
||||
|
||||
6
packages/now-cli/test/dev-router.unit.js
vendored
6
packages/now-cli/test/dev-router.unit.js
vendored
@@ -3,11 +3,7 @@ import { devRouter } from '../src/util/dev/router';
|
||||
|
||||
test('[dev-router] 301 redirection', async t => {
|
||||
const routesConfig = [
|
||||
{
|
||||
src: '/redirect',
|
||||
status: 301,
|
||||
headers: { Location: 'https://vercel.com' },
|
||||
},
|
||||
{ src: '/redirect', status: 301, headers: { Location: 'https://vercel.com' } },
|
||||
];
|
||||
const result = await devRouter('/redirect', 'GET', routesConfig);
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
.vercel
|
||||
@@ -1,10 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Handler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Req Path: %s", r.URL.Path)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user