mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-28 19:00:08 +00:00
Compare commits
1 Commits
@vercel/ne
...
loopless-4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dabbed9e60 |
@@ -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 }}
|
||||
|
||||
@@ -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": "20.0.0-canary.1",
|
||||
"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/build-utils": "2.4.2",
|
||||
"@vercel/go": "1.1.5-canary.0",
|
||||
"@vercel/next": "2.6.19",
|
||||
"@vercel/node": "1.7.5-canary.1",
|
||||
"@vercel/next": "2.6.14-canary.1",
|
||||
"@vercel/node": "1.7.4-canary.0",
|
||||
"@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.7-canary.0",
|
||||
"update-notifier": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -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}`,
|
||||
|
||||
@@ -128,7 +128,7 @@ export default async function add(
|
||||
return 1;
|
||||
}
|
||||
|
||||
const domainConfig = await getDomainConfig(client, domainName);
|
||||
const domainConfig = await getDomainConfig(client, contextName, domainName);
|
||||
|
||||
if (domainConfig.misconfigured) {
|
||||
output.warn(
|
||||
@@ -142,7 +142,7 @@ export default async function add(
|
||||
);
|
||||
output.print(
|
||||
` ${chalk.grey('b)')} ` +
|
||||
`Change your Domains's nameservers to the intended set`
|
||||
`Change your domain nameservers to the intended set`
|
||||
);
|
||||
output.print(
|
||||
`\n${formatNSTable(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ import inspect from './inspect';
|
||||
import ls from './ls';
|
||||
import rm from './rm';
|
||||
import move from './move';
|
||||
import verify from './verify';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
|
||||
const help = () => {
|
||||
@@ -82,7 +81,6 @@ const COMMAND_CONFIG = {
|
||||
move: ['move'],
|
||||
rm: ['rm', 'remove'],
|
||||
transferIn: ['transfer-in'],
|
||||
verify: ['verify'],
|
||||
};
|
||||
|
||||
export default async function main(ctx: NowContext) {
|
||||
@@ -121,8 +119,6 @@ export default async function main(ctx: NowContext) {
|
||||
return rm(ctx, argv, args, output);
|
||||
case 'transferIn':
|
||||
return transferIn(ctx, argv, args, output);
|
||||
case 'verify':
|
||||
return verify(ctx, argv, args, output);
|
||||
default:
|
||||
return ls(ctx, argv, args, output);
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ 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 +67,38 @@ 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 || 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;
|
||||
}
|
||||
|
||||
const projects = await findProjectsForDomain(client, domainName);
|
||||
|
||||
if (projects instanceof Error) {
|
||||
output.prettyError(projects);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const domainConfig = await getDomainConfig(client, contextName, domainName);
|
||||
|
||||
output.log(
|
||||
`Domain ${domainName} found under ${chalk.bold(contextName)} ${chalk.gray(
|
||||
@@ -98,27 +108,46 @@ 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,6 +158,26 @@ export default async function inspect(
|
||||
);
|
||||
output.print('\n');
|
||||
|
||||
if (domainConfig.misconfigured) {
|
||||
output.warn(
|
||||
`This domain is not configured properly. To configure 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 domain nameservers to the intended set detailed above.\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');
|
||||
}
|
||||
|
||||
if (Array.isArray(projects) && projects.length > 0) {
|
||||
output.print(chalk.bold(' Projects\n'));
|
||||
|
||||
@@ -159,109 +208,8 @@ export default async function inspect(
|
||||
.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,
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
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`
|
||||
);
|
||||
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))
|
||||
);
|
||||
|
||||
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('\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,8 +1,8 @@
|
||||
import ms from 'ms';
|
||||
import psl from 'psl';
|
||||
import chalk from 'chalk';
|
||||
import plural from 'pluralize';
|
||||
|
||||
import wait from '../../util/output/wait';
|
||||
import Client from '../../util/client';
|
||||
import getDomains from '../../util/domains/get-domains';
|
||||
import getScope from '../../util/get-scope';
|
||||
@@ -10,17 +10,28 @@ import stamp from '../../util/output/stamp';
|
||||
import { Output } from '../../util/output';
|
||||
import formatTable from '../../util/format-table';
|
||||
import { formatDateWithoutTime } from '../../util/format-date';
|
||||
import { Domain, NowContext } from '../../types';
|
||||
import { Domain, Project, NowContext } from '../../types';
|
||||
import { getProjectsWithDomains } from '../../util/projects/get-projects-with-domains';
|
||||
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';
|
||||
import { isPublicSuffix } from '../../util/domains/is-public-suffix';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
'--next': number;
|
||||
};
|
||||
|
||||
interface DomainInfo {
|
||||
domain: string;
|
||||
apexDomain: string;
|
||||
projectName: string | null;
|
||||
dns: 'Vercel' | 'External';
|
||||
configured: boolean;
|
||||
expiresAt: number | null;
|
||||
createdAt: number | null;
|
||||
}
|
||||
|
||||
export default async function ls(
|
||||
ctx: NowContext,
|
||||
opts: Options,
|
||||
@@ -64,21 +75,29 @@ export default async function ls(
|
||||
return 1;
|
||||
}
|
||||
|
||||
const cancelWait = wait(`Fetching domains under ${chalk.bold(contextName)}`);
|
||||
const [{ domains, pagination }, projects] = await Promise.all([
|
||||
getDomains(client, contextName),
|
||||
getProjectsWithDomains(client),
|
||||
] as const);
|
||||
|
||||
const { domains, pagination } = await getDomains(client).finally(() => {
|
||||
cancelWait();
|
||||
});
|
||||
if (projects instanceof Error) {
|
||||
output.prettyError(projects);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const domainsInfo = createDomainsInfo(domains, projects);
|
||||
|
||||
output.log(
|
||||
`${plural('Domain', domains.length, true)} found under ${chalk.bold(
|
||||
contextName
|
||||
)} ${chalk.gray(lsStamp())}`
|
||||
`${plural(
|
||||
'project domain',
|
||||
domainsInfo.length,
|
||||
true
|
||||
)} found under ${chalk.bold(contextName)} ${chalk.gray(lsStamp())}`
|
||||
);
|
||||
|
||||
if (domains.length > 0) {
|
||||
if (domainsInfo.length > 0) {
|
||||
output.print(
|
||||
formatDomainsTable(domains).replace(/^(.*)/gm, `${' '.repeat(1)}$1`)
|
||||
formatDomainsTable(domainsInfo).replace(/^(.*)/gm, `${' '.repeat(3)}$1`)
|
||||
);
|
||||
output.print('\n\n');
|
||||
}
|
||||
@@ -86,7 +105,7 @@ export default async function ls(
|
||||
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}`
|
||||
)}`
|
||||
);
|
||||
@@ -95,26 +114,92 @@ export default async function ls(
|
||||
return 0;
|
||||
}
|
||||
|
||||
function formatDomainsTable(domains: Domain[]) {
|
||||
function createDomainsInfo(domains: Domain[], projects: Project[]) {
|
||||
const info = new Map<string, DomainInfo>();
|
||||
|
||||
domains.forEach(domain => {
|
||||
info.set(domain.name, {
|
||||
domain: domain.name,
|
||||
apexDomain: domain.name,
|
||||
projectName: null,
|
||||
expiresAt: domain.expiresAt || null,
|
||||
createdAt: domain.createdAt,
|
||||
configured: Boolean(domain.verified),
|
||||
dns: isDomainExternal(domain) ? 'External' : 'Vercel',
|
||||
});
|
||||
|
||||
projects.forEach(project => {
|
||||
(project.alias || []).forEach(target => {
|
||||
if (!target.domain.endsWith(domain.name)) return;
|
||||
|
||||
info.set(target.domain, {
|
||||
domain: target.domain,
|
||||
apexDomain: domain.name,
|
||||
projectName: project.name,
|
||||
expiresAt: domain.expiresAt || null,
|
||||
createdAt: domain.createdAt || target.createdAt || null,
|
||||
configured: Boolean(domain.verified),
|
||||
dns: isDomainExternal(domain) ? 'External' : 'Vercel',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
projects.forEach(project => {
|
||||
(project.alias || []).forEach(target => {
|
||||
if (info.has(target.domain)) return;
|
||||
|
||||
const { domain: apexDomain } = psl.parse(
|
||||
target.domain
|
||||
) as psl.ParsedDomain;
|
||||
|
||||
info.set(target.domain, {
|
||||
domain: target.domain,
|
||||
apexDomain: apexDomain || target.domain,
|
||||
projectName: project.name,
|
||||
expiresAt: null,
|
||||
createdAt: target.createdAt || null,
|
||||
configured: isPublicSuffix(target.domain),
|
||||
dns: isPublicSuffix(target.domain) ? 'Vercel' : 'External',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const list = Array.from(info.values());
|
||||
|
||||
return list.sort((a, b) => {
|
||||
if (a.apexDomain === b.apexDomain) {
|
||||
if (a.apexDomain === a.domain) return -1;
|
||||
if (b.apexDomain === b.domain) return 1;
|
||||
return a.domain.localeCompare(b.domain);
|
||||
}
|
||||
|
||||
return a.apexDomain.localeCompare(b.apexDomain);
|
||||
});
|
||||
}
|
||||
|
||||
function formatDomainsTable(domainsInfo: DomainInfo[]) {
|
||||
const current = Date.now();
|
||||
|
||||
const rows: string[][] = domains.map(domain => {
|
||||
const expiration = formatDateWithoutTime(domain.expiresAt);
|
||||
const age = domain.createdAt ? ms(current - domain.createdAt) : '-';
|
||||
const rows: string[][] = domainsInfo.map(info => {
|
||||
const expiration = formatDateWithoutTime(info.expiresAt);
|
||||
const age = info.createdAt ? ms(current - info.createdAt) : '-';
|
||||
|
||||
return [
|
||||
domain.name,
|
||||
getDomainRegistrar(domain),
|
||||
isDomainExternal(domain) ? 'Third Party' : 'Vercel',
|
||||
info.domain,
|
||||
info.projectName || '-',
|
||||
info.dns,
|
||||
expiration,
|
||||
domain.creator.username,
|
||||
info.configured.toString(),
|
||||
chalk.gray(age),
|
||||
];
|
||||
});
|
||||
|
||||
return formatTable(
|
||||
['Domain', 'Registrar', 'Nameservers', 'Expiration', 'Creator', 'Age'],
|
||||
const table = formatTable(
|
||||
['domain', 'project', 'dns', 'expiration', 'configured', 'age'],
|
||||
['l', 'l', 'l', 'l', 'l', 'l'],
|
||||
[{ rows }]
|
||||
);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
@@ -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 +0,0 @@
|
||||
import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import { NowBuildError } from '@vercel/build-utils';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
|
||||
export default async function verify(
|
||||
_ctx: NowContext,
|
||||
_opts: {},
|
||||
args: string[],
|
||||
output: Output
|
||||
) {
|
||||
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',
|
||||
});
|
||||
|
||||
output.prettyError(error);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1578,13 +1578,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,
|
||||
|
||||
@@ -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,7 +1,16 @@
|
||||
import chalk from 'chalk';
|
||||
import Client from '../client';
|
||||
import wait from '../output/wait';
|
||||
import { DomainConfig } from '../../types';
|
||||
|
||||
export async function getDomainConfig(client: Client, domainName: string) {
|
||||
export async function getDomainConfig(
|
||||
client: Client,
|
||||
contextName: string,
|
||||
domainName: string
|
||||
) {
|
||||
const cancelWait = wait(
|
||||
`Fetching domain config ${domainName} under ${chalk.bold(contextName)}`
|
||||
);
|
||||
try {
|
||||
const config = await client.fetch<DomainConfig>(
|
||||
`/v4/domains/${domainName}/config`
|
||||
@@ -14,5 +23,7 @@ export async function getDomainConfig(client: Client, domainName: string) {
|
||||
}
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
cancelWait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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;
|
||||
}
|
||||
|
||||
@@ -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.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { join, basename } from 'path';
|
||||
import chalk from 'chalk';
|
||||
import { remove } from 'fs-extra';
|
||||
import { NowContext, ProjectLinkResult, ProjectSettings } from '../../types';
|
||||
import { NowContext, ProjectLinkResult } from '../../types';
|
||||
import { NowConfig } from '../dev/types';
|
||||
import { Output } from '../output';
|
||||
import {
|
||||
@@ -142,75 +142,69 @@ export default async function setupAndLink(
|
||||
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;
|
||||
const now = new Now({
|
||||
apiUrl,
|
||||
token,
|
||||
debug,
|
||||
currentTeam: client.currentTeam,
|
||||
});
|
||||
let deployment = null;
|
||||
|
||||
try {
|
||||
let settings: ProjectSettings = {};
|
||||
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,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
deployment = await createDeploy(
|
||||
output,
|
||||
now,
|
||||
client.currentTeam || 'current user',
|
||||
[sourcePath],
|
||||
createArgs,
|
||||
org,
|
||||
!isFile,
|
||||
path
|
||||
);
|
||||
|
||||
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 };
|
||||
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);
|
||||
}
|
||||
|
||||
const { projectSettings, framework } = deployment;
|
||||
|
||||
settings = await editProjectSettings(
|
||||
output,
|
||||
projectSettings,
|
||||
framework,
|
||||
autoConfirm
|
||||
);
|
||||
return { status: 'error', exitCode: 1 };
|
||||
}
|
||||
|
||||
const { projectSettings, framework } = deployment;
|
||||
|
||||
if (rootDirectory) {
|
||||
settings.rootDirectory = rootDirectory;
|
||||
projectSettings.rootDirectory = rootDirectory;
|
||||
}
|
||||
|
||||
const settings = await editProjectSettings(
|
||||
output,
|
||||
projectSettings,
|
||||
framework,
|
||||
autoConfirm
|
||||
);
|
||||
const project = await createProject(client, newProjectName);
|
||||
await updateProject(client, project.id, settings);
|
||||
Object.assign(project, settings);
|
||||
|
||||
@@ -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,4 +1,6 @@
|
||||
import chalk from 'chalk';
|
||||
import Client from '../client';
|
||||
import wait from '../output/wait';
|
||||
import { Project } from '../../types';
|
||||
import { URLSearchParams } from 'url';
|
||||
|
||||
@@ -6,6 +8,9 @@ export async function findProjectsForDomain(
|
||||
client: Client,
|
||||
domainName: string
|
||||
): Promise<Project[] | Error> {
|
||||
const cancelWait = wait(
|
||||
`Searching project for domain ${chalk.bold(domainName)}`
|
||||
);
|
||||
try {
|
||||
const limit = 50;
|
||||
let result: Project[] = [];
|
||||
@@ -25,7 +30,7 @@ export async function findProjectsForDomain(
|
||||
}
|
||||
|
||||
const [latest] = response.sort((a, b) => b.updatedAt - a.updatedAt);
|
||||
query.set('from', latest.updatedAt.toString());
|
||||
query.append('from', latest.updatedAt.toString());
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -35,5 +40,7 @@ export async function findProjectsForDomain(
|
||||
}
|
||||
|
||||
throw err;
|
||||
} finally {
|
||||
cancelWait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import Client from '../client';
|
||||
import wait from '../output/wait';
|
||||
import { Project } from '../../types';
|
||||
import { URLSearchParams } from 'url';
|
||||
|
||||
export async function getProjectsWithDomains(
|
||||
client: Client
|
||||
): Promise<Project[] | Error> {
|
||||
const cancelWait = wait(`Fetching projects with domains`);
|
||||
try {
|
||||
const limit = 50;
|
||||
let result: Project[] = [];
|
||||
|
||||
const query = new URLSearchParams({
|
||||
hasProductionDomains: '1',
|
||||
limit: limit.toString(),
|
||||
});
|
||||
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const response = await client.fetch<Project[]>(`/v2/projects/?${query}`);
|
||||
result.push(...response);
|
||||
|
||||
const [latest] = response.sort((a, b) => b.updatedAt - a.updatedAt);
|
||||
query.append('from', latest.updatedAt.toString());
|
||||
|
||||
if (response.length !== limit) break;
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (err) {
|
||||
if (err.status < 500) {
|
||||
return err;
|
||||
}
|
||||
|
||||
throw err;
|
||||
} finally {
|
||||
cancelWait();
|
||||
}
|
||||
}
|
||||
@@ -244,12 +244,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;
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"rewrites": [
|
||||
{ "source": "/rewrite", "destination": "https://vercel.com/robots.txt" }
|
||||
],
|
||||
"redirects": [
|
||||
{ "source": "/redirect", "destination": "https://vercel.com/robots.txt" },
|
||||
{
|
||||
"source": "/tempRedirect",
|
||||
"destination": "https://vercel.com/robots.txt",
|
||||
"permanent": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,8 +20,6 @@ let port = 3000;
|
||||
const binaryPath = resolve(__dirname, `../../scripts/start.js`);
|
||||
const fixture = name => join('test', 'dev', 'fixtures', name);
|
||||
const fixtureAbsolute = name => join(__dirname, 'fixtures', name);
|
||||
const exampleAbsolute = name =>
|
||||
join(__dirname, '..', '..', '..', '..', 'examples', name);
|
||||
|
||||
let processCounter = 0;
|
||||
const processList = new Map();
|
||||
@@ -113,7 +111,7 @@ async function exec(directory, args = []) {
|
||||
}
|
||||
|
||||
async function runNpmInstall(fixturePath) {
|
||||
if (await fs.pathExists(join(fixturePath, 'package.json'))) {
|
||||
if (await fs.exists(join(fixturePath, 'package.json'))) {
|
||||
await execa('yarn', ['install'], {
|
||||
cwd: fixturePath,
|
||||
shell: true,
|
||||
@@ -129,10 +127,9 @@ async function testPath(
|
||||
path,
|
||||
expectedText,
|
||||
headers = {},
|
||||
method = 'GET',
|
||||
body = undefined
|
||||
method = 'GET'
|
||||
) {
|
||||
const opts = { redirect: 'manual-dont-change', method, body };
|
||||
const opts = { redirect: 'manual-dont-change', method };
|
||||
const url = `${origin}${path}`;
|
||||
const res = await fetch(url, opts);
|
||||
const msg = `Testing response from ${method} ${url}`;
|
||||
@@ -233,18 +230,10 @@ async function testFixture(directory, opts = {}, args = []) {
|
||||
function testFixtureStdio(
|
||||
directory,
|
||||
fn,
|
||||
{ expectedCode = 0, skipDeploy, isExample } = {}
|
||||
{ expectedCode = 0, skipDeploy } = {}
|
||||
) {
|
||||
return async t => {
|
||||
const nodeMajor = Number(process.versions.node.split('.')[0]);
|
||||
if (isExample && nodeMajor < 12) {
|
||||
console.log(`Skipping ${directory} on Node ${process.version}`);
|
||||
t.pass();
|
||||
return;
|
||||
}
|
||||
const cwd = isExample
|
||||
? exampleAbsolute(directory)
|
||||
: fixtureAbsolute(directory);
|
||||
const cwd = fixtureAbsolute(directory);
|
||||
const token = await fetchTokenWithRetry();
|
||||
let deploymentUrl;
|
||||
|
||||
@@ -380,21 +369,6 @@ test.afterEach(async () => {
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
'[vercel dev] redwoodjs example',
|
||||
testFixtureStdio(
|
||||
'redwoodjs',
|
||||
async testPath => {
|
||||
await testPath(200, '/', /<div id="redwood-app">/m);
|
||||
await testPath(200, '/about', /<div id="redwood-app">/m);
|
||||
const reqBody = '{"query":"{redwood{version}}"}';
|
||||
const resBody = '{"data":{"redwood":{"version":"0.15.0"}}}';
|
||||
await testPath(200, '/api/graphql', resBody, {}, 'POST', reqBody);
|
||||
},
|
||||
{ isExample: true }
|
||||
)
|
||||
);
|
||||
|
||||
test('[vercel dev] prints `npm install` errors', async t => {
|
||||
const dir = fixture('runtime-not-installed');
|
||||
const result = await exec(dir);
|
||||
@@ -843,20 +817,6 @@ test(
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] test rewrites and redirects serve correct external content',
|
||||
testFixtureStdio('test-external-rewrites-and-redirects', async testPath => {
|
||||
const vcRobots = `https://vercel.com/robots.txt`;
|
||||
await testPath(200, '/rewrite', /User-Agent: \*/m);
|
||||
await testPath(308, '/redirect', `Redirecting to ${vcRobots} (308)`, {
|
||||
Location: vcRobots,
|
||||
});
|
||||
await testPath(307, '/tempRedirect', `Redirecting to ${vcRobots} (307)`, {
|
||||
Location: vcRobots,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] test rewrites and redirects is case sensitive',
|
||||
testFixtureStdio('test-routing-case-sensitive', async testPath => {
|
||||
@@ -1175,8 +1135,7 @@ test(
|
||||
await testPath(200, '/api/date', new RegExp(new Date().getFullYear()));
|
||||
await testPath(200, '/contact', /Contact Page/);
|
||||
await testPath(200, '/support', /Contact Page/);
|
||||
// TODO: Fix this test assertion that fails intermittently
|
||||
// await testPath(404, '/nothing', /Custom Next 404/);
|
||||
await testPath(404, '/nothing', /Custom Next 404/);
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ const getConfigFile = builds =>
|
||||
]
|
||||
}`
|
||||
: `{
|
||||
"version": 1
|
||||
"version": 2
|
||||
}`;
|
||||
|
||||
const getIndexHTMLFile = session => `
|
||||
@@ -500,21 +500,14 @@ CMD ["node", "index.js"]`,
|
||||
},
|
||||
}),
|
||||
},
|
||||
'project-link-deploy': {
|
||||
'package.json': '{}',
|
||||
},
|
||||
'project-link-zeroconf': {
|
||||
'package.json': '{}',
|
||||
'project-link': {
|
||||
'package.json': JSON.stringify({}),
|
||||
},
|
||||
'project-link-confirm': {
|
||||
'package.json': '{}',
|
||||
'package.json': JSON.stringify({}),
|
||||
},
|
||||
'project-link-dev': {
|
||||
'package.json': '{}',
|
||||
},
|
||||
'project-link-legacy': {
|
||||
'index.html': 'Hello',
|
||||
'vercel.json': '{"builds":[{"src":"*.html","use":"@vercel/static"}]}',
|
||||
'package.json': JSON.stringify({}),
|
||||
},
|
||||
'dev-proxy-headers-and-env': {
|
||||
'package.json': JSON.stringify({}),
|
||||
|
||||
112
packages/now-cli/test/integration.js
vendored
112
packages/now-cli/test/integration.js
vendored
@@ -2321,11 +2321,7 @@ test('render build errors', async t => {
|
||||
console.log(output.exitCode);
|
||||
|
||||
t.is(output.exitCode, 1, formatOutput(output));
|
||||
t.regex(
|
||||
output.stderr,
|
||||
/Command "yarn run build" exited with 1/gm,
|
||||
formatOutput(output)
|
||||
);
|
||||
t.regex(output.stderr, /Build failed/gm, formatOutput(output));
|
||||
});
|
||||
|
||||
test('invalid deployment, projects and alias names', async t => {
|
||||
@@ -2621,6 +2617,39 @@ test('assign a domain to a project', async t => {
|
||||
t.is(removeResponse.exitCode, 0, formatOutput(removeResponse));
|
||||
});
|
||||
|
||||
test('list project domains', async t => {
|
||||
const domain = `project-domain.${contextName}.now.sh`;
|
||||
const directory = fixture('static-deployment');
|
||||
|
||||
const deploymentOutput = await execute([directory, '--public', '--confirm']);
|
||||
t.is(deploymentOutput.exitCode, 0, formatOutput(deploymentOutput));
|
||||
|
||||
const host = deploymentOutput.stdout.trim().replace('https://', '');
|
||||
const deployment = await apiFetch(
|
||||
`/v10/now/deployments/unknown?url=${host}`
|
||||
).then(resp => resp.json());
|
||||
|
||||
t.is(typeof deployment.name, 'string', JSON.stringify(deployment, null, 2));
|
||||
const project = deployment.name;
|
||||
|
||||
const addOutput = await execute([
|
||||
'domains',
|
||||
'add',
|
||||
domain,
|
||||
project,
|
||||
'--force',
|
||||
]);
|
||||
t.is(addOutput.exitCode, 0, formatOutput(addOutput));
|
||||
|
||||
const output = await execute(['domains', 'ls']);
|
||||
t.is(output.exitCode, 0, formatOutput(output));
|
||||
t.regex(output.stderr, new RegExp(domain), formatOutput(output));
|
||||
t.regex(output.stderr, new RegExp(project), formatOutput(output));
|
||||
|
||||
const removeResponse = await execute(['rm', project, '-y']);
|
||||
t.is(removeResponse.exitCode, 0, formatOutput(removeResponse));
|
||||
});
|
||||
|
||||
test('ensure `github` and `scope` are not sent to the API', async t => {
|
||||
const directory = fixture('github-and-scope-config');
|
||||
const output = await execute([directory, '--confirm']);
|
||||
@@ -2628,9 +2657,9 @@ test('ensure `github` and `scope` are not sent to the API', async t => {
|
||||
t.is(output.exitCode, 0, formatOutput(output));
|
||||
});
|
||||
|
||||
test('should show prompts to set up project during first deploy', async t => {
|
||||
const directory = fixture('project-link-deploy');
|
||||
const projectName = `project-link-deploy-${
|
||||
test('should show prompts to set up project', async t => {
|
||||
const directory = fixture('project-link');
|
||||
const projectName = `project-link-${
|
||||
Math.random().toString(36).split('.')[1]
|
||||
}`;
|
||||
|
||||
@@ -2701,7 +2730,7 @@ test('should show prompts to set up project during first deploy', async t => {
|
||||
// Ensure .gitignore is created
|
||||
t.is(
|
||||
(await readFile(path.join(directory, '.gitignore'))).toString(),
|
||||
'.vercel\n'
|
||||
'.vercel'
|
||||
);
|
||||
|
||||
// Ensure .vercel/project.json and .vercel/README.txt are created
|
||||
@@ -3255,8 +3284,8 @@ test('reject deploying with wrong team .vercel config', async t => {
|
||||
});
|
||||
|
||||
test('[vc link] should show prompts to set up project', async t => {
|
||||
const dir = fixture('project-link-zeroconf');
|
||||
const projectName = `project-link-zeroconf-${
|
||||
const dir = fixture('project-link');
|
||||
const projectName = `project-link-${
|
||||
Math.random().toString(36).split('.')[1]
|
||||
}`;
|
||||
|
||||
@@ -3321,7 +3350,7 @@ test('[vc link] should show prompts to set up project', async t => {
|
||||
t.is(output.exitCode, 0, formatOutput(output));
|
||||
|
||||
// Ensure .gitignore is created
|
||||
t.is((await readFile(path.join(dir, '.gitignore'))).toString(), '.vercel\n');
|
||||
t.is((await readFile(path.join(dir, '.gitignore'))).toString(), '.vercel');
|
||||
|
||||
// Ensure .vercel/project.json and .vercel/README.txt are created
|
||||
t.is(
|
||||
@@ -3355,7 +3384,7 @@ test('[vc link --confirm] should not show prompts and autolink', async t => {
|
||||
t.regex(stderr, /Linked to /m);
|
||||
|
||||
// Ensure .gitignore is created
|
||||
t.is((await readFile(path.join(dir, '.gitignore'))).toString(), '.vercel\n');
|
||||
t.is((await readFile(path.join(dir, '.gitignore'))).toString(), '.vercel');
|
||||
|
||||
// Ensure .vercel/project.json and .vercel/README.txt are created
|
||||
t.is(
|
||||
@@ -3439,7 +3468,7 @@ test('[vc dev] should show prompts to set up project', async t => {
|
||||
await waitForPrompt(dev, chunk => chunk.includes('Linked to'));
|
||||
|
||||
// Ensure .gitignore is created
|
||||
t.is((await readFile(path.join(dir, '.gitignore'))).toString(), '.vercel\n');
|
||||
t.is((await readFile(path.join(dir, '.gitignore'))).toString(), '.vercel');
|
||||
|
||||
// Ensure .vercel/project.json and .vercel/README.txt are created
|
||||
t.is(
|
||||
@@ -3465,61 +3494,6 @@ test('[vc dev] should show prompts to set up project', async t => {
|
||||
}
|
||||
});
|
||||
|
||||
test('[vc link] should show project prompts but not framework when `builds` defined', async t => {
|
||||
const dir = fixture('project-link-legacy');
|
||||
const projectName = `project-link-legacy-${
|
||||
Math.random().toString(36).split('.')[1]
|
||||
}`;
|
||||
|
||||
// remove previously linked project if it exists
|
||||
await remove(path.join(dir, '.vercel'));
|
||||
|
||||
const vc = execa(binaryPath, ['link', ...defaultArgs], { cwd: dir });
|
||||
|
||||
await waitForPrompt(vc, chunk => /Set up [^?]+\?/.test(chunk));
|
||||
vc.stdin.write('yes\n');
|
||||
|
||||
await waitForPrompt(vc, chunk =>
|
||||
chunk.includes('Which scope should contain your project?')
|
||||
);
|
||||
vc.stdin.write('\n');
|
||||
|
||||
await waitForPrompt(vc, chunk => chunk.includes('Link to existing project?'));
|
||||
vc.stdin.write('no\n');
|
||||
|
||||
await waitForPrompt(vc, chunk =>
|
||||
chunk.includes('What’s your project’s name?')
|
||||
);
|
||||
vc.stdin.write(`${projectName}\n`);
|
||||
|
||||
await waitForPrompt(vc, chunk =>
|
||||
chunk.includes('In which directory is your code located?')
|
||||
);
|
||||
vc.stdin.write('\n');
|
||||
|
||||
await waitForPrompt(vc, chunk => chunk.includes('Linked to'));
|
||||
|
||||
const output = await vc;
|
||||
|
||||
// Ensure the exit code is right
|
||||
t.is(output.exitCode, 0, formatOutput(output));
|
||||
|
||||
// Ensure .gitignore is created
|
||||
t.is((await readFile(path.join(dir, '.gitignore'))).toString(), '.vercel\n');
|
||||
|
||||
// Ensure .vercel/project.json and .vercel/README.txt are created
|
||||
t.is(
|
||||
await exists(path.join(dir, '.vercel', 'project.json')),
|
||||
true,
|
||||
'project.json should be created'
|
||||
);
|
||||
t.is(
|
||||
await exists(path.join(dir, '.vercel', 'README.txt')),
|
||||
true,
|
||||
'README.txt should be created'
|
||||
);
|
||||
});
|
||||
|
||||
test('[vc dev] should send the platform proxy request headers to frontend dev server ', async t => {
|
||||
const dir = fixture('dev-proxy-headers-and-env');
|
||||
const port = 58353;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "8.2.2-canary.4",
|
||||
"version": "8.2.2-canary.0",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -38,7 +38,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.4.3-canary.2",
|
||||
"@vercel/build-utils": "2.4.2",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "3.0.0",
|
||||
|
||||
@@ -80,11 +80,7 @@ export default function buildCreateDeployment(version: number) {
|
||||
rootFiles = [path];
|
||||
}
|
||||
|
||||
let { fileList } = await buildFileTree(
|
||||
path,
|
||||
clientOptions.isDirectory,
|
||||
debug
|
||||
);
|
||||
let fileList = await buildFileTree(path, clientOptions.isDirectory, debug);
|
||||
|
||||
let configPath: string | undefined;
|
||||
if (!nowConfig) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import buildCreateDeployment from './create-deployment';
|
||||
|
||||
export { getVercelIgnore, buildFileTree } from './utils/index';
|
||||
export { getVercelIgnore } from './utils/index';
|
||||
export const createDeployment = buildCreateDeployment(2);
|
||||
export const createLegacyDeployment = buildCreateDeployment(1);
|
||||
export * from './errors';
|
||||
|
||||
@@ -35,7 +35,7 @@ const EVENTS_ARRAY = [
|
||||
'canceled',
|
||||
] as const;
|
||||
|
||||
export type DeploymentEventType = typeof EVENTS_ARRAY[number];
|
||||
export type DeploymentEventType = (typeof EVENTS_ARRAY)[number];
|
||||
export const EVENTS = new Set(EVENTS_ARRAY);
|
||||
|
||||
export function getApiDeploymentsUrl(
|
||||
@@ -69,7 +69,7 @@ export async function parseVercelConfig(filePath?: string): Promise<NowConfig> {
|
||||
}
|
||||
}
|
||||
|
||||
const maybeRead = async function <T>(path: string, default_: T) {
|
||||
const maybeRead = async function<T>(path: string, default_: T) {
|
||||
try {
|
||||
return await readFile(path, 'utf8');
|
||||
} catch (err) {
|
||||
@@ -81,26 +81,19 @@ export async function buildFileTree(
|
||||
path: string | string[],
|
||||
isDirectory: boolean,
|
||||
debug: Debug
|
||||
): Promise<{ fileList: string[]; ignoreList: string[] }> {
|
||||
const ignoreList: string[] = [];
|
||||
): Promise<string[]> {
|
||||
let fileList: string[];
|
||||
let { ig, ignores } = await getVercelIgnore(path);
|
||||
let { ig } = await getVercelIgnore(path);
|
||||
|
||||
debug(`Found ${ignores.length} rules in .vercelignore`);
|
||||
debug(`Found ${ig.ignores.length} rules in .vercelignore`);
|
||||
debug('Building file tree...');
|
||||
|
||||
if (isDirectory && !Array.isArray(path)) {
|
||||
// Directory path
|
||||
const ignores = (absPath: string) => {
|
||||
const rel = relative(path, absPath);
|
||||
const ignored = ig.ignores(rel);
|
||||
if (ignored) {
|
||||
ignoreList.push(rel);
|
||||
}
|
||||
return ignored;
|
||||
};
|
||||
const cwd = process.cwd();
|
||||
const ignores = (absPath: string) => ig.ignores(relative(cwd, absPath));
|
||||
fileList = await readdir(path, [ignores]);
|
||||
debug(`Found ${fileList.length} files in the specified directory`);
|
||||
debug(`Read ${fileList.length} files in the specified directory`);
|
||||
} else if (Array.isArray(path)) {
|
||||
// Array of file paths
|
||||
fileList = path;
|
||||
@@ -111,7 +104,7 @@ export async function buildFileTree(
|
||||
debug(`Deploying the provided path as single file`);
|
||||
}
|
||||
|
||||
return { fileList, ignoreList };
|
||||
return fileList;
|
||||
}
|
||||
|
||||
export async function getVercelIgnore(
|
||||
|
||||
@@ -14,30 +14,18 @@ const normalizeWindowsPaths = (files: string[]) => {
|
||||
const toAbsolutePaths = (cwd: string, files: string[]) =>
|
||||
files.map(p => join(cwd, p));
|
||||
|
||||
describe('buildFileTree()', () => {
|
||||
describe('buildFileTree', () => {
|
||||
it('should exclude files using `.nowignore` blocklist', async () => {
|
||||
const cwd = fixture('nowignore');
|
||||
const { fileList, ignoreList } = await buildFileTree(cwd, true, noop);
|
||||
|
||||
const expectedFileList = toAbsolutePaths(cwd, ['.nowignore', 'index.txt']);
|
||||
expect(normalizeWindowsPaths(expectedFileList).sort()).toEqual(
|
||||
normalizeWindowsPaths(fileList).sort()
|
||||
);
|
||||
|
||||
const expectedIgnoreList = [
|
||||
'ignore.txt',
|
||||
'folder/ignore.txt',
|
||||
'node_modules/ignore.txt',
|
||||
];
|
||||
expect(normalizeWindowsPaths(expectedIgnoreList).sort()).toEqual(
|
||||
normalizeWindowsPaths(ignoreList).sort()
|
||||
const expected = toAbsolutePaths(cwd, ['.nowignore', 'index.txt']);
|
||||
const actual = await buildFileTree(cwd, true, noop);
|
||||
expect(normalizeWindowsPaths(expected).sort()).toEqual(
|
||||
normalizeWindowsPaths(actual).sort()
|
||||
);
|
||||
});
|
||||
|
||||
it('should include the node_modules using `.vercelignore` allowlist', async () => {
|
||||
const cwd = fixture('vercelignore-allow-nodemodules');
|
||||
const { fileList, ignoreList } = await buildFileTree(cwd, true, noop);
|
||||
|
||||
const expected = toAbsolutePaths(cwd, [
|
||||
'node_modules/one.txt',
|
||||
'sub/node_modules/two.txt',
|
||||
@@ -45,13 +33,9 @@ describe('buildFileTree()', () => {
|
||||
'.vercelignore',
|
||||
'hello.txt',
|
||||
]);
|
||||
const actual = await buildFileTree(cwd, true, noop);
|
||||
expect(normalizeWindowsPaths(expected).sort()).toEqual(
|
||||
normalizeWindowsPaths(fileList).sort()
|
||||
);
|
||||
|
||||
const expectedIgnoreList = ['.env.local', 'exclude.txt'];
|
||||
expect(normalizeWindowsPaths(expectedIgnoreList).sort()).toEqual(
|
||||
normalizeWindowsPaths(ignoreList).sort()
|
||||
normalizeWindowsPaths(actual).sort()
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "2.6.19",
|
||||
"version": "2.6.14-canary.1",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
|
||||
@@ -26,7 +26,7 @@
|
||||
"@types/resolve-from": "5.0.1",
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/yazl": "2.4.1",
|
||||
"@zeit/node-file-trace": "0.8.2",
|
||||
"@zeit/node-file-trace": "0.8.0",
|
||||
"async-sema": "3.0.1",
|
||||
"buffer-crc32": "0.2.13",
|
||||
"escape-string-regexp": "3.0.0",
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
import buildUtils from './build-utils';
|
||||
import url from 'url';
|
||||
const {
|
||||
createLambda,
|
||||
debug,
|
||||
download,
|
||||
getLambdaOptionsFromFunction,
|
||||
getNodeVersion,
|
||||
getSpawnOptions,
|
||||
getScriptName,
|
||||
glob,
|
||||
runNpmInstall,
|
||||
runPackageJsonScript,
|
||||
execCommand,
|
||||
getNodeBinPath,
|
||||
} = buildUtils;
|
||||
|
||||
import {
|
||||
BuildOptions,
|
||||
Config,
|
||||
@@ -17,17 +34,13 @@ import {
|
||||
convertRewrites,
|
||||
} from '@vercel/routing-utils/dist/superstatic';
|
||||
import { nodeFileTrace, NodeFileTraceReasons } from '@zeit/node-file-trace';
|
||||
import { Sema } from 'async-sema';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
import escapeStringRegexp from 'escape-string-regexp';
|
||||
import findUp from 'find-up';
|
||||
import { lstat, pathExists, readFile, remove, writeFile } from 'fs-extra';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import resolveFrom from 'resolve-from';
|
||||
import semver from 'semver';
|
||||
import url from 'url';
|
||||
import buildUtils from './build-utils';
|
||||
import createServerlessConfig from './create-serverless-config';
|
||||
import nextLegacyVersions from './legacy-versions';
|
||||
import {
|
||||
@@ -53,20 +66,8 @@ import {
|
||||
syncEnvVars,
|
||||
validateEntrypoint,
|
||||
} from './utils';
|
||||
const {
|
||||
createLambda,
|
||||
debug,
|
||||
download,
|
||||
getLambdaOptionsFromFunction,
|
||||
getNodeVersion,
|
||||
getSpawnOptions,
|
||||
getScriptName,
|
||||
glob,
|
||||
runNpmInstall,
|
||||
runPackageJsonScript,
|
||||
execCommand,
|
||||
getNodeBinPath,
|
||||
} = buildUtils;
|
||||
import findUp from 'find-up';
|
||||
import { Sema } from 'async-sema';
|
||||
|
||||
interface BuildParamsMeta {
|
||||
isDev: boolean | undefined;
|
||||
@@ -234,7 +235,7 @@ export const build = async ({
|
||||
|
||||
await download(files, workPath, meta);
|
||||
|
||||
let pkg = await readPackageJson(entryPath);
|
||||
const pkg = await readPackageJson(entryPath);
|
||||
const nextVersionRange = await getNextVersionRange(entryPath);
|
||||
const nodeVersion = await getNodeVersion(entryPath, undefined, config, meta);
|
||||
const spawnOpts = getSpawnOptions(meta, nodeVersion);
|
||||
@@ -330,17 +331,17 @@ export const build = async ({
|
||||
]);
|
||||
|
||||
debug('Normalizing package.json');
|
||||
pkg = normalizePackageJson(pkg);
|
||||
debug('Normalized package.json result: ', pkg);
|
||||
await writePackageJson(entryPath, pkg);
|
||||
const packageJson = normalizePackageJson(pkg);
|
||||
debug('Normalized package.json result: ', packageJson);
|
||||
await writePackageJson(entryPath, packageJson);
|
||||
}
|
||||
|
||||
let buildScriptName = getScriptName(pkg, [
|
||||
const buildScriptName = getScriptName(pkg, [
|
||||
'vercel-build',
|
||||
'now-build',
|
||||
'build',
|
||||
]);
|
||||
const { buildCommand } = config;
|
||||
let { buildCommand } = config;
|
||||
|
||||
if (!buildScriptName && !buildCommand) {
|
||||
console.log(
|
||||
@@ -348,15 +349,7 @@ export const build = async ({
|
||||
'If you need to define a different build step, please create a `vercel-build` script in your `package.json` ' +
|
||||
'(e.g. `{ "scripts": { "vercel-build": "npm run prepare && next build" } }`).'
|
||||
);
|
||||
|
||||
await writePackageJson(entryPath, {
|
||||
...pkg,
|
||||
scripts: {
|
||||
'vercel-build': 'next build',
|
||||
...pkg.scripts,
|
||||
},
|
||||
});
|
||||
buildScriptName = 'vercel-build';
|
||||
buildCommand = 'next build';
|
||||
}
|
||||
|
||||
if (process.env.NPM_AUTH_TOKEN) {
|
||||
@@ -445,7 +438,7 @@ export const build = async ({
|
||||
for (const dataRoute of routesManifest.dataRoutes) {
|
||||
const ssgDataRoute =
|
||||
prerenderManifest.fallbackRoutes[dataRoute.page] ||
|
||||
prerenderManifest.blockingFallbackRoutes[dataRoute.page];
|
||||
prerenderManifest.legacyBlockingRoutes[dataRoute.page];
|
||||
|
||||
// we don't need to add routes for non-lazy SSG routes since
|
||||
// they have outputs which would override the routes anyways
|
||||
@@ -885,15 +878,9 @@ export const build = async ({
|
||||
initialRevalidate === false &&
|
||||
!canUsePreviewMode &&
|
||||
!prerenderManifest.fallbackRoutes[route] &&
|
||||
!prerenderManifest.blockingFallbackRoutes[route]
|
||||
!prerenderManifest.legacyBlockingRoutes[route]
|
||||
) {
|
||||
// if the 404 page used getStaticProps we need to update static404Page
|
||||
// since it wasn't populated from the staticPages group
|
||||
if (route === '/404') {
|
||||
static404Page = path.join(entryDirectory, '404');
|
||||
}
|
||||
|
||||
nonLambdaSsgPages.add(route === '/' ? '/index' : route);
|
||||
nonLambdaSsgPages.add(route);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1130,7 +1117,7 @@ export const build = async ({
|
||||
src: `^${escapeStringRegexp(outputName).replace(
|
||||
/\/index$/,
|
||||
'(/|/index|)'
|
||||
)}/?$`,
|
||||
)}$`,
|
||||
dest: `${path.join('/', currentLambdaGroup.lambdaIdentifier)}`,
|
||||
headers: {
|
||||
'x-nextjs-page': outputName,
|
||||
@@ -1310,7 +1297,7 @@ export const build = async ({
|
||||
if (!toRender) {
|
||||
try {
|
||||
const { pathname } = url.parse(req.url)
|
||||
toRender = pathname.replace(/\\/$/, '')
|
||||
toRender = pathname
|
||||
} catch (_) {
|
||||
// handle failing to parse url
|
||||
res.statusCode = 400
|
||||
@@ -1465,7 +1452,7 @@ export const build = async ({
|
||||
if (isFallback || isBlocking) {
|
||||
const pr = isFallback
|
||||
? prerenderManifest.fallbackRoutes[routeKey]
|
||||
: prerenderManifest.blockingFallbackRoutes[routeKey];
|
||||
: prerenderManifest.legacyBlockingRoutes[routeKey];
|
||||
initialRevalidate = 1; // TODO: should Next.js provide this default?
|
||||
// @ts-ignore
|
||||
if (initialRevalidate === false) {
|
||||
@@ -1556,7 +1543,7 @@ export const build = async ({
|
||||
Object.keys(prerenderManifest.fallbackRoutes).forEach(route =>
|
||||
onPrerenderRoute(route, { isBlocking: false, isFallback: true })
|
||||
);
|
||||
Object.keys(prerenderManifest.blockingFallbackRoutes).forEach(route =>
|
||||
Object.keys(prerenderManifest.legacyBlockingRoutes).forEach(route =>
|
||||
onPrerenderRoute(route, { isBlocking: true, isFallback: false })
|
||||
);
|
||||
|
||||
@@ -1566,7 +1553,7 @@ export const build = async ({
|
||||
// Dynamic pages for lazy routes should be handled by the lambda flow.
|
||||
[
|
||||
...Object.entries(prerenderManifest.fallbackRoutes),
|
||||
...Object.entries(prerenderManifest.blockingFallbackRoutes),
|
||||
...Object.entries(prerenderManifest.legacyBlockingRoutes),
|
||||
].forEach(([, { dataRouteRegex, dataRoute }]) => {
|
||||
dataRoutes.push({
|
||||
// Next.js provided data route regex
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { FileFsRef, Files } from '@vercel/build-utils';
|
||||
import { NowHeader, NowRewrite, Route, Source } from '@vercel/routing-utils';
|
||||
import { Sema } from 'async-sema';
|
||||
import crc32 from 'buffer-crc32';
|
||||
import fs from 'fs-extra';
|
||||
import zlib from 'zlib';
|
||||
import path from 'path';
|
||||
import resolveFrom from 'resolve-from';
|
||||
import fs from 'fs-extra';
|
||||
import semver from 'semver';
|
||||
import { ZipFile } from 'yazl';
|
||||
import zlib from 'zlib';
|
||||
import crc32 from 'buffer-crc32';
|
||||
import { Sema } from 'async-sema';
|
||||
import resolveFrom from 'resolve-from';
|
||||
import buildUtils from './build-utils';
|
||||
const { streamToBuffer, Lambda, NowBuildError, isSymbolicLink } = buildUtils;
|
||||
import { Files, FileFsRef } from '@vercel/build-utils';
|
||||
import { Route, Source, NowHeader, NowRewrite } from '@vercel/routing-utils';
|
||||
|
||||
type stringMap = { [key: string]: string };
|
||||
|
||||
@@ -198,7 +198,7 @@ async function getRoutes(
|
||||
// If default pages dir isn't found check for `src/pages`
|
||||
if (
|
||||
!pagesDir &&
|
||||
fileKeys.some(file =>
|
||||
fileKeys.some((file) =>
|
||||
file.startsWith(path.join(entryDirectory, 'src/pages'))
|
||||
)
|
||||
) {
|
||||
@@ -260,7 +260,7 @@ async function getRoutes(
|
||||
entryDirectory,
|
||||
dynamicPages,
|
||||
true
|
||||
).then(arr =>
|
||||
).then((arr) =>
|
||||
arr.map((route: Source) => {
|
||||
// convert to make entire RegExp match as one group
|
||||
route.src = route.src
|
||||
@@ -287,7 +287,7 @@ async function getRoutes(
|
||||
};
|
||||
|
||||
// Only add the route if a page is not already using it
|
||||
if (!routes.some(r => (r as Source).src === route.src)) {
|
||||
if (!routes.some((r) => (r as Source).src === route.src)) {
|
||||
routes.push(route);
|
||||
}
|
||||
}
|
||||
@@ -420,7 +420,7 @@ export async function getDynamicRoutes(
|
||||
dest: `${!isDev ? path.join('/', entryDirectory, page) : page}${
|
||||
routeKeys
|
||||
? `?${Object.keys(routeKeys)
|
||||
.map(key => `${routeKeys[key]}=$${key}`)
|
||||
.map((key) => `${routeKeys[key]}=$${key}`)
|
||||
.join('&')}`
|
||||
: ''
|
||||
}`,
|
||||
@@ -479,13 +479,13 @@ export async function getDynamicRoutes(
|
||||
});
|
||||
}
|
||||
|
||||
const pageMatchers = getSortedRoutes(dynamicPages).map(pageName => ({
|
||||
const pageMatchers = getSortedRoutes(dynamicPages).map((pageName) => ({
|
||||
pageName,
|
||||
matcher: getRouteRegex && getRouteRegex(pageName).re,
|
||||
}));
|
||||
|
||||
const routes: Source[] = [];
|
||||
pageMatchers.forEach(pageMatcher => {
|
||||
pageMatchers.forEach((pageMatcher) => {
|
||||
// in `vercel dev` we don't need to prefix the destination
|
||||
const dest = !isDev
|
||||
? path.join('/', entryDirectory, pageMatcher.pageName)
|
||||
@@ -693,7 +693,7 @@ export type NextPrerenderedRoutes = {
|
||||
};
|
||||
};
|
||||
|
||||
blockingFallbackRoutes: {
|
||||
legacyBlockingRoutes: {
|
||||
[route: string]: {
|
||||
routeRegex: string;
|
||||
dataRoute: string;
|
||||
@@ -797,7 +797,7 @@ export async function getPrerenderManifest(
|
||||
if (!hasManifest) {
|
||||
return {
|
||||
staticRoutes: {},
|
||||
blockingFallbackRoutes: {},
|
||||
legacyBlockingRoutes: {},
|
||||
fallbackRoutes: {},
|
||||
bypassToken: null,
|
||||
omittedRoutes: [],
|
||||
@@ -855,14 +855,14 @@ export async function getPrerenderManifest(
|
||||
|
||||
const ret: NextPrerenderedRoutes = {
|
||||
staticRoutes: {},
|
||||
blockingFallbackRoutes: {},
|
||||
legacyBlockingRoutes: {},
|
||||
fallbackRoutes: {},
|
||||
bypassToken:
|
||||
(manifest.preview && manifest.preview.previewModeId) || null,
|
||||
omittedRoutes: [],
|
||||
};
|
||||
|
||||
routes.forEach(route => {
|
||||
routes.forEach((route) => {
|
||||
const {
|
||||
initialRevalidateSeconds,
|
||||
dataRoute,
|
||||
@@ -878,7 +878,7 @@ export async function getPrerenderManifest(
|
||||
};
|
||||
});
|
||||
|
||||
lazyRoutes.forEach(lazyRoute => {
|
||||
lazyRoutes.forEach((lazyRoute) => {
|
||||
const {
|
||||
routeRegex,
|
||||
fallback,
|
||||
@@ -894,7 +894,7 @@ export async function getPrerenderManifest(
|
||||
dataRouteRegex,
|
||||
};
|
||||
} else {
|
||||
ret.blockingFallbackRoutes[lazyRoute] = {
|
||||
ret.legacyBlockingRoutes[lazyRoute] = {
|
||||
routeRegex,
|
||||
dataRoute,
|
||||
dataRouteRegex,
|
||||
@@ -910,13 +910,13 @@ export async function getPrerenderManifest(
|
||||
|
||||
const ret: NextPrerenderedRoutes = {
|
||||
staticRoutes: {},
|
||||
blockingFallbackRoutes: {},
|
||||
legacyBlockingRoutes: {},
|
||||
fallbackRoutes: {},
|
||||
bypassToken: manifest.preview.previewModeId,
|
||||
omittedRoutes: [],
|
||||
};
|
||||
|
||||
routes.forEach(route => {
|
||||
routes.forEach((route) => {
|
||||
const {
|
||||
initialRevalidateSeconds,
|
||||
dataRoute,
|
||||
@@ -932,7 +932,7 @@ export async function getPrerenderManifest(
|
||||
};
|
||||
});
|
||||
|
||||
lazyRoutes.forEach(lazyRoute => {
|
||||
lazyRoutes.forEach((lazyRoute) => {
|
||||
const {
|
||||
routeRegex,
|
||||
fallback,
|
||||
@@ -940,24 +940,19 @@ export async function getPrerenderManifest(
|
||||
dataRouteRegex,
|
||||
} = manifest.dynamicRoutes[lazyRoute];
|
||||
|
||||
if (typeof fallback === 'string') {
|
||||
ret.fallbackRoutes[lazyRoute] = {
|
||||
routeRegex,
|
||||
fallback,
|
||||
dataRoute,
|
||||
dataRouteRegex,
|
||||
};
|
||||
} else if (fallback === null) {
|
||||
ret.blockingFallbackRoutes[lazyRoute] = {
|
||||
routeRegex,
|
||||
dataRoute,
|
||||
dataRouteRegex,
|
||||
};
|
||||
} else {
|
||||
if (!fallback) {
|
||||
// Fallback behavior is disabled, all routes would've been provided
|
||||
// in the top-level `routes` key (`staticRoutes`).
|
||||
ret.omittedRoutes.push(lazyRoute);
|
||||
return;
|
||||
}
|
||||
|
||||
ret.fallbackRoutes[lazyRoute] = {
|
||||
routeRegex,
|
||||
fallback,
|
||||
dataRoute,
|
||||
dataRouteRegex,
|
||||
};
|
||||
});
|
||||
|
||||
return ret;
|
||||
@@ -965,7 +960,7 @@ export async function getPrerenderManifest(
|
||||
default: {
|
||||
return {
|
||||
staticRoutes: {},
|
||||
blockingFallbackRoutes: {},
|
||||
legacyBlockingRoutes: {},
|
||||
fallbackRoutes: {},
|
||||
bypassToken: null,
|
||||
omittedRoutes: [],
|
||||
|
||||
@@ -1,6 +1 @@
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
trailingSlash: true,
|
||||
};
|
||||
module.exports = { trailingSlash: true };
|
||||
|
||||
@@ -33,48 +33,6 @@
|
||||
"path": "/test.txt",
|
||||
"status": 200,
|
||||
"mustContain": "this is a file"
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/blog/post-1",
|
||||
"status": 308,
|
||||
"responseHeaders": {
|
||||
"refresh": "/url=/blog/post-1/$/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/blog/post-1/",
|
||||
"status": 200,
|
||||
"mustContain": "post: <!-- -->post-1"
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/_next/data/testing-build-id/blog/post-1.json/",
|
||||
"status": 308,
|
||||
"responseHeaders": {
|
||||
"refresh": "/url=/_next/data/testing-build-id/blog/post-1.json$/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/_next/data/testing-build-id/blog/post-1.json",
|
||||
"status": 200,
|
||||
"mustContain": "\"post-1\""
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/api/hello",
|
||||
"status": 308,
|
||||
"responseHeaders": {
|
||||
"refresh": "/url=/api/hello/$/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/api/hello/",
|
||||
"status": 200,
|
||||
"mustContain": "hello from API"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
export default function Page() {
|
||||
return <p>nested page</p>;
|
||||
}
|
||||
|
||||
export const getServerSideProps = () => {
|
||||
return {
|
||||
props: {
|
||||
hello: 'world',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export default (req, res) => {
|
||||
res.end('hello from API');
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
export default function Page({ post }) {
|
||||
return <p>post: {post}</p>;
|
||||
}
|
||||
|
||||
export const getServerSideProps = ({ params }) => {
|
||||
return {
|
||||
props: {
|
||||
post: params.post,
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -3,13 +3,13 @@
|
||||
"builds": [{ "src": "package.json", "use": "@vercel/next" }],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/_next/__NEXT_SCRIPT__(/)",
|
||||
"path": "/_next/static/testing-build-id/pages/index.js",
|
||||
"responseHeaders": {
|
||||
"cache-control": "public,max-age=31536000,immutable"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/static/invalid-build-id/pages/non-existent.js",
|
||||
"path": "/_next/static/invalid-build-id/pages/index.js",
|
||||
"notResponseHeaders": {
|
||||
"cache-control": "public,max-age=31536000,immutable"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"next": "latest",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"next": "^9.1.2-canary.8",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"next": "9.2.1-canary.3",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"next": "latest",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"build": "next build"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"next": "^9.1.2-canary.8",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user