mirror of
https://github.com/LukeHagar/vercel.git
synced 2026-01-01 04:09:15 +00:00
Compare commits
23 Commits
@vercel/ne
...
vercel@19.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e19446f89c | ||
|
|
ea3233502d | ||
|
|
0a8810b64f | ||
|
|
f6b373f0f4 | ||
|
|
7ad2a99cd7 | ||
|
|
0349eea494 | ||
|
|
ed4d006fb7 | ||
|
|
12a9d203e9 | ||
|
|
ac1f4cf789 | ||
|
|
b284ca350a | ||
|
|
a43bf6c912 | ||
|
|
62410806bb | ||
|
|
47e3111cab | ||
|
|
135f35002f | ||
|
|
ee40052cee | ||
|
|
86b730c1cd | ||
|
|
b440249c26 | ||
|
|
5380c12569 | ||
|
|
f11eb32b2c | ||
|
|
3d40e343ac | ||
|
|
80f525796f | ||
|
|
af4ad358f2 | ||
|
|
e6033d7a2d |
4
examples/blitzjs/.babelrc.js
Normal file
4
examples/blitzjs/.babelrc.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
presets: ["next/babel"],
|
||||
plugins: [],
|
||||
}
|
||||
10
examples/blitzjs/.eslintrc.js
Normal file
10
examples/blitzjs/.eslintrc.js
Normal file
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
extends: ["react-app", "plugin:jsx-a11y/recommended"],
|
||||
plugins: ["jsx-a11y"],
|
||||
rules: {
|
||||
"import/no-anonymous-default-export": "error",
|
||||
"import/no-webpack-loader-syntax": "off",
|
||||
"react/react-in-jsx-scope": "off", // React is always in scope with Blitz
|
||||
"jsx-a11y/anchor-is-valid": "off", //Doesn't play well with Blitz/Next <Link> usage
|
||||
},
|
||||
}
|
||||
56
examples/blitzjs/.gitignore
vendored
Normal file
56
examples/blitzjs/.gitignore
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
# dependencies
|
||||
node_modules
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.pnp.*
|
||||
.npm
|
||||
web_modules/
|
||||
|
||||
# blitz
|
||||
/.blitz/
|
||||
/.next/
|
||||
*.sqlite
|
||||
.now
|
||||
.vercel
|
||||
.blitz-console-history
|
||||
blitz-log.log
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
||||
# local env files
|
||||
.env
|
||||
.envrc
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Testing
|
||||
coverage
|
||||
*.lcov
|
||||
.nyc_output
|
||||
lib-cov
|
||||
|
||||
# Caches
|
||||
*.tsbuildinfo
|
||||
.eslintcache
|
||||
.node_repl_history
|
||||
.yarn-integrity
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
1
examples/blitzjs/.npmrc
Normal file
1
examples/blitzjs/.npmrc
Normal file
@@ -0,0 +1 @@
|
||||
save-exact=true
|
||||
6
examples/blitzjs/.prettierignore
Normal file
6
examples/blitzjs/.prettierignore
Normal file
@@ -0,0 +1,6 @@
|
||||
.gitkeep
|
||||
.env
|
||||
*.ico
|
||||
*.lock
|
||||
db/migrations
|
||||
|
||||
21
examples/blitzjs/README.md
Normal file
21
examples/blitzjs/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||

|
||||
|
||||
This is a [Blitz.js](https://blitzjs.com/) project bootstrapped with `blitz new`.
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npx blitz start
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Blitz.js, view [Blitzjs.com](https://blitzjs.com)
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
View the [documentation on deploying to Vercel](https://blitzjs.com/docs/deploy-vercel)
|
||||
0
examples/blitzjs/app/components/.keep
Normal file
0
examples/blitzjs/app/components/.keep
Normal file
21
examples/blitzjs/app/components/ErrorBoundary.tsx
Normal file
21
examples/blitzjs/app/components/ErrorBoundary.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from "react"
|
||||
|
||||
export default class ErrorBoundary extends React.Component<{
|
||||
fallback: (error: any) => React.ReactNode
|
||||
}> {
|
||||
state = { hasError: false, error: null }
|
||||
|
||||
static getDerivedStateFromError(error: any) {
|
||||
return {
|
||||
hasError: true,
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return this.props.fallback(this.state.error)
|
||||
}
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
0
examples/blitzjs/app/layouts/.keep
Normal file
0
examples/blitzjs/app/layouts/.keep
Normal file
5
examples/blitzjs/app/pages/_app.tsx
Normal file
5
examples/blitzjs/app/pages/_app.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { AppProps } from "blitz"
|
||||
|
||||
export default function MyApp({ Component, pageProps }: AppProps) {
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
23
examples/blitzjs/app/pages/_document.tsx
Normal file
23
examples/blitzjs/app/pages/_document.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Document, Html, DocumentHead, Main, BlitzScript /*DocumentContext*/ } from "blitz"
|
||||
|
||||
class MyDocument extends Document {
|
||||
// Only uncomment if you need to customize this behaviour
|
||||
// static async getInitialProps(ctx: DocumentContext) {
|
||||
// const initialProps = await Document.getInitialProps(ctx)
|
||||
// return {...initialProps}
|
||||
// }
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<DocumentHead />
|
||||
<body>
|
||||
<Main />
|
||||
<BlitzScript />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default MyDocument
|
||||
197
examples/blitzjs/app/pages/index.tsx
Normal file
197
examples/blitzjs/app/pages/index.tsx
Normal file
@@ -0,0 +1,197 @@
|
||||
import { Head, Link } from "blitz"
|
||||
|
||||
const Home = () => (
|
||||
<div className="container">
|
||||
<Head>
|
||||
<title>blitzjs</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<main>
|
||||
<div className="logo">
|
||||
<img src="/logo.png" alt="blitz.js" />
|
||||
</div>
|
||||
<p>1. Run this command in your terminal:</p>
|
||||
<pre>
|
||||
<code>blitz generate all project name:string</code>
|
||||
</pre>
|
||||
<p>2. Then run this command:</p>
|
||||
<pre>
|
||||
<code>blitz db migrate</code>
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
3. Go to{" "}
|
||||
<Link href="/projects">
|
||||
<a>/projects</a>
|
||||
</Link>
|
||||
</p>
|
||||
<div className="buttons">
|
||||
<a
|
||||
className="button"
|
||||
href="https://github.com/blitz-js/blitz/blob/master/USER_GUIDE.md?utm_source=blitz-new&utm_medium=app-template&utm_campaign=blitz-new"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Documentation
|
||||
</a>
|
||||
<a
|
||||
className="button-outline"
|
||||
href="https://github.com/blitz-js/blitz"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Github Repo
|
||||
</a>
|
||||
<a
|
||||
className="button-outline"
|
||||
href="https://slack.blitzjs.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Slack Community
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<a
|
||||
href="https://blitzjs.com?utm_source=blitz-new&utm_medium=app-template&utm_campaign=blitz-new"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Powered by Blitz.js
|
||||
</a>
|
||||
</footer>
|
||||
|
||||
<style jsx>{`
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 5rem 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
main p {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
border-top: 1px solid #eaeaea;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #45009d;
|
||||
}
|
||||
|
||||
footer a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: #f4f4f4;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.logo img {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
grid-gap: 0.5rem;
|
||||
margin-top: 6rem;
|
||||
}
|
||||
|
||||
a.button {
|
||||
background-color: #6700eb;
|
||||
padding: 1rem 2rem;
|
||||
color: #f4f4f4;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a.button:hover {
|
||||
background-color: #45009d;
|
||||
}
|
||||
|
||||
a.button-outline {
|
||||
border: 2px solid #6700eb;
|
||||
padding: 1rem 2rem;
|
||||
color: #6700eb;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a.button-outline:hover {
|
||||
border-color: #45009d;
|
||||
color: #45009d;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #fafafa;
|
||||
border-radius: 5px;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
code {
|
||||
font-size: 0.9rem;
|
||||
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
|
||||
Bitstream Vera Sans Mono, Courier New, monospace;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
max-width: 800px;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.grid {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<style jsx global>{`
|
||||
@import url("https://fonts.googleapis.com/css2?family=Libre+Franklin:wght@300;700&display=swap");
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: "Libre Franklin", -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default Home
|
||||
15
examples/blitzjs/blitz.config.js
Normal file
15
examples/blitzjs/blitz.config.js
Normal file
@@ -0,0 +1,15 @@
|
||||
module.exports = {
|
||||
/*
|
||||
webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
|
||||
// Note: we provide webpack above so you should not `require` it
|
||||
// Perform customizations to webpack config
|
||||
// Important: return the modified config
|
||||
return config
|
||||
},
|
||||
webpackDevMiddleware: (config) => {
|
||||
// Perform customizations to webpack dev middleware config
|
||||
// Important: return the modified config
|
||||
return config
|
||||
},
|
||||
*/
|
||||
}
|
||||
15
examples/blitzjs/db/index.ts
Normal file
15
examples/blitzjs/db/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
export * from "@prisma/client"
|
||||
|
||||
let prisma: PrismaClient
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
prisma = new PrismaClient()
|
||||
} else {
|
||||
// Ensure the prisma instance is re-used during hot-reloading
|
||||
// Otherwise, a new client will be created on every reload
|
||||
global["prisma"] = global["prisma"] || new PrismaClient()
|
||||
prisma = global["prisma"]
|
||||
}
|
||||
|
||||
export default prisma
|
||||
0
examples/blitzjs/db/migrations/.keep
Normal file
0
examples/blitzjs/db/migrations/.keep
Normal file
27
examples/blitzjs/db/schema.prisma
Normal file
27
examples/blitzjs/db/schema.prisma
Normal file
@@ -0,0 +1,27 @@
|
||||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
datasource sqlite {
|
||||
provider = "sqlite"
|
||||
url = "file:./db.sqlite"
|
||||
}
|
||||
|
||||
// SQLite is easy to start with, but if you use Postgres in production
|
||||
// you should also use it in development with the following:
|
||||
//datasource postgresql {
|
||||
// provider = "postgresql"
|
||||
// url = env("DATABASE_URL")
|
||||
//}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------
|
||||
|
||||
//model Project {
|
||||
// id Int @default(autoincrement()) @id
|
||||
// name String
|
||||
//}
|
||||
|
||||
0
examples/blitzjs/integrations/.keep
Normal file
0
examples/blitzjs/integrations/.keep
Normal file
55
examples/blitzjs/package.json
Normal file
55
examples/blitzjs/package.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "blitzjs",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"start": "blitz start",
|
||||
"studio": "blitz db studio",
|
||||
"build": "blitz build",
|
||||
"lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .",
|
||||
"test": "echo \"No tests yet\""
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults"
|
||||
],
|
||||
"prettier": {
|
||||
"semi": false,
|
||||
"printWidth": 100
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged && pretty-quick --staged",
|
||||
"pre-push": "blitz test"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,ts,tsx}": [
|
||||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/cli": "latest",
|
||||
"@prisma/client": "latest",
|
||||
"blitz": "latest",
|
||||
"react": "experimental",
|
||||
"react-dom": "experimental"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "16.9.36",
|
||||
"@typescript-eslint/eslint-plugin": "2.x",
|
||||
"@typescript-eslint/parser": "2.x",
|
||||
"babel-eslint": "10.1.0",
|
||||
"eslint": "6.8.0",
|
||||
"eslint-config-react-app": "5.2.1",
|
||||
"eslint-plugin-flowtype": "4.7.0",
|
||||
"eslint-plugin-import": "2.21.2",
|
||||
"eslint-plugin-jsx-a11y": "6.2.3",
|
||||
"eslint-plugin-react": "7.20.0",
|
||||
"eslint-plugin-react-hooks": "3.0.0",
|
||||
"husky": "4.2.5",
|
||||
"lint-staged": "10.2.10",
|
||||
"prettier": "2.0.5",
|
||||
"pretty-quick": "2.0.1",
|
||||
"typescript": "3.9.5"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
BIN
examples/blitzjs/public/favicon.ico
Executable file
BIN
examples/blitzjs/public/favicon.ico
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 556 B |
BIN
examples/blitzjs/public/logo.png
Normal file
BIN
examples/blitzjs/public/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
20
examples/blitzjs/tsconfig.json
Normal file
20
examples/blitzjs/tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"baseUrl": "./",
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"exclude": ["node_modules"],
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
|
||||
}
|
||||
0
examples/blitzjs/utils/.keep
Normal file
0
examples/blitzjs/utils/.keep
Normal file
@@ -64,7 +64,8 @@
|
||||
},
|
||||
"prettier": {
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true
|
||||
"singleQuote": true,
|
||||
"arrowParens": "avoid"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
||||
@@ -1,4 +1,32 @@
|
||||
[
|
||||
{
|
||||
"name": "Blitz.js",
|
||||
"slug": "blitzjs",
|
||||
"demo": "https://blitzjs.now-examples.now.sh",
|
||||
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/blitz.svg",
|
||||
"tagline": "Blitz.js: The Fullstack React Framework",
|
||||
"description": "A brand new Blitz.js app: the output of running `blitz new`",
|
||||
"website": "https://blitzjs.com",
|
||||
"detectors": {
|
||||
"every": [
|
||||
{
|
||||
"path": "package.json",
|
||||
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"blitz\":\\s*\".+?\"[^}]*}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `blitz build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "blitz start"
|
||||
},
|
||||
"outputDirectory": {
|
||||
"placeholder": "Next.js default"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Next.js",
|
||||
"slug": "nextjs",
|
||||
|
||||
30
packages/frameworks/logos/blitz.svg
Normal file
30
packages/frameworks/logos/blitz.svg
Normal file
@@ -0,0 +1,30 @@
|
||||
<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M95.4242 249.857H173.991C203.89 249.857 232.049 263.909 250.026 287.799L327.526 390.789C328.991 392.736 329.212 395.349 328.095 397.513L283.421 484.069C281.278 488.221 275.532 488.71 272.719 484.978L95.4242 249.857Z" fill="url(#paint0_linear)"/>
|
||||
<g filter="url(#filter0_d)">
|
||||
<path d="M404.558 249.991H325.991C296.093 249.991 267.933 235.939 249.956 212.049L172.456 109.059C170.991 107.112 170.771 104.499 171.888 102.335L216.561 15.7794C218.705 11.6267 224.45 11.1382 227.264 14.8695L404.558 249.991Z" fill="url(#paint1_linear)"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d" x="71.1812" y="-39.6553" width="433.377" height="437.646" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dy="48"/>
|
||||
<feGaussianBlur stdDeviation="50"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.270588 0 0 0 0 0 0 0 0 0 0.615686 0 0 0 0.2 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear" x1="163.936" y1="392.775" x2="316.429" y2="155.244" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#6700EB"/>
|
||||
<stop offset="1" stop-color="#45009D"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="336.047" y1="107.073" x2="183.554" y2="344.604" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#6700EB"/>
|
||||
<stop offset="1" stop-color="#45009D"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0">
|
||||
<rect width="500" height="500" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/frameworks",
|
||||
"version": "0.0.16-canary.0",
|
||||
"version": "0.0.16",
|
||||
"main": "frameworks.json",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "2.4.1-canary.1",
|
||||
"version": "2.4.1",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -462,7 +462,7 @@ function detectFrontBuilder(
|
||||
});
|
||||
}
|
||||
|
||||
if (framework === 'nextjs') {
|
||||
if (framework === 'nextjs' || framework === 'blitzjs') {
|
||||
return { src: 'package.json', use: `@vercel/next${withTag}`, config };
|
||||
}
|
||||
|
||||
@@ -496,7 +496,7 @@ function getMissingBuildScriptError() {
|
||||
code: 'missing_build_script',
|
||||
message:
|
||||
'Your `package.json` file is missing a `build` property inside the `scripts` property.' +
|
||||
'\nMore details: https://vercel.com/docs/v2/platform/frequently-asked-questions#missing-build-script',
|
||||
'\nLearn More: https://vercel.com/docs/v2/platform/frequently-asked-questions#missing-build-script',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ interface Props {
|
||||
*/
|
||||
link?: string;
|
||||
/**
|
||||
* Optional "action" to display before the `link`, such as "More details".
|
||||
* Optional "action" to display before the `link`, such as "Learn More".
|
||||
*/
|
||||
action?: string;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "19.1.2-canary.17",
|
||||
"version": "19.1.2",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -62,13 +62,13 @@
|
||||
"node": ">= 10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.4.1-canary.1",
|
||||
"@vercel/go": "1.1.3-canary.1",
|
||||
"@vercel/next": "2.6.8",
|
||||
"@vercel/node": "1.7.2-canary.1",
|
||||
"@vercel/build-utils": "2.4.1",
|
||||
"@vercel/go": "1.1.3",
|
||||
"@vercel/next": "2.6.11",
|
||||
"@vercel/node": "1.7.2",
|
||||
"@vercel/python": "1.2.2",
|
||||
"@vercel/ruby": "1.2.3-canary.0",
|
||||
"@vercel/static-build": "0.17.5-canary.0"
|
||||
"@vercel/ruby": "1.2.3",
|
||||
"@vercel/static-build": "0.17.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sentry/node": "5.5.0",
|
||||
|
||||
@@ -354,7 +354,7 @@ export default async function main(
|
||||
path,
|
||||
sourcePath,
|
||||
project
|
||||
? `To change your project settings, go to https://vercel.com/${org.slug}/${project.name}/settings`
|
||||
? `To change your Project Settings, go to https://vercel.com/${org.slug}/${project.name}/settings`
|
||||
: ''
|
||||
)) === false
|
||||
) {
|
||||
|
||||
@@ -97,7 +97,7 @@ export default async function main(ctx: NowContext) {
|
||||
'package.json'
|
||||
)} must not contain ${cmd('now dev')}`
|
||||
);
|
||||
output.error(`More details: http://err.sh/now/now-dev-as-dev-script`);
|
||||
output.error(`Learn More: http://err.sh/now/now-dev-as-dev-script`);
|
||||
return 1;
|
||||
}
|
||||
if (scripts && scripts.dev && /\bvercel\b\W+\bdev\b/.test(scripts.dev)) {
|
||||
@@ -106,7 +106,7 @@ export default async function main(ctx: NowContext) {
|
||||
'package.json'
|
||||
)} must not contain ${cmd('vercel dev')}`
|
||||
);
|
||||
output.error(`More details: http://err.sh/now/now-dev-as-dev-script`);
|
||||
output.error(`Learn More: http://err.sh/now/now-dev-as-dev-script`);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ import reportError from './util/report-error';
|
||||
import getConfig from './util/get-config';
|
||||
import * as ERRORS from './util/errors-ts';
|
||||
import { NowError } from './util/now-error';
|
||||
import { APIError } from './util/errors-ts.ts';
|
||||
import { SENTRY_DSN } from './util/constants.ts';
|
||||
import getUpdateCommand from './util/get-update-command';
|
||||
import { metrics, shouldCollectMetrics } from './util/metrics.ts';
|
||||
@@ -190,8 +191,9 @@ const main = async argv_ => {
|
||||
} catch (err) {
|
||||
console.error(
|
||||
error(
|
||||
`${'An unexpected error occurred while trying to find the ' +
|
||||
'global directory: '}${err.message}`
|
||||
`An unexpected error occurred while trying to find the global directory: ${
|
||||
err.message
|
||||
}`
|
||||
)
|
||||
);
|
||||
|
||||
@@ -204,8 +206,10 @@ const main = async argv_ => {
|
||||
} catch (err) {
|
||||
console.error(
|
||||
error(
|
||||
`${'An unexpected error occurred while trying to create the ' +
|
||||
`global directory "${hp(VERCEL_DIR)}" `}${err.message}`
|
||||
`${
|
||||
'An unexpected error occurred while trying to create the ' +
|
||||
`global directory "${hp(VERCEL_DIR)}" `
|
||||
}${err.message}`
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -219,8 +223,10 @@ const main = async argv_ => {
|
||||
} catch (err) {
|
||||
console.error(
|
||||
error(
|
||||
`${'An unexpected error occurred while trying to find the ' +
|
||||
`config file "${hp(VERCEL_CONFIG_PATH)}" `}${err.message}`
|
||||
`${
|
||||
'An unexpected error occurred while trying to find the ' +
|
||||
`config file "${hp(VERCEL_CONFIG_PATH)}" `
|
||||
}${err.message}`
|
||||
)
|
||||
);
|
||||
|
||||
@@ -235,8 +241,10 @@ const main = async argv_ => {
|
||||
} catch (err) {
|
||||
console.error(
|
||||
error(
|
||||
`${'An unexpected error occurred while trying to read the ' +
|
||||
`config in "${hp(VERCEL_CONFIG_PATH)}" `}${err.message}`
|
||||
`${
|
||||
'An unexpected error occurred while trying to read the ' +
|
||||
`config in "${hp(VERCEL_CONFIG_PATH)}" `
|
||||
}${err.message}`
|
||||
)
|
||||
);
|
||||
|
||||
@@ -267,8 +275,10 @@ const main = async argv_ => {
|
||||
} catch (err) {
|
||||
console.error(
|
||||
error(
|
||||
`${'An unexpected error occurred while trying to write the ' +
|
||||
`default config to "${hp(VERCEL_CONFIG_PATH)}" `}${err.message}`
|
||||
`${
|
||||
'An unexpected error occurred while trying to write the ' +
|
||||
`default config to "${hp(VERCEL_CONFIG_PATH)}" `
|
||||
}${err.message}`
|
||||
)
|
||||
);
|
||||
|
||||
@@ -283,8 +293,10 @@ const main = async argv_ => {
|
||||
} catch (err) {
|
||||
console.error(
|
||||
error(
|
||||
`${'An unexpected error occurred while trying to find the ' +
|
||||
`auth file "${hp(VERCEL_AUTH_CONFIG_PATH)}" `}${err.message}`
|
||||
`${
|
||||
'An unexpected error occurred while trying to find the ' +
|
||||
`auth file "${hp(VERCEL_AUTH_CONFIG_PATH)}" `
|
||||
}${err.message}`
|
||||
)
|
||||
);
|
||||
|
||||
@@ -301,8 +313,10 @@ const main = async argv_ => {
|
||||
} catch (err) {
|
||||
console.error(
|
||||
error(
|
||||
`${'An unexpected error occurred while trying to read the ' +
|
||||
`auth config in "${hp(VERCEL_AUTH_CONFIG_PATH)}" `}${err.message}`
|
||||
`${
|
||||
'An unexpected error occurred while trying to read the ' +
|
||||
`auth config in "${hp(VERCEL_AUTH_CONFIG_PATH)}" `
|
||||
}${err.message}`
|
||||
)
|
||||
);
|
||||
|
||||
@@ -326,10 +340,10 @@ const main = async argv_ => {
|
||||
} catch (err) {
|
||||
console.error(
|
||||
error(
|
||||
`${'An unexpected error occurred while trying to write the ' +
|
||||
`default config to "${hp(VERCEL_AUTH_CONFIG_PATH)}" `}${
|
||||
err.message
|
||||
}`
|
||||
`${
|
||||
'An unexpected error occurred while trying to write the ' +
|
||||
`default config to "${hp(VERCEL_AUTH_CONFIG_PATH)}" `
|
||||
}${err.message}`
|
||||
)
|
||||
);
|
||||
return 1;
|
||||
@@ -639,6 +653,12 @@ const main = async argv_ => {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (err instanceof APIError && 400 <= err.status && err.status <= 499) {
|
||||
err.message = err.serverMessage;
|
||||
output.prettyError(err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (shouldCollectMetrics) {
|
||||
metric
|
||||
.event(eventCategory, '1', pkg.version)
|
||||
|
||||
@@ -29,7 +29,7 @@ export function resolveRouteParameters(
|
||||
export function getRoutesTypes(routes: Route[] = []) {
|
||||
const handleMap = new Map<HandleValue | null, Route[]>();
|
||||
let prevHandle: HandleValue | null = null;
|
||||
routes.forEach((route) => {
|
||||
routes.forEach(route => {
|
||||
if (isHandler(route)) {
|
||||
prevHandle = route.handle;
|
||||
} else {
|
||||
|
||||
@@ -49,7 +49,7 @@ import getNowConfigPath from '../config/local-path';
|
||||
import { MissingDotenvVarsError } from '../errors-ts';
|
||||
import cliPkg from '../pkg';
|
||||
import { getVercelDirectory } from '../projects/link';
|
||||
import { staticFiles as getFiles, getAllProjectFiles } from '../get-files';
|
||||
import { staticFiles as getFiles } from '../get-files';
|
||||
import { validateConfig } from './validate';
|
||||
import { devRouter, getRoutesTypes } from './router';
|
||||
import getMimeType from './mime-type';
|
||||
@@ -90,6 +90,10 @@ interface FSEvent {
|
||||
path: string;
|
||||
}
|
||||
|
||||
type WithFileNameSymbol<T> = T & {
|
||||
[fileNameSymbol]: string;
|
||||
};
|
||||
|
||||
function sortBuilders(buildA: Builder, buildB: Builder) {
|
||||
if (buildA && buildA.use && isOfficialRuntime('static-build', buildA.use)) {
|
||||
return 1;
|
||||
@@ -113,7 +117,6 @@ export default class DevServer {
|
||||
public address: string;
|
||||
public devCacheDir: string;
|
||||
|
||||
private cachedNowConfig: NowConfig | null;
|
||||
private caseSensitive: boolean;
|
||||
private apiDir: string | null;
|
||||
private apiExtensions: Set<string>;
|
||||
@@ -148,7 +151,6 @@ export default class DevServer {
|
||||
this.devCommand = options.devCommand;
|
||||
this.projectSettings = options.projectSettings;
|
||||
this.frameworkSlug = options.frameworkSlug;
|
||||
this.cachedNowConfig = null;
|
||||
this.caseSensitive = false;
|
||||
this.apiDir = null;
|
||||
this.apiExtensions = new Set();
|
||||
@@ -226,15 +228,7 @@ export default class DevServer {
|
||||
}
|
||||
}
|
||||
|
||||
const nowConfig = await this.getNowConfig(false);
|
||||
|
||||
// Update the env vars configuration
|
||||
const [runEnv, buildEnv] = await Promise.all([
|
||||
this.getLocalEnv('.env', nowConfig.env),
|
||||
this.getLocalEnv('.env.build', nowConfig.build?.env),
|
||||
]);
|
||||
const allEnv = { ...buildEnv, ...runEnv };
|
||||
this.envConfigs = { buildEnv, runEnv, allEnv };
|
||||
const nowConfig = await this.getNowConfig();
|
||||
|
||||
// Update the build matches in case an entrypoint was created or deleted
|
||||
await this.updateBuildMatches(nowConfig);
|
||||
@@ -292,7 +286,7 @@ export default class DevServer {
|
||||
filesRemovedArray
|
||||
).catch((err: Error) => {
|
||||
this.output.warn(
|
||||
`An error occurred while rebuilding ${match.src}:`
|
||||
`An error occurred while rebuilding \`${match.src}\`:`
|
||||
);
|
||||
console.error(err.stack);
|
||||
});
|
||||
@@ -503,23 +497,24 @@ export default class DevServer {
|
||||
return {};
|
||||
}
|
||||
|
||||
async getNowConfig(canUseCache: boolean = true): Promise<NowConfig> {
|
||||
clearNowConfigPromise = () => {
|
||||
this.getNowConfigPromise = null;
|
||||
};
|
||||
|
||||
getNowConfig(): Promise<NowConfig> {
|
||||
if (this.getNowConfigPromise) {
|
||||
return this.getNowConfigPromise;
|
||||
}
|
||||
this.getNowConfigPromise = this._getNowConfig(canUseCache);
|
||||
try {
|
||||
return await this.getNowConfigPromise;
|
||||
} finally {
|
||||
this.getNowConfigPromise = null;
|
||||
}
|
||||
this.getNowConfigPromise = this._getNowConfig();
|
||||
|
||||
// Clean up the promise once it has resolved
|
||||
const clear = this.clearNowConfigPromise;
|
||||
this.getNowConfigPromise.finally(clear);
|
||||
|
||||
return this.getNowConfigPromise;
|
||||
}
|
||||
|
||||
async _getNowConfig(canUseCache: boolean = true): Promise<NowConfig> {
|
||||
if (canUseCache && this.cachedNowConfig) {
|
||||
return this.cachedNowConfig;
|
||||
}
|
||||
|
||||
async _getNowConfig(): Promise<NowConfig> {
|
||||
const configPath = getNowConfigPath(this.cwd);
|
||||
|
||||
const [
|
||||
@@ -532,14 +527,6 @@ export default class DevServer {
|
||||
this.readJsonFile<NowConfig>(configPath),
|
||||
]);
|
||||
|
||||
const allFiles = await getAllProjectFiles(this.cwd, this.output);
|
||||
const files = allFiles.filter(this.filter);
|
||||
|
||||
this.output.debug(
|
||||
`Found ${allFiles.length} and ` +
|
||||
`filtered out ${allFiles.length - files.length} files`
|
||||
);
|
||||
|
||||
await this.validateNowConfig(config);
|
||||
const { error: routeError, routes: maybeRoutes } = getTransformedRoutes({
|
||||
nowConfig: config,
|
||||
@@ -555,6 +542,11 @@ export default class DevServer {
|
||||
const featHandleMiss = true; // enable for zero config
|
||||
const { projectSettings, cleanUrls, trailingSlash } = config;
|
||||
|
||||
const opts = { output: this.output, isBuilds: true };
|
||||
const files = (await getFiles(this.cwd, config, opts)).map((f) =>
|
||||
relative(this.cwd, f)
|
||||
);
|
||||
|
||||
let {
|
||||
builders,
|
||||
warnings,
|
||||
@@ -588,6 +580,8 @@ export default class DevServer {
|
||||
|
||||
config.builds = config.builds || [];
|
||||
config.builds.push(...builders);
|
||||
|
||||
delete config.functions;
|
||||
}
|
||||
|
||||
let routes: Route[] = [];
|
||||
@@ -621,14 +615,24 @@ export default class DevServer {
|
||||
|
||||
await this.validateNowConfig(config);
|
||||
|
||||
this.cachedNowConfig = config;
|
||||
this.caseSensitive = hasNewRoutingProperties(config);
|
||||
this.apiDir = detectApiDirectory(config.builds || []);
|
||||
this.apiExtensions = detectApiExtensions(config.builds || []);
|
||||
|
||||
// Update the env vars configuration
|
||||
const [runEnv, buildEnv] = await Promise.all([
|
||||
this.getLocalEnv('.env', config.env),
|
||||
this.getLocalEnv('.env.build', config.build?.env),
|
||||
]);
|
||||
const allEnv = { ...buildEnv, ...runEnv };
|
||||
this.envConfigs = { buildEnv, runEnv, allEnv };
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
async readJsonFile<T>(filePath: string): Promise<T | void> {
|
||||
async readJsonFile<T>(
|
||||
filePath: string
|
||||
): Promise<WithFileNameSymbol<T> | void> {
|
||||
let rel, abs;
|
||||
if (isAbsolute(filePath)) {
|
||||
rel = path.relative(this.cwd, filePath);
|
||||
@@ -640,7 +644,10 @@ export default class DevServer {
|
||||
this.output.debug(`Reading \`${rel}\` file`);
|
||||
|
||||
try {
|
||||
return JSON.parse(await fs.readFile(abs, 'utf8'));
|
||||
const raw = await fs.readFile(abs, 'utf8');
|
||||
const parsed: WithFileNameSymbol<T> = JSON.parse(raw);
|
||||
parsed[fileNameSymbol] = rel;
|
||||
return parsed;
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
this.output.debug(`No \`${rel}\` file present`);
|
||||
@@ -747,14 +754,7 @@ export default class DevServer {
|
||||
const { ig } = await getVercelIgnore(this.cwd);
|
||||
this.filter = ig.createFilter();
|
||||
|
||||
// Retrieve the path of the native module
|
||||
const nowConfig = await this.getNowConfig(false);
|
||||
const [runEnv, buildEnv] = await Promise.all([
|
||||
this.getLocalEnv('.env', nowConfig.env),
|
||||
this.getLocalEnv('.env.build', nowConfig.build?.env),
|
||||
]);
|
||||
const allEnv = { ...buildEnv, ...runEnv };
|
||||
this.envConfigs = { buildEnv, runEnv, allEnv };
|
||||
const nowConfig = await this.getNowConfig();
|
||||
|
||||
const opts = { output: this.output, isBuilds: true };
|
||||
const files = await getFiles(this.cwd, nowConfig, opts);
|
||||
|
||||
@@ -47,5 +47,14 @@ export function validateConfig(config: NowConfig): NowBuildError | null {
|
||||
}
|
||||
}
|
||||
|
||||
if (config.functions && config.builds) {
|
||||
return new NowBuildError({
|
||||
code: 'FUNCTIONS_AND_BUILDS',
|
||||
message:
|
||||
'The `functions` property cannot be used in conjunction with the `builds` property. Please remove one of them.',
|
||||
link: 'https://vercel.link/functions-and-builds',
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ export class APIError extends Error {
|
||||
status: number;
|
||||
serverMessage: string;
|
||||
link?: string;
|
||||
action?: string;
|
||||
retryAfter: number | null | 'never';
|
||||
[key: string]: any;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { resolve, join } from 'path';
|
||||
import { resolve } from 'path';
|
||||
import ignore from 'ignore';
|
||||
import dockerignore from '@zeit/dockerignore';
|
||||
import _glob, { IOptions } from 'glob';
|
||||
import _glob, { IOptions as GlobOptions } from 'glob';
|
||||
import fs from 'fs-extra';
|
||||
import { getVercelIgnore } from '@vercel/client';
|
||||
import IGNORED from './ignored';
|
||||
@@ -12,11 +12,11 @@ import { NowConfig } from './dev/types';
|
||||
|
||||
type NullableString = string | null;
|
||||
|
||||
const flatten = (
|
||||
function flatten(
|
||||
arr: NullableString[] | NullableString[][],
|
||||
res: NullableString[] = []
|
||||
) => {
|
||||
for (let cur of arr) {
|
||||
): NullableString[] {
|
||||
for (const cur of arr) {
|
||||
if (Array.isArray(cur)) {
|
||||
flatten(cur, res);
|
||||
} else {
|
||||
@@ -24,21 +24,17 @@ const flatten = (
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
const glob = async function(pattern: string, options: IOptions) {
|
||||
return new Promise<string[]>((resolve, reject) => {
|
||||
_glob(pattern, options, (error, files) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(files);
|
||||
}
|
||||
async function glob(pattern: string, options: GlobOptions): Promise<string[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
_glob(pattern, options, (err, files) => {
|
||||
err ? reject(err) : resolve(files);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
interface WalkSyncOptions {
|
||||
interface WalkOptions {
|
||||
output: Output;
|
||||
}
|
||||
|
||||
@@ -51,27 +47,27 @@ interface WalkSyncOptions {
|
||||
* - `output` {Object} "output" helper object
|
||||
* @returns {Array}
|
||||
*/
|
||||
const walkSync = async (
|
||||
async function walk(
|
||||
dir: string,
|
||||
path: string,
|
||||
filelist: string[] = [],
|
||||
opts: WalkSyncOptions
|
||||
) => {
|
||||
opts: WalkOptions
|
||||
) {
|
||||
const { debug } = opts.output;
|
||||
const dirc = await fs.readdir(asAbsolute(dir, path));
|
||||
for (let file of dirc) {
|
||||
file = asAbsolute(file, dir);
|
||||
try {
|
||||
const file_stat = await fs.stat(file);
|
||||
filelist = file_stat.isDirectory()
|
||||
? await walkSync(file, path, filelist, opts)
|
||||
const fileStat = await fs.stat(file);
|
||||
filelist = fileStat.isDirectory()
|
||||
? await walk(file, path, filelist, opts)
|
||||
: filelist.concat(file);
|
||||
} catch (e) {
|
||||
debug(`Ignoring invalid file ${file}`);
|
||||
}
|
||||
}
|
||||
return filelist;
|
||||
};
|
||||
}
|
||||
|
||||
interface FilesInWhitelistOptions {
|
||||
output: Output;
|
||||
@@ -85,7 +81,7 @@ interface FilesInWhitelistOptions {
|
||||
* - `output` {Object} "output" helper object
|
||||
* @returns {Array} the expanded list of whitelisted files.
|
||||
*/
|
||||
const getFilesInWhitelist = async function(
|
||||
const getFilesInWhitelist = async function (
|
||||
whitelist: string[],
|
||||
path: string,
|
||||
opts: FilesInWhitelistOptions
|
||||
@@ -97,10 +93,10 @@ const getFilesInWhitelist = async function(
|
||||
whitelist.map(async (file: string) => {
|
||||
file = asAbsolute(file, path);
|
||||
try {
|
||||
const file_stat = await fs.stat(file);
|
||||
if (file_stat.isDirectory()) {
|
||||
const dir_files = await walkSync(file, path, [], opts);
|
||||
files.push(...dir_files);
|
||||
const fileStat = await fs.stat(file);
|
||||
if (fileStat.isDirectory()) {
|
||||
const dirFiles = await walk(file, path, [], opts);
|
||||
files.push(...dirFiles);
|
||||
} else {
|
||||
files.push(file);
|
||||
}
|
||||
@@ -117,7 +113,7 @@ const getFilesInWhitelist = async function(
|
||||
* because ignore doesn't like them :|
|
||||
*/
|
||||
|
||||
const clearRelative = function(str: string) {
|
||||
const clearRelative = function (str: string) {
|
||||
return str.replace(/(\n|^)\.\//g, '$1');
|
||||
};
|
||||
|
||||
@@ -127,7 +123,7 @@ const clearRelative = function(str: string) {
|
||||
* @return {String} results or `''`
|
||||
*/
|
||||
|
||||
const maybeRead = async function<T>(path: string, default_: T) {
|
||||
const maybeRead = async function <T>(path: string, default_: T) {
|
||||
try {
|
||||
return await fs.readFile(path, 'utf8');
|
||||
} catch (err) {
|
||||
@@ -143,7 +139,7 @@ const maybeRead = async function<T>(path: string, default_: T) {
|
||||
* @param {String} parent full path
|
||||
*/
|
||||
|
||||
const asAbsolute = function(path: string, parent: string) {
|
||||
const asAbsolute = function (path: string, parent: string) {
|
||||
if (path[0] === '/') {
|
||||
return path;
|
||||
}
|
||||
@@ -272,7 +268,7 @@ export async function npm(
|
||||
const search = Array.prototype.concat.apply(
|
||||
[],
|
||||
await Promise.all(
|
||||
search_.map(file =>
|
||||
search_.map((file) =>
|
||||
glob(file, { cwd: path, absolute: true, dot: true })
|
||||
)
|
||||
)
|
||||
@@ -364,7 +360,7 @@ export async function docker(
|
||||
const search_ = ['.'];
|
||||
|
||||
// Convert all filenames into absolute paths
|
||||
const search = search_.map(file => asAbsolute(file, path));
|
||||
const search = search_.map((file) => asAbsolute(file, path));
|
||||
|
||||
// Compile list of ignored patterns and files
|
||||
const dockerIgnore = await maybeRead(resolve(path, '.dockerignore'), null);
|
||||
@@ -382,7 +378,7 @@ export async function docker(
|
||||
.createFilter();
|
||||
|
||||
const prefixLength = path.length + 1;
|
||||
const accepts = function(file: string) {
|
||||
const accepts = function (file: string) {
|
||||
const relativePath = file.substr(prefixLength);
|
||||
|
||||
if (relativePath === '') {
|
||||
@@ -415,24 +411,6 @@ export async function docker(
|
||||
return uniqueStrings(files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all files inside the project folder
|
||||
*
|
||||
* @param {String} of the current working directory
|
||||
* @param {Object} output instance
|
||||
* @return {Array} of {String}s with the found files
|
||||
*/
|
||||
export async function getAllProjectFiles(cwd: string, { debug }: Output) {
|
||||
// We need a slash at the end to remove it later on from the matched files
|
||||
const current = join(resolve(cwd), '/');
|
||||
debug(`Searching files inside of ${current}`);
|
||||
|
||||
const list = await glob('**', { cwd: current, absolute: true, nodir: true });
|
||||
|
||||
// We need to replace \ with / for windows
|
||||
return list.map(file => file.replace(current.replace(/\\/g, '/'), ''));
|
||||
}
|
||||
|
||||
interface ExplodeOptions {
|
||||
accepts: (file: string) => boolean;
|
||||
output: Output;
|
||||
@@ -482,7 +460,7 @@ async function explode(
|
||||
if (s.isDirectory()) {
|
||||
const all = await fs.readdir(file);
|
||||
/* eslint-disable no-use-before-define */
|
||||
const recursive = many(all.map(subdir => asAbsolute(subdir, file)));
|
||||
const recursive = many(all.map((subdir) => asAbsolute(subdir, file)));
|
||||
return (recursive as any) as Promise<string | null>;
|
||||
/* eslint-enable no-use-before-define */
|
||||
}
|
||||
@@ -494,7 +472,7 @@ async function explode(
|
||||
return path;
|
||||
};
|
||||
|
||||
const many = (all: string[]) => Promise.all(all.map(file => list(file)));
|
||||
const many = (all: string[]) => Promise.all(all.map((file) => list(file)));
|
||||
const arrayOfArrays = await many(paths);
|
||||
return flatten(arrayOfArrays).filter(notNull);
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ export default async function editProjectSettings(
|
||||
|
||||
output.print(
|
||||
!framework.slug
|
||||
? `No framework detected. Default project settings:\n`
|
||||
: `Auto-detected project settings (${chalk.bold(framework.name)}):\n`
|
||||
? `No framework detected. Default Project Settings:\n`
|
||||
: `Auto-detected Project Settings (${chalk.bold(framework.name)}):\n`
|
||||
);
|
||||
|
||||
settings.framework = framework.slug;
|
||||
|
||||
@@ -27,7 +27,8 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
|
||||
function warn(
|
||||
str: string,
|
||||
slug: string | null = null,
|
||||
link: string | null = null
|
||||
link: string | null = null,
|
||||
action: string = 'Learn More'
|
||||
) {
|
||||
const prevTerm = process.env.TERM;
|
||||
|
||||
@@ -42,7 +43,7 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
|
||||
boxen(
|
||||
chalk.bold.yellow('WARN! ') +
|
||||
str +
|
||||
(details ? `\nMore details: ${renderLink(details)}` : ''),
|
||||
(details ? `\n${action}: ${renderLink(details)}` : ''),
|
||||
{
|
||||
padding: {
|
||||
top: 0,
|
||||
@@ -67,7 +68,7 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
|
||||
str: string,
|
||||
slug?: string,
|
||||
link?: string,
|
||||
action = 'More details'
|
||||
action = 'Learn More'
|
||||
) {
|
||||
print(`${chalk.red(`Error!`)} ${str}\n`);
|
||||
const details = slug ? `https://err.sh/now/${slug}` : link;
|
||||
|
||||
@@ -8,11 +8,11 @@ const metric = metrics();
|
||||
export default function error(...input: string[] | [APIError]) {
|
||||
let messages = input;
|
||||
if (typeof input[0] === 'object') {
|
||||
const { slug, message, link } = input[0];
|
||||
const { slug, message, link, action = 'Learn More' } = input[0];
|
||||
messages = [message];
|
||||
const details = slug ? `https://err.sh/now/${slug}` : link;
|
||||
if (details) {
|
||||
messages.push(`${chalk.bold('More details')}: ${renderLink(details)}`);
|
||||
messages.push(`${chalk.bold(action)}: ${renderLink(details)}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ async function getLinkFromDir(dir: string): Promise<ProjectLink | null> {
|
||||
|
||||
if (!ajv.validate(linkSchema, link)) {
|
||||
throw new Error(
|
||||
`Project settings are invalid. To link your project again, remove the ${dir} directory.`
|
||||
`Project Settings are invalid. To link your project again, remove the ${dir} directory.`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ async function getLinkFromDir(dir: string): Promise<ProjectLink | null> {
|
||||
// link file can't be read
|
||||
if (error.name === 'SyntaxError') {
|
||||
throw new Error(
|
||||
`Project settings could not be retrieved. To link your project again, remove the ${dir} directory.`
|
||||
`Project Settings could not be retrieved. To link your project again, remove the ${dir} directory.`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ export default async function responseError(
|
||||
fallbackMessage = null,
|
||||
parsedBody = {}
|
||||
) {
|
||||
let message;
|
||||
let bodyError;
|
||||
|
||||
if (res.status >= 400 && res.status < 500) {
|
||||
@@ -20,12 +19,9 @@ export default async function responseError(
|
||||
|
||||
// Some APIs wrongly return `err` instead of `error`
|
||||
bodyError = body.error || body.err || body;
|
||||
message = bodyError.message;
|
||||
}
|
||||
|
||||
if (message == null) {
|
||||
message = fallbackMessage === null ? 'Response Error' : fallbackMessage;
|
||||
}
|
||||
const msg = bodyError?.message || fallbackMessage || 'Response Error';
|
||||
|
||||
return new APIError(message, res, bodyError);
|
||||
return new APIError(msg, res, bodyError);
|
||||
}
|
||||
|
||||
57
packages/now-cli/test/dev-validate.unit.js
vendored
57
packages/now-cli/test/dev-validate.unit.js
vendored
@@ -1,18 +1,17 @@
|
||||
import test from 'ava';
|
||||
import { validateConfig } from '../src/util/dev/validate';
|
||||
|
||||
test('[dev-validate] should not error with empty config', async t => {
|
||||
test('[dev-validate] should not error with empty config', async (t) => {
|
||||
const config = {};
|
||||
const error = validateConfig(config);
|
||||
t.deepEqual(error, null);
|
||||
});
|
||||
|
||||
test('[dev-validate] should not error with complete config', async t => {
|
||||
test('[dev-validate] should not error with complete config', async (t) => {
|
||||
const config = {
|
||||
version: 2,
|
||||
public: true,
|
||||
regions: ['sfo1', 'iad1'],
|
||||
builds: [{ src: 'package.json', use: '@vercel/next' }],
|
||||
cleanUrls: true,
|
||||
headers: [{ source: '/', headers: [{ key: 'x-id', value: '123' }] }],
|
||||
rewrites: [{ source: '/help', destination: '/support' }],
|
||||
@@ -24,7 +23,7 @@ test('[dev-validate] should not error with complete config', async t => {
|
||||
t.deepEqual(error, null);
|
||||
});
|
||||
|
||||
test('[dev-validate] should not error with builds and routes', async t => {
|
||||
test('[dev-validate] should not error with builds and routes', async (t) => {
|
||||
const config = {
|
||||
builds: [{ src: 'api/index.js', use: '@vercel/node' }],
|
||||
routes: [{ src: '/(.*)', dest: '/api/index.js' }],
|
||||
@@ -33,7 +32,7 @@ test('[dev-validate] should not error with builds and routes', async t => {
|
||||
t.deepEqual(error, null);
|
||||
});
|
||||
|
||||
test('[dev-validate] should error with invalid rewrites due to additional property and offer suggestion', async t => {
|
||||
test('[dev-validate] should error with invalid rewrites due to additional property and offer suggestion', async (t) => {
|
||||
const config = {
|
||||
rewrites: [{ src: '/(.*)', dest: '/api/index.js' }],
|
||||
};
|
||||
@@ -48,7 +47,7 @@ test('[dev-validate] should error with invalid rewrites due to additional proper
|
||||
);
|
||||
});
|
||||
|
||||
test('[dev-validate] should error with invalid routes due to additional property and offer suggestion', async t => {
|
||||
test('[dev-validate] should error with invalid routes due to additional property and offer suggestion', async (t) => {
|
||||
const config = {
|
||||
routes: [{ source: '/(.*)', destination: '/api/index.js' }],
|
||||
};
|
||||
@@ -63,7 +62,7 @@ test('[dev-validate] should error with invalid routes due to additional property
|
||||
);
|
||||
});
|
||||
|
||||
test('[dev-validate] should error with invalid routes array type', async t => {
|
||||
test('[dev-validate] should error with invalid routes array type', async (t) => {
|
||||
const config = {
|
||||
routes: { src: '/(.*)', dest: '/api/index.js' },
|
||||
};
|
||||
@@ -75,7 +74,7 @@ test('[dev-validate] should error with invalid routes array type', async t => {
|
||||
);
|
||||
});
|
||||
|
||||
test('[dev-validate] should error with invalid redirects array object', async t => {
|
||||
test('[dev-validate] should error with invalid redirects array object', async (t) => {
|
||||
const config = {
|
||||
redirects: [
|
||||
{
|
||||
@@ -94,7 +93,7 @@ test('[dev-validate] should error with invalid redirects array object', async t
|
||||
);
|
||||
});
|
||||
|
||||
test('[dev-validate] should error with invalid redirects.permanent poperty', async t => {
|
||||
test('[dev-validate] should error with invalid redirects.permanent poperty', async (t) => {
|
||||
const config = {
|
||||
redirects: [{ source: '/', destination: '/go', permanent: 'yes' }],
|
||||
};
|
||||
@@ -109,7 +108,7 @@ test('[dev-validate] should error with invalid redirects.permanent poperty', asy
|
||||
);
|
||||
});
|
||||
|
||||
test('[dev-validate] should error with invalid cleanUrls type', async t => {
|
||||
test('[dev-validate] should error with invalid cleanUrls type', async (t) => {
|
||||
const config = {
|
||||
cleanUrls: 'true',
|
||||
};
|
||||
@@ -124,7 +123,7 @@ test('[dev-validate] should error with invalid cleanUrls type', async t => {
|
||||
);
|
||||
});
|
||||
|
||||
test('[dev-validate] should error with invalid trailingSlash type', async t => {
|
||||
test('[dev-validate] should error with invalid trailingSlash type', async (t) => {
|
||||
const config = {
|
||||
trailingSlash: [true],
|
||||
};
|
||||
@@ -139,7 +138,7 @@ test('[dev-validate] should error with invalid trailingSlash type', async t => {
|
||||
);
|
||||
});
|
||||
|
||||
test('[dev-validate] should error with invalid headers property', async t => {
|
||||
test('[dev-validate] should error with invalid headers property', async (t) => {
|
||||
const config = {
|
||||
headers: [{ 'Content-Type': 'text/html' }],
|
||||
};
|
||||
@@ -154,7 +153,7 @@ test('[dev-validate] should error with invalid headers property', async t => {
|
||||
);
|
||||
});
|
||||
|
||||
test('[dev-validate] should error with invalid headers.source type', async t => {
|
||||
test('[dev-validate] should error with invalid headers.source type', async (t) => {
|
||||
const config = {
|
||||
headers: [{ source: [{ 'Content-Type': 'text/html' }] }],
|
||||
};
|
||||
@@ -169,7 +168,7 @@ test('[dev-validate] should error with invalid headers.source type', async t =>
|
||||
);
|
||||
});
|
||||
|
||||
test('[dev-validate] should error with invalid headers additional property', async t => {
|
||||
test('[dev-validate] should error with invalid headers additional property', async (t) => {
|
||||
const config = {
|
||||
headers: [{ source: '/', stuff: [{ 'Content-Type': 'text/html' }] }],
|
||||
};
|
||||
@@ -184,7 +183,7 @@ test('[dev-validate] should error with invalid headers additional property', asy
|
||||
);
|
||||
});
|
||||
|
||||
test('[dev-validate] should error with invalid headers wrong nested headers type', async t => {
|
||||
test('[dev-validate] should error with invalid headers wrong nested headers type', async (t) => {
|
||||
const config = {
|
||||
headers: [{ source: '/', headers: [{ 'Content-Type': 'text/html' }] }],
|
||||
};
|
||||
@@ -199,7 +198,7 @@ test('[dev-validate] should error with invalid headers wrong nested headers type
|
||||
);
|
||||
});
|
||||
|
||||
test('[dev-validate] should error with invalid headers wrong nested headers additional property', async t => {
|
||||
test('[dev-validate] should error with invalid headers wrong nested headers additional property', async (t) => {
|
||||
const config = {
|
||||
headers: [
|
||||
{ source: '/', headers: [{ key: 'Content-Type', val: 'text/html' }] },
|
||||
@@ -216,7 +215,7 @@ test('[dev-validate] should error with invalid headers wrong nested headers addi
|
||||
);
|
||||
});
|
||||
|
||||
test('[dev-validate] should error with too many redirects', async t => {
|
||||
test('[dev-validate] should error with too many redirects', async (t) => {
|
||||
const config = {
|
||||
redirects: Array.from({ length: 5000 }).map((_, i) => ({
|
||||
source: `/${i}`,
|
||||
@@ -234,7 +233,7 @@ test('[dev-validate] should error with too many redirects', async t => {
|
||||
);
|
||||
});
|
||||
|
||||
test('[dev-validate] should error with too many nested headers', async t => {
|
||||
test('[dev-validate] should error with too many nested headers', async (t) => {
|
||||
const config = {
|
||||
headers: [
|
||||
{
|
||||
@@ -260,3 +259,25 @@ test('[dev-validate] should error with too many nested headers', async t => {
|
||||
'https://vercel.com/docs/configuration#project/headers'
|
||||
);
|
||||
});
|
||||
|
||||
test('[dev-validate] should error with "functions" and "builds"', async (t) => {
|
||||
const config = {
|
||||
builds: [
|
||||
{
|
||||
src: 'index.html',
|
||||
use: '@vercel/static',
|
||||
},
|
||||
],
|
||||
functions: {
|
||||
'api/test.js': {
|
||||
memory: 1024,
|
||||
},
|
||||
},
|
||||
};
|
||||
const error = validateConfig(config);
|
||||
t.deepEqual(
|
||||
error.message,
|
||||
'The `functions` property cannot be used in conjunction with the `builds` property. Please remove one of them.'
|
||||
);
|
||||
t.deepEqual(error.link, 'https://vercel.link/functions-and-builds');
|
||||
});
|
||||
|
||||
@@ -1 +1 @@
|
||||
{ "env": { "FOO": "bar" } }
|
||||
{"env":{"FOO":"bar"}}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{ "builds": [{ "src": "index.js", "use": "@vercel/node@canary" }] }
|
||||
{"builds":[{"src":"index.js","use":"@vercel/node@canary"}]}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
!public
|
||||
.vercel
|
||||
|
||||
@@ -411,7 +411,6 @@ test('[vercel dev] `vercel.json` should be invalidated if deleted', async t => {
|
||||
{
|
||||
// Env var should not be set after `vercel.json` is deleted
|
||||
await fs.remove(configPath);
|
||||
await sleep(1000);
|
||||
|
||||
const res = await fetch(`http://localhost:${port}/api`);
|
||||
const body = await res.json();
|
||||
@@ -454,7 +453,6 @@ test('[vercel dev] reflects changes to config and env without restart', async t
|
||||
],
|
||||
};
|
||||
await fs.writeJSON(configPath, config);
|
||||
await sleep(1000);
|
||||
|
||||
const res = await fetch(`http://localhost:${port}/?foo=bar`);
|
||||
const body = await res.json();
|
||||
@@ -476,7 +474,6 @@ test('[vercel dev] reflects changes to config and env without restart', async t
|
||||
],
|
||||
};
|
||||
await fs.writeJSON(configPath, config);
|
||||
await sleep(1000);
|
||||
|
||||
const res = await fetch(`http://localhost:${port}/?foo=baz`);
|
||||
const body = await res.json();
|
||||
@@ -495,7 +492,6 @@ test('[vercel dev] reflects changes to config and env without restart', async t
|
||||
},
|
||||
};
|
||||
await fs.writeJSON(configPath, config);
|
||||
await sleep(1000);
|
||||
|
||||
const res = await fetch(`http://localhost:${port}/?foo=baz`);
|
||||
const body = await res.json();
|
||||
@@ -514,7 +510,6 @@ test('[vercel dev] reflects changes to config and env without restart', async t
|
||||
},
|
||||
};
|
||||
await fs.writeJSON(configPath, config);
|
||||
await sleep(1000);
|
||||
|
||||
const res = await fetch(`http://localhost:${port}/?foo=boo`);
|
||||
const body = await res.json();
|
||||
|
||||
22
packages/now-cli/test/integration.js
vendored
22
packages/now-cli/test/integration.js
vendored
@@ -219,9 +219,7 @@ const createUser = async () => {
|
||||
|
||||
email = user.email;
|
||||
contextName = user.username;
|
||||
session = Math.random()
|
||||
.toString(36)
|
||||
.split('.')[1];
|
||||
session = Math.random().toString(36).split('.')[1];
|
||||
},
|
||||
{ retries: 3, factor: 1 }
|
||||
);
|
||||
@@ -2365,7 +2363,7 @@ test('fail to deploy a Lambda with an incorrect value for of memory', async t =>
|
||||
|
||||
t.is(output.exitCode, 1, formatOutput(output));
|
||||
t.regex(output.stderr, /steps of 64/gm, formatOutput(output));
|
||||
t.regex(output.stderr, /More details/gm, formatOutput(output));
|
||||
t.regex(output.stderr, /Learn More/gm, formatOutput(output));
|
||||
});
|
||||
|
||||
test('deploy a Lambda with 3 seconds of maxDuration', async t => {
|
||||
@@ -2470,9 +2468,7 @@ test('change user', async t => {
|
||||
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]
|
||||
Math.random().toString(36).split('.')[1]
|
||||
}`;
|
||||
|
||||
// remove previously linked project if it exists
|
||||
@@ -2598,9 +2594,7 @@ test('should show prompts to set up project', async t => {
|
||||
|
||||
test('should prefill "project name" prompt with folder name', async t => {
|
||||
const projectName = `static-deployment-${
|
||||
Math.random()
|
||||
.toString(36)
|
||||
.split('.')[1]
|
||||
Math.random().toString(36).split('.')[1]
|
||||
}`;
|
||||
|
||||
const src = fixture('static-deployment');
|
||||
@@ -2648,9 +2642,7 @@ test('should prefill "project name" prompt with folder name', async t => {
|
||||
test('should prefill "project name" prompt with --name', async t => {
|
||||
const directory = fixture('static-deployment');
|
||||
const projectName = `static-deployment-${
|
||||
Math.random()
|
||||
.toString(36)
|
||||
.split('.')[1]
|
||||
Math.random().toString(36).split('.')[1]
|
||||
}`;
|
||||
|
||||
// remove previously linked project if it exists
|
||||
@@ -2708,9 +2700,7 @@ test('should prefill "project name" prompt with --name', async t => {
|
||||
test('should prefill "project name" prompt with now.json `name`', async t => {
|
||||
const directory = fixture('static-deployment');
|
||||
const projectName = `static-deployment-${
|
||||
Math.random()
|
||||
.toString(36)
|
||||
.split('.')[1]
|
||||
Math.random().toString(36).split('.')[1]
|
||||
}`;
|
||||
|
||||
// remove previously linked project if it exists
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "8.1.1-canary.1",
|
||||
"version": "8.2.0",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -38,6 +38,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.4.1",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "3.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/go",
|
||||
"version": "1.1.3-canary.1",
|
||||
"version": "1.1.3",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "2.6.8",
|
||||
"version": "2.6.11",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
|
||||
@@ -36,6 +36,7 @@
|
||||
"get-port": "5.0.0",
|
||||
"resolve-from": "5.0.0",
|
||||
"semver": "6.1.1",
|
||||
"set-cookie-parser": "2.4.6",
|
||||
"typescript": "3.9.3",
|
||||
"yazl": "https://github.com/ijjk/yazl#70949c55b482647669ce37023017b1514c42b33c"
|
||||
}
|
||||
|
||||
@@ -15,18 +15,18 @@ const {
|
||||
} = buildUtils;
|
||||
|
||||
import {
|
||||
Lambda,
|
||||
BuildOptions,
|
||||
Config,
|
||||
FileBlob,
|
||||
FileFsRef,
|
||||
Files,
|
||||
Lambda,
|
||||
NowBuildError,
|
||||
PackageJson,
|
||||
PrepareCacheOptions,
|
||||
Prerender,
|
||||
NowBuildError,
|
||||
} from '@vercel/build-utils';
|
||||
import { Route, Handler } from '@vercel/routing-utils';
|
||||
import { Handler, Route } from '@vercel/routing-utils';
|
||||
import {
|
||||
convertHeaders,
|
||||
convertRedirects,
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
} from '@vercel/routing-utils/dist/superstatic';
|
||||
import nodeFileTrace, { NodeFileTraceReasons } from '@zeit/node-file-trace';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
import escapeStringRegexp from 'escape-string-regexp';
|
||||
import {
|
||||
lstat,
|
||||
pathExists,
|
||||
@@ -46,7 +47,6 @@ import path from 'path';
|
||||
import resolveFrom from 'resolve-from';
|
||||
import semver from 'semver';
|
||||
import createServerlessConfig from './create-serverless-config';
|
||||
import escapeStringRegexp from 'escape-string-regexp';
|
||||
import nextLegacyVersions from './legacy-versions';
|
||||
import {
|
||||
createLambdaFromPseudoLayers,
|
||||
@@ -108,7 +108,13 @@ const MAX_AGE_ONE_YEAR = 31536000;
|
||||
/**
|
||||
* Read package.json from files
|
||||
*/
|
||||
async function readPackageJson(entryPath: string) {
|
||||
async function readPackageJson(
|
||||
entryPath: string
|
||||
): Promise<{
|
||||
scripts?: { [key: string]: string };
|
||||
dependencies?: { [key: string]: string };
|
||||
devDependencies?: { [key: string]: string };
|
||||
}> {
|
||||
const packagePath = path.join(entryPath, 'package.json');
|
||||
|
||||
try {
|
||||
@@ -139,15 +145,38 @@ async function writeNpmRc(workPath: string, token: string) {
|
||||
);
|
||||
}
|
||||
|
||||
function getNextVersion(packageJson: {
|
||||
dependencies?: { [key: string]: string };
|
||||
devDependencies?: { [key: string]: string };
|
||||
}) {
|
||||
let nextVersion;
|
||||
if (packageJson.dependencies && packageJson.dependencies.next) {
|
||||
nextVersion = packageJson.dependencies.next;
|
||||
} else if (packageJson.devDependencies && packageJson.devDependencies.next) {
|
||||
nextVersion = packageJson.devDependencies.next;
|
||||
/**
|
||||
* Get the installed Next version.
|
||||
*/
|
||||
function getRealNextVersion(entryPath: string): string | false {
|
||||
try {
|
||||
// First try to resolve the `next` dependency and get the real version from its
|
||||
// package.json. This allows the builder to be used with frameworks like Blitz that
|
||||
// bundle Next but where Next isn't in the project root's package.json
|
||||
const nextVersion: string = require(resolveFrom(
|
||||
entryPath,
|
||||
'next/package.json'
|
||||
)).version;
|
||||
debug(`Detected Next.js version: ${nextVersion}`);
|
||||
return nextVersion;
|
||||
} catch (_ignored) {
|
||||
debug(
|
||||
`Could not identify real Next.js version, ensure it is defined as a project dependency.`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the package.json Next version.
|
||||
*/
|
||||
async function getNextVersionRange(entryPath: string): Promise<string | false> {
|
||||
let nextVersion: string | false = false;
|
||||
const pkg = await readPackageJson(entryPath);
|
||||
if (pkg.dependencies && pkg.dependencies.next) {
|
||||
nextVersion = pkg.dependencies.next;
|
||||
} else if (pkg.devDependencies && pkg.devDependencies.next) {
|
||||
nextVersion = pkg.devDependencies.next;
|
||||
}
|
||||
return nextVersion;
|
||||
}
|
||||
@@ -218,19 +247,10 @@ export const build = async ({
|
||||
await download(files, workPath, meta);
|
||||
|
||||
const pkg = await readPackageJson(entryPath);
|
||||
const nextVersion = getNextVersion(pkg);
|
||||
|
||||
const nextVersionRange = await getNextVersionRange(entryPath);
|
||||
const nodeVersion = await getNodeVersion(entryPath, undefined, config, meta);
|
||||
const spawnOpts = getSpawnOptions(meta, nodeVersion);
|
||||
|
||||
if (!nextVersion) {
|
||||
throw new NowBuildError({
|
||||
code: 'NEXT_NO_VERSION',
|
||||
message:
|
||||
'No Next.js version could be detected in "package.json". Make sure `"next"` is installed in "dependencies" or "devDependencies"',
|
||||
});
|
||||
}
|
||||
|
||||
const nowJsonPath = await findUp(['now.json', 'vercel.json'], {
|
||||
cwd: path.join(workPath, path.dirname(entrypoint)),
|
||||
});
|
||||
@@ -308,7 +328,7 @@ export const build = async ({
|
||||
console.warn('WARNING: You should not upload the `.next` directory.');
|
||||
}
|
||||
|
||||
const isLegacy = isLegacyNext(nextVersion);
|
||||
const isLegacy = nextVersionRange && isLegacyNext(nextVersionRange);
|
||||
let shouldRunScript = 'now-build';
|
||||
|
||||
debug(`MODE: ${isLegacy ? 'legacy' : 'serverless'}`);
|
||||
@@ -362,18 +382,20 @@ export const build = async ({
|
||||
console.log('Installing dependencies...');
|
||||
await runNpmInstall(entryPath, ['--prefer-offline'], spawnOpts, meta);
|
||||
|
||||
let realNextVersion: string | undefined;
|
||||
try {
|
||||
realNextVersion = require(resolveFrom(entryPath, 'next/package.json'))
|
||||
.version;
|
||||
|
||||
debug(`Detected Next.js version: ${realNextVersion}`);
|
||||
} catch (_ignored) {
|
||||
debug(`Could not identify real Next.js version, that's OK!`);
|
||||
// Refetch Next version now that dependencies are installed.
|
||||
// This will now resolve the actual installed Next version,
|
||||
// even if Next isn't in the project package.json
|
||||
const nextVersion = getRealNextVersion(entryPath);
|
||||
if (!nextVersion) {
|
||||
throw new NowBuildError({
|
||||
code: 'NEXT_NO_VERSION',
|
||||
message:
|
||||
'No Next.js version could be detected in your project. Make sure `"next"` is installed in "dependencies" or "devDependencies"',
|
||||
});
|
||||
}
|
||||
|
||||
if (!isLegacy) {
|
||||
await createServerlessConfig(workPath, entryPath, realNextVersion);
|
||||
await createServerlessConfig(workPath, entryPath, nextVersion);
|
||||
}
|
||||
|
||||
debug('Running user script...');
|
||||
@@ -410,7 +432,7 @@ export const build = async ({
|
||||
const routesManifest = await getRoutesManifest(
|
||||
entryPath,
|
||||
outputDirectory,
|
||||
realNextVersion
|
||||
nextVersion
|
||||
);
|
||||
const prerenderManifest = await getPrerenderManifest(entryPath);
|
||||
const headers: Route[] = [];
|
||||
@@ -839,10 +861,7 @@ export const build = async ({
|
||||
// Assume tracing to be safe, bail if we know we don't need it.
|
||||
let requiresTracing = hasLambdas;
|
||||
try {
|
||||
if (
|
||||
realNextVersion &&
|
||||
semver.lt(realNextVersion, ExperimentalTraceVersion)
|
||||
) {
|
||||
if (nextVersion && semver.lt(nextVersion, ExperimentalTraceVersion)) {
|
||||
debug(
|
||||
'Next.js version is too old for us to trace the required dependencies.\n' +
|
||||
'Assuming Next.js has handled it!'
|
||||
@@ -1209,6 +1228,21 @@ export const build = async ({
|
||||
const launcherPath = path.join(__dirname, 'templated-launcher-shared.js');
|
||||
const launcherData = await readFile(launcherPath, 'utf8');
|
||||
|
||||
// we need to include the prerenderManifest.omittedRoutes here
|
||||
// for the page to be able to be matched in the lambda for preview mode
|
||||
const completeDynamicRoutes = await getDynamicRoutes(
|
||||
entryPath,
|
||||
entryDirectory,
|
||||
dynamicPages,
|
||||
false,
|
||||
routesManifest
|
||||
).then((arr) =>
|
||||
arr.map((route) => {
|
||||
route.src = route.src.replace('^', `^${dynamicPrefix}`);
|
||||
return route;
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
[...apiLambdaGroups, ...pageLambdaGroups].map(
|
||||
async function buildLambdaGroup(group: LambdaGroup) {
|
||||
@@ -1268,7 +1302,7 @@ export const build = async ({
|
||||
// for prerendered dynamic routes (/blog/post-1) we need to
|
||||
// find the match since it won't match the page directly
|
||||
const dynamicRoutes = ${JSON.stringify(
|
||||
dynamicRoutes.map((route) => ({
|
||||
completeDynamicRoutes.map((route) => ({
|
||||
src: route.src,
|
||||
dest: route.dest,
|
||||
}))
|
||||
@@ -1760,14 +1794,8 @@ export const prepareCache = async ({
|
||||
const entryPath = path.join(workPath, entryDirectory);
|
||||
const outputDirectory = config.outputDirectory || '.next';
|
||||
|
||||
const pkg = await readPackageJson(entryPath);
|
||||
const nextVersion = getNextVersion(pkg);
|
||||
if (!nextVersion)
|
||||
throw new NowBuildError({
|
||||
code: 'NEXT_VERSION_PARSE_FAILED',
|
||||
message: 'Could not parse Next.js version',
|
||||
});
|
||||
const isLegacy = isLegacyNext(nextVersion);
|
||||
const nextVersionRange = await getNextVersionRange(entryPath);
|
||||
const isLegacy = nextVersionRange && isLegacyNext(nextVersionRange);
|
||||
|
||||
if (isLegacy) {
|
||||
// skip caching legacy mode (swapping deps between all and production can get bug-prone)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -363,17 +363,18 @@ export async function getRoutesManifest(
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const routesManifest: RoutesManifest = require(pathRoutesManifest);
|
||||
|
||||
// massage temporary array based routeKeys from v1/v2 of routes
|
||||
// manifest into new object based
|
||||
for (const route of [
|
||||
...(routesManifest.dataRoutes || []),
|
||||
...(routesManifest.dynamicRoutes || []),
|
||||
]) {
|
||||
// remove temporary array based routeKeys from v1/v2 of routes
|
||||
// manifest since it can result in invalid routes
|
||||
for (const route of routesManifest.dataRoutes || []) {
|
||||
if (Array.isArray(route.routeKeys)) {
|
||||
route.routeKeys = route.routeKeys.reduce((prev, cur) => {
|
||||
prev[cur] = cur;
|
||||
return prev;
|
||||
}, {});
|
||||
delete route.routeKeys;
|
||||
delete route.namedDataRouteRegex;
|
||||
}
|
||||
}
|
||||
for (const route of routesManifest.dynamicRoutes || []) {
|
||||
if (Array.isArray(route.routeKeys)) {
|
||||
delete route.routeKeys;
|
||||
delete route.namedRegex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,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('&')}`
|
||||
: ''
|
||||
}`,
|
||||
@@ -478,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)
|
||||
@@ -861,7 +862,7 @@ export async function getPrerenderManifest(
|
||||
omittedRoutes: [],
|
||||
};
|
||||
|
||||
routes.forEach(route => {
|
||||
routes.forEach((route) => {
|
||||
const {
|
||||
initialRevalidateSeconds,
|
||||
dataRoute,
|
||||
@@ -877,7 +878,7 @@ export async function getPrerenderManifest(
|
||||
};
|
||||
});
|
||||
|
||||
lazyRoutes.forEach(lazyRoute => {
|
||||
lazyRoutes.forEach((lazyRoute) => {
|
||||
const {
|
||||
routeRegex,
|
||||
fallback,
|
||||
@@ -915,7 +916,7 @@ export async function getPrerenderManifest(
|
||||
omittedRoutes: [],
|
||||
};
|
||||
|
||||
routes.forEach(route => {
|
||||
routes.forEach((route) => {
|
||||
const {
|
||||
initialRevalidateSeconds,
|
||||
dataRoute,
|
||||
@@ -931,7 +932,7 @@ export async function getPrerenderManifest(
|
||||
};
|
||||
});
|
||||
|
||||
lazyRoutes.forEach(lazyRoute => {
|
||||
lazyRoutes.forEach((lazyRoute) => {
|
||||
const {
|
||||
routeRegex,
|
||||
fallback,
|
||||
|
||||
13
packages/now-next/test/fixtures/23-custom-routes-verbose/pages/b/[123].js
vendored
Normal file
13
packages/now-next/test/fixtures/23-custom-routes-verbose/pages/b/[123].js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
export const getServerSideProps = ({ params }) => {
|
||||
console.log({ params });
|
||||
|
||||
return {
|
||||
props: {
|
||||
params,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default function Page(props) {
|
||||
return <p id="props">{JSON.stringify(props)}</p>;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export const getServerSideProps = ({ params }) => {
|
||||
console.log({ params });
|
||||
|
||||
return {
|
||||
props: {
|
||||
params,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default function Page(props) {
|
||||
return <p id="props">{JSON.stringify(props)}</p>;
|
||||
}
|
||||
5
packages/now-next/test/fixtures/27-non-word-param/next.config.js
vendored
Normal file
5
packages/now-next/test/fixtures/27-non-word-param/next.config.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
};
|
||||
50
packages/now-next/test/fixtures/27-non-word-param/now.json
vendored
Normal file
50
packages/now-next/test/fixtures/27-non-word-param/now.json
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "package.json", "use": "@vercel/next" }],
|
||||
"probes": [
|
||||
// make sure index responds correctly
|
||||
{
|
||||
"path": "/",
|
||||
"mustContain": "Create Next App"
|
||||
},
|
||||
|
||||
// make sure lazy catch-all SSG page matches
|
||||
{
|
||||
"path": "/another",
|
||||
"mustContain": "Loading..."
|
||||
},
|
||||
|
||||
{ "delay": 2000 },
|
||||
|
||||
// make sure lazy catch-all SSG page was cached
|
||||
{
|
||||
"path": "/another",
|
||||
"mustContain": "My awesome article"
|
||||
},
|
||||
|
||||
// make sure correct param is used
|
||||
{
|
||||
"path": "/another",
|
||||
"mustContain": "another"
|
||||
},
|
||||
|
||||
// make sure lazy catch-all SSG data route matches
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/something.json",
|
||||
"mustContain": "My awesome article"
|
||||
},
|
||||
|
||||
// make sure lazy catch-all SSG data route has correct param
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/something.json",
|
||||
"mustContain": "something"
|
||||
},
|
||||
|
||||
// make sure lazy catch-all SSG data route doesn't have HTML
|
||||
// make sure lazy catch-all SSG data route matches
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/one-more.json",
|
||||
"mustNotContain": "<html>"
|
||||
}
|
||||
]
|
||||
}
|
||||
7
packages/now-next/test/fixtures/27-non-word-param/package.json
vendored
Normal file
7
packages/now-next/test/fixtures/27-non-word-param/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "9.4.4",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
65
packages/now-next/test/fixtures/27-non-word-param/pages/[...path-segments].js
vendored
Normal file
65
packages/now-next/test/fixtures/27-non-word-param/pages/[...path-segments].js
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
import { useRouter } from 'next/router';
|
||||
import Error from 'next/error';
|
||||
|
||||
function loadArticle() {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'header',
|
||||
content: 'My awesome article',
|
||||
},
|
||||
{
|
||||
type: 'paragraph',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend.',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const Page = ({ path, article }) => {
|
||||
const router = useRouter();
|
||||
|
||||
if (router.isFallback) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
if (!article.content) {
|
||||
return <Error statusCode={404} />;
|
||||
}
|
||||
|
||||
const [header, ...body] = article.content;
|
||||
|
||||
return (
|
||||
<article>
|
||||
<header>{header.content}</header>
|
||||
<small>path: {path.join('/')}</small>
|
||||
<main>
|
||||
{body.map(({ content }) => (
|
||||
<p>{content}</p>
|
||||
))}
|
||||
</main>
|
||||
</article>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
export async function getStaticProps({ params }) {
|
||||
const path = params['path-segments'];
|
||||
const article = loadArticle(path);
|
||||
|
||||
return {
|
||||
props: {
|
||||
article,
|
||||
path,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return {
|
||||
paths: [],
|
||||
fallback: true,
|
||||
};
|
||||
}
|
||||
201
packages/now-next/test/fixtures/27-non-word-param/pages/index.js
vendored
Normal file
201
packages/now-next/test/fixtures/27-non-word-param/pages/index.js
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
import Head from 'next/head';
|
||||
|
||||
const Home = () => (
|
||||
<div className="container">
|
||||
<Head>
|
||||
<title>Create Next App</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<main>
|
||||
<h1 className="title">
|
||||
Welcome to <a href="https://nextjs.org">Next.js!</a>
|
||||
</h1>
|
||||
|
||||
<p className="description">
|
||||
Get started by editing <code>pages/index.js</code>
|
||||
</p>
|
||||
|
||||
<div className="grid">
|
||||
<a href="https://nextjs.org/docs" className="card">
|
||||
<h3>Documentation →</h3>
|
||||
<p>Find in-depth information about Next.js features and API.</p>
|
||||
</a>
|
||||
|
||||
<a href="https://nextjs.org/learn" className="card">
|
||||
<h3>Learn →</h3>
|
||||
<p>Learn about Next.js in an interactive course with quizzes!</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://github.com/zeit/next.js/tree/master/examples"
|
||||
className="card"
|
||||
>
|
||||
<h3>Examples →</h3>
|
||||
<p>Discover and deploy boilerplate example Next.js projects.</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/import?filter=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
className="card"
|
||||
>
|
||||
<h3>Deploy →</h3>
|
||||
<p>Instantly deploy your Next.js site to a public URL with Vercel.</p>
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<a
|
||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Powered by Vercel
|
||||
</a>
|
||||
</footer>
|
||||
|
||||
<style jsx>{`
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
padding: 0 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 5rem 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
footer {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
border-top: 1px solid #eaeaea;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
footer img {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
footer a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.title a {
|
||||
color: #0070f3;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.title a:hover,
|
||||
.title a:focus,
|
||||
.title a:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
line-height: 1.15;
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
.title,
|
||||
.description {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.description {
|
||||
line-height: 1.5;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #fafafa;
|
||||
border-radius: 5px;
|
||||
padding: 0.75rem;
|
||||
font-size: 1.1rem;
|
||||
font-family: Menlo, Monaco, Lucida Console, Liberation Mono,
|
||||
DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
max-width: 800px;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin: 1rem;
|
||||
flex-basis: 45%;
|
||||
padding: 1.5rem;
|
||||
text-align: left;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
border: 1px solid #eaeaea;
|
||||
border-radius: 10px;
|
||||
transition: color 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.card:hover,
|
||||
.card:focus,
|
||||
.card:active {
|
||||
color: #0070f3;
|
||||
border-color: #0070f3;
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
margin: 0 0 1rem 0;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.card p {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.grid {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<style jsx global>{`
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Home;
|
||||
108
packages/now-next/test/fixtures/27-preview-mode/additional.js
vendored
Normal file
108
packages/now-next/test/fixtures/27-preview-mode/additional.js
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
/* eslint-env jest */
|
||||
const cheerio = require('cheerio');
|
||||
const fetch = require('node-fetch');
|
||||
const setCookieParser = require('set-cookie-parser');
|
||||
|
||||
module.exports = function (ctx) {
|
||||
let previewCookie;
|
||||
|
||||
it('should enable preview mode successfully', async () => {
|
||||
const res = await fetch(`${ctx.deploymentUrl}/api/enable`);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
const cookies = setCookieParser.parse(
|
||||
setCookieParser.splitCookiesString(res.headers.get('set-cookie'))
|
||||
);
|
||||
const bypassCookie = cookies.find(
|
||||
(cookie) => cookie.name === '__prerender_bypass'
|
||||
);
|
||||
const previewDataCookie = cookies.find(
|
||||
(cookie) => cookie.name === '__next_preview_data'
|
||||
);
|
||||
|
||||
expect(bypassCookie).toBeDefined();
|
||||
expect(previewDataCookie).toBeDefined();
|
||||
expect(bypassCookie.value.length > 0).toBe(true);
|
||||
expect(previewDataCookie.value.length > 0).toBe(true);
|
||||
|
||||
previewCookie = cookies.reduce((prev, cur) => {
|
||||
return `${prev}${prev ? ';' : ''}${cur.name}=${cur.value}`;
|
||||
}, '');
|
||||
});
|
||||
|
||||
it('should disable preview mode successfully', async () => {
|
||||
const res = await fetch(`${ctx.deploymentUrl}/api/disable`);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
const cookies = setCookieParser.parse(
|
||||
setCookieParser.splitCookiesString(res.headers.get('set-cookie'))
|
||||
);
|
||||
const bypassCookie = cookies.find(
|
||||
(cookie) => cookie.name === '__prerender_bypass'
|
||||
);
|
||||
const previewDataCookie = cookies.find(
|
||||
(cookie) => cookie.name === '__next_preview_data'
|
||||
);
|
||||
|
||||
expect(bypassCookie).toBeDefined();
|
||||
expect(previewDataCookie).toBeDefined();
|
||||
expect(bypassCookie.value.length === 0).toBe(true);
|
||||
expect(previewDataCookie.value.length === 0).toBe(true);
|
||||
});
|
||||
|
||||
it('should render the page on-demand with preview mode enabled (normal page)', async () => {
|
||||
const res = await fetch(`${ctx.deploymentUrl}/docs`);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
const html = await res.text();
|
||||
const $ = cheerio.load(html);
|
||||
const props = JSON.parse($('#props').text());
|
||||
const random = props.random;
|
||||
|
||||
expect(props.hello).toBe('world');
|
||||
|
||||
const previewRes = await fetch(`${ctx.deploymentUrl}/docs`, {
|
||||
headers: {
|
||||
Cookie: previewCookie,
|
||||
},
|
||||
});
|
||||
expect(previewRes.status).toBe(200);
|
||||
|
||||
const previewHtml = await previewRes.text();
|
||||
const preview$ = cheerio.load(previewHtml);
|
||||
const previewProps = JSON.parse(preview$('#props').text());
|
||||
|
||||
expect(previewProps.random).not.toBe(random);
|
||||
expect(isNaN(previewProps.random)).toBe(false);
|
||||
expect(previewProps.hello).toBe('world');
|
||||
});
|
||||
|
||||
it('should render the page on-demand with preview mode enabled (dynamic page)', async () => {
|
||||
const res = await fetch(`${ctx.deploymentUrl}/docs/first`);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
const html = await res.text();
|
||||
const $ = cheerio.load(html);
|
||||
const props = JSON.parse($('#props').text());
|
||||
const random = props.random;
|
||||
|
||||
expect(props.hello).toBe('world');
|
||||
expect(props.params).toEqual({ rest: ['first'] });
|
||||
|
||||
const previewRes = await fetch(`${ctx.deploymentUrl}/docs/first`, {
|
||||
headers: {
|
||||
Cookie: previewCookie,
|
||||
},
|
||||
});
|
||||
expect(previewRes.status).toBe(200);
|
||||
|
||||
const previewHtml = await previewRes.text();
|
||||
const preview$ = cheerio.load(previewHtml);
|
||||
const previewProps = JSON.parse(preview$('#props').text());
|
||||
|
||||
expect(previewProps.random).not.toBe(random);
|
||||
expect(isNaN(previewProps.random)).toBe(false);
|
||||
expect(previewProps.hello).toBe('world');
|
||||
expect(previewProps.params).toEqual({ rest: ['first'] });
|
||||
});
|
||||
};
|
||||
5
packages/now-next/test/fixtures/27-preview-mode/next.config.js
vendored
Normal file
5
packages/now-next/test/fixtures/27-preview-mode/next.config.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
};
|
||||
4
packages/now-next/test/fixtures/27-preview-mode/now.json
vendored
Normal file
4
packages/now-next/test/fixtures/27-preview-mode/now.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "package.json", "use": "@vercel/next" }]
|
||||
}
|
||||
7
packages/now-next/test/fixtures/27-preview-mode/package.json
vendored
Normal file
7
packages/now-next/test/fixtures/27-preview-mode/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
4
packages/now-next/test/fixtures/27-preview-mode/pages/api/disable.js
vendored
Normal file
4
packages/now-next/test/fixtures/27-preview-mode/pages/api/disable.js
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export default (req, res) => {
|
||||
res.clearPreviewData();
|
||||
res.end('preview mode disabled');
|
||||
};
|
||||
4
packages/now-next/test/fixtures/27-preview-mode/pages/api/enable.js
vendored
Normal file
4
packages/now-next/test/fixtures/27-preview-mode/pages/api/enable.js
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export default (req, res) => {
|
||||
res.setPreviewData({ hello: 'world' });
|
||||
res.end('preview mode enabled');
|
||||
};
|
||||
24
packages/now-next/test/fixtures/27-preview-mode/pages/docs/[...rest].js
vendored
Normal file
24
packages/now-next/test/fixtures/27-preview-mode/pages/docs/[...rest].js
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
export const getStaticProps = (ctx) => {
|
||||
console.log('previewData', ctx.previewData);
|
||||
|
||||
return {
|
||||
props: {
|
||||
hello: 'world',
|
||||
params: ctx.params,
|
||||
random: Math.random(),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticPaths = () => {
|
||||
return {
|
||||
paths: [['first'], ['second'], ['another', 'one']].map((rest) => ({
|
||||
params: { rest },
|
||||
})),
|
||||
fallback: false,
|
||||
};
|
||||
};
|
||||
|
||||
export default function Docs(props) {
|
||||
return <p id="props">{JSON.stringify(props)}</p>;
|
||||
}
|
||||
14
packages/now-next/test/fixtures/27-preview-mode/pages/docs/index.js
vendored
Normal file
14
packages/now-next/test/fixtures/27-preview-mode/pages/docs/index.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
export const getStaticProps = (ctx) => {
|
||||
console.log('previewData', ctx.previewData);
|
||||
|
||||
return {
|
||||
props: {
|
||||
hello: 'world',
|
||||
random: Math.random(),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default function Docs(props) {
|
||||
return <p id="props">{JSON.stringify(props)}</p>;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/node",
|
||||
"version": "1.7.2-canary.1",
|
||||
"version": "1.7.2",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@vercel/ruby",
|
||||
"author": "Nathan Cahill <nathan@nathancahill.com>",
|
||||
"version": "1.2.3-canary.0",
|
||||
"version": "1.2.3",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/static-build",
|
||||
"version": "0.17.5-canary.0",
|
||||
"version": "0.17.5",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/static-builds",
|
||||
|
||||
@@ -63,7 +63,7 @@ function validateDistDir(distDir: string) {
|
||||
if (!exists()) {
|
||||
throw new NowBuildError({
|
||||
code: 'STATIC_BUILD_NO_OUT_DIR',
|
||||
message: `No Output Directory named "${distDirName}" found after the Build completed. You can configure the Output Directory in your project settings.`,
|
||||
message: `No Output Directory named "${distDirName}" found after the Build completed. You can configure the Output Directory in your Project Settings.`,
|
||||
link,
|
||||
});
|
||||
}
|
||||
@@ -445,8 +445,9 @@ export async function build({
|
||||
|
||||
if (!found) {
|
||||
throw new Error(
|
||||
`Missing required "${buildCommand ||
|
||||
buildScript}" script in "${entrypoint}"`
|
||||
`Missing required "${
|
||||
buildCommand || buildScript
|
||||
}" script in "${entrypoint}"`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ const fixturesPath = path.resolve(__dirname, 'fixtures');
|
||||
const testsThatFailToBuild = new Map([
|
||||
[
|
||||
'04-wrong-dist-dir',
|
||||
'No Output Directory named "out" found after the Build completed. You can configure the Output Directory in your project settings.',
|
||||
'No Output Directory named "out" found after the Build completed. You can configure the Output Directory in your Project Settings.',
|
||||
],
|
||||
['05-empty-dist-dir', 'The Output Directory "dist" is empty.'],
|
||||
[
|
||||
@@ -40,7 +40,7 @@ const testsThatFailToBuild = new Map([
|
||||
['07-nonzero-sh', 'Command "./build.sh" exited with 1'],
|
||||
[
|
||||
'22-docusaurus-2-build-fail',
|
||||
'No Output Directory named "build" found after the Build completed. You can configure the Output Directory in your project settings.',
|
||||
'No Output Directory named "build" found after the Build completed. You can configure the Output Directory in your Project Settings.',
|
||||
],
|
||||
[
|
||||
'36-hugo-version-not-found',
|
||||
|
||||
@@ -10109,6 +10109,11 @@ set-blocking@^2.0.0, set-blocking@~2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
|
||||
|
||||
set-cookie-parser@2.4.6:
|
||||
version "2.4.6"
|
||||
resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.4.6.tgz#43bdea028b9e6f176474ee5298e758b4a44799c3"
|
||||
integrity sha512-mNCnTUF0OYPwYzSHbdRdCfNNHqrne+HS5tS5xNb6yJbdP9wInV0q5xPLE0EyfV/Q3tImo3y/OXpD8Jn0Jtnjrg==
|
||||
|
||||
set-value@^2.0.0, set-value@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
|
||||
|
||||
Reference in New Issue
Block a user