mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-26 19:00:08 +00:00
Compare commits
36 Commits
@vercel/no
...
@vercel/cl
| 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 | ||
|
|
d3148dffaa | ||
|
|
30048cf4ff | ||
|
|
07c65fa5c8 | ||
|
|
411ec64986 | ||
|
|
e4d2cc704c | ||
|
|
38db720586 | ||
|
|
c26c7886be | ||
|
|
b0e5d308ca | ||
|
|
609b98cc73 | ||
|
|
e0ec6c792b | ||
|
|
b29db2fd1d | ||
|
|
b604ced99d | ||
|
|
b7fd69517e |
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
11913
examples/gatsby/yarn.lock
Normal file
11913
examples/gatsby/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,7 @@
|
||||
"lint-staged": "9.2.5",
|
||||
"node-fetch": "2.6.0",
|
||||
"npm-package-arg": "6.1.0",
|
||||
"prettier": "1.18.2"
|
||||
"prettier": "2.0.5"
|
||||
},
|
||||
"scripts": {
|
||||
"lerna": "lerna",
|
||||
@@ -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.14",
|
||||
"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-canary.4",
|
||||
"@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.4-canary.1"
|
||||
"@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)
|
||||
|
||||
@@ -4,7 +4,7 @@ import PCRE from 'pcre-to-regexp';
|
||||
import isURL from './is-url';
|
||||
import DevServer from './server';
|
||||
|
||||
import { HttpHeadersConfig, RouteResult } from './types';
|
||||
import { NowConfig, HttpHeadersConfig, RouteResult } from './types';
|
||||
import { isHandler, Route, HandleValue } from '@vercel/routing-utils';
|
||||
|
||||
export function resolveRouteParameters(
|
||||
@@ -50,6 +50,7 @@ export async function devRouter(
|
||||
reqMethod?: string,
|
||||
routes?: Route[],
|
||||
devServer?: DevServer,
|
||||
nowConfig?: NowConfig,
|
||||
previousHeaders?: HttpHeadersConfig,
|
||||
missRoutes?: Route[],
|
||||
phase?: HandleValue | null
|
||||
@@ -117,9 +118,12 @@ export async function devRouter(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (routeConfig.check && devServer && phase !== 'hit') {
|
||||
if (routeConfig.check && devServer && nowConfig && phase !== 'hit') {
|
||||
const { pathname = '/' } = url.parse(destPath);
|
||||
const hasDestFile = await devServer.hasFilesystem(pathname);
|
||||
const hasDestFile = await devServer.hasFilesystem(
|
||||
pathname,
|
||||
nowConfig
|
||||
);
|
||||
|
||||
if (!hasDestFile) {
|
||||
if (routeConfig.status && phase !== 'miss') {
|
||||
@@ -131,6 +135,7 @@ export async function devRouter(
|
||||
reqMethod,
|
||||
missRoutes,
|
||||
devServer,
|
||||
nowConfig,
|
||||
combinedHeaders,
|
||||
[],
|
||||
'miss'
|
||||
|
||||
@@ -12,7 +12,7 @@ import { randomBytes } from 'crypto';
|
||||
import serveHandler from 'serve-handler';
|
||||
import { watch, FSWatcher } from 'chokidar';
|
||||
import { parse as parseDotenv } from 'dotenv';
|
||||
import { basename, dirname, extname, join } from 'path';
|
||||
import path, { isAbsolute, basename, dirname, extname, join } from 'path';
|
||||
import once from '@tootallnate/once';
|
||||
import directoryTemplate from 'serve-handler/src/directory';
|
||||
import getPort from 'get-port';
|
||||
@@ -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();
|
||||
@@ -172,10 +174,8 @@ export default class DevServer {
|
||||
this.watchAggregationEvents = [];
|
||||
this.watchAggregationTimeout = 500;
|
||||
|
||||
this.filter = path => Boolean(path);
|
||||
this.podId = Math.random()
|
||||
.toString(32)
|
||||
.slice(-5);
|
||||
this.filter = (path) => Boolean(path);
|
||||
this.podId = Math.random().toString(32).slice(-5);
|
||||
|
||||
this.devServerPids = new Set();
|
||||
}
|
||||
@@ -213,8 +213,8 @@ export default class DevServer {
|
||||
}
|
||||
}
|
||||
|
||||
events = events.filter(event =>
|
||||
distPaths.every(distPath => !event.path.startsWith(distPath))
|
||||
events = events.filter((event) =>
|
||||
distPaths.every((distPath) => !event.path.startsWith(distPath))
|
||||
);
|
||||
|
||||
// First, update the `files` mapping of source files
|
||||
@@ -228,16 +228,7 @@ export default class DevServer {
|
||||
}
|
||||
}
|
||||
|
||||
const nowConfig = await this.getNowConfig(false);
|
||||
|
||||
// Update the env vars configuration
|
||||
const nowConfigBuild = nowConfig.build || {};
|
||||
const [runEnv, buildEnv] = await Promise.all([
|
||||
this.getLocalEnv('.env', nowConfig.env),
|
||||
this.getLocalEnv('.env.build', nowConfigBuild.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);
|
||||
@@ -283,18 +274,19 @@ export default class DevServer {
|
||||
for (const [result, [requestPath, match]] of needsRebuild) {
|
||||
if (
|
||||
requestPath === null ||
|
||||
(await shouldServe(match, this.files, requestPath, this))
|
||||
(await shouldServe(match, this.files, requestPath, this, nowConfig))
|
||||
) {
|
||||
this.triggerBuild(
|
||||
match,
|
||||
requestPath,
|
||||
null,
|
||||
nowConfig,
|
||||
result,
|
||||
filesChangedArray,
|
||||
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);
|
||||
});
|
||||
@@ -378,7 +370,7 @@ export default class DevServer {
|
||||
this,
|
||||
fileList
|
||||
);
|
||||
const sources = matches.map(m => m.src);
|
||||
const sources = matches.map((m) => m.src);
|
||||
|
||||
if (isInitial && fileList.length === 0) {
|
||||
this.output.warn('There are no files inside your deployment.');
|
||||
@@ -505,57 +497,35 @@ 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 pkg = await this.getPackageJson();
|
||||
|
||||
// The default empty `vercel.json` is used to serve all files as static
|
||||
// when no `vercel.json` is present
|
||||
let configPath = 'vercel.json';
|
||||
let config: NowConfig = {
|
||||
version: 2,
|
||||
[fileNameSymbol]: configPath,
|
||||
};
|
||||
|
||||
try {
|
||||
configPath = getNowConfigPath(this.cwd);
|
||||
this.output.debug(`Reading ${configPath}`);
|
||||
config = JSON.parse(await fs.readFile(configPath, 'utf8'));
|
||||
config[fileNameSymbol] = basename(configPath);
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
this.output.debug(err.toString());
|
||||
} else if (err.name === 'SyntaxError') {
|
||||
this.output.warn(
|
||||
`There is a syntax error in ${configPath}: ${err.message}`
|
||||
);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
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`
|
||||
);
|
||||
const [
|
||||
pkg = null,
|
||||
// The default empty `vercel.json` is used to serve all
|
||||
// files as static when no `vercel.json` is present
|
||||
config = { version: 2, [fileNameSymbol]: 'vercel.json' },
|
||||
] = await Promise.all([
|
||||
this.readJsonFile<PackageJson>('package.json'),
|
||||
this.readJsonFile<NowConfig>(configPath),
|
||||
]);
|
||||
|
||||
await this.validateNowConfig(config);
|
||||
const { error: routeError, routes: maybeRoutes } = getTransformedRoutes({
|
||||
@@ -572,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,
|
||||
@@ -595,7 +570,7 @@ export default class DevServer {
|
||||
}
|
||||
|
||||
if (warnings && warnings.length > 0) {
|
||||
warnings.forEach(warning => this.output.warn(warning.message));
|
||||
warnings.forEach((warning) => this.output.warn(warning.message));
|
||||
}
|
||||
|
||||
if (builders) {
|
||||
@@ -605,6 +580,8 @@ export default class DevServer {
|
||||
|
||||
config.builds = config.builds || [];
|
||||
config.builds.push(...builders);
|
||||
|
||||
delete config.functions;
|
||||
}
|
||||
|
||||
let routes: Route[] = [];
|
||||
@@ -638,34 +615,50 @@ 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 getPackageJson(): Promise<PackageJson | null> {
|
||||
const pkgPath = join(this.cwd, 'package.json');
|
||||
let pkg: PackageJson | null = null;
|
||||
|
||||
this.output.debug('Reading `package.json` file');
|
||||
async readJsonFile<T>(
|
||||
filePath: string
|
||||
): Promise<WithFileNameSymbol<T> | void> {
|
||||
let rel, abs;
|
||||
if (isAbsolute(filePath)) {
|
||||
rel = path.relative(this.cwd, filePath);
|
||||
abs = filePath;
|
||||
} else {
|
||||
rel = filePath;
|
||||
abs = join(this.cwd, filePath);
|
||||
}
|
||||
this.output.debug(`Reading \`${rel}\` file`);
|
||||
|
||||
try {
|
||||
pkg = JSON.parse(await fs.readFile(pkgPath, '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 `package.json` file present');
|
||||
this.output.debug(`No \`${rel}\` file present`);
|
||||
} else if (err.name === 'SyntaxError') {
|
||||
this.output.warn(
|
||||
`There is a syntax error in the \`package.json\` file: ${err.message}`
|
||||
`There is a syntax error in the \`${rel}\` file: ${err.message}`
|
||||
);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
return pkg;
|
||||
}
|
||||
|
||||
async tryValidateOrExit(
|
||||
@@ -761,15 +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 nowConfigBuild = nowConfig.build || {};
|
||||
const [runEnv, buildEnv] = await Promise.all([
|
||||
this.getLocalEnv('.env', nowConfig.env),
|
||||
this.getLocalEnv('.env.build', nowConfigBuild.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);
|
||||
@@ -797,11 +782,11 @@ export default class DevServer {
|
||||
// get their "build matches" invalidated so that the new version is used.
|
||||
this.updateBuildersTimeout = setTimeout(() => {
|
||||
this.updateBuildersPromise = updateBuilders(builders, this.output)
|
||||
.then(updatedBuilders => {
|
||||
.then((updatedBuilders) => {
|
||||
this.updateBuildersPromise = null;
|
||||
this.invalidateBuildMatches(nowConfig, updatedBuilders);
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
this.updateBuildersPromise = null;
|
||||
this.output.error(`Failed to update builders: ${err.message}`);
|
||||
this.output.debug(err.stack);
|
||||
@@ -1142,6 +1127,7 @@ export default class DevServer {
|
||||
match: BuildMatch,
|
||||
requestPath: string | null,
|
||||
req: http.IncomingMessage | null,
|
||||
nowConfig: NowConfig,
|
||||
previousBuildResult?: BuildResult,
|
||||
filesChanged?: string[],
|
||||
filesRemoved?: string[]
|
||||
@@ -1157,10 +1143,11 @@ export default class DevServer {
|
||||
// A build for `buildKey` is already in progress, so don't trigger
|
||||
// another rebuild for this request - just wait on the existing one.
|
||||
let msg = `De-duping build "${buildKey}"`;
|
||||
if (req) msg += ` for "${req.method} ${req.url}"`;
|
||||
if (req) {
|
||||
msg += ` for "${req.method} ${req.url}"`;
|
||||
}
|
||||
this.output.debug(msg);
|
||||
} else {
|
||||
const nowConfig = await this.getNowConfig();
|
||||
if (previousBuildResult) {
|
||||
// Tear down any `output` assets from a previous build, so that they
|
||||
// are not available to be served while the rebuild is in progress.
|
||||
@@ -1171,7 +1158,9 @@ export default class DevServer {
|
||||
}
|
||||
}
|
||||
let msg = `Building asset "${buildKey}"`;
|
||||
if (req) msg += ` for "${req.method} ${req.url}"`;
|
||||
if (req) {
|
||||
msg += ` for "${req.method} ${req.url}"`;
|
||||
}
|
||||
this.output.debug(msg);
|
||||
buildPromise = executeBuild(
|
||||
nowConfig,
|
||||
@@ -1252,7 +1241,7 @@ export default class DevServer {
|
||||
const { status, headers, dest } = routeResult;
|
||||
const location = headers['location'] || dest;
|
||||
|
||||
if (status && location && (300 <= status && status <= 399)) {
|
||||
if (status && location && 300 <= status && status <= 399) {
|
||||
this.output.debug(`Route found with redirect status code ${status}`);
|
||||
await this.sendRedirect(req, res, nowRequestId, location, status);
|
||||
return true;
|
||||
@@ -1342,6 +1331,7 @@ export default class DevServer {
|
||||
req.method,
|
||||
phaseRoutes,
|
||||
this,
|
||||
nowConfig,
|
||||
prevHeaders,
|
||||
missRoutes,
|
||||
phase
|
||||
@@ -1369,7 +1359,8 @@ export default class DevServer {
|
||||
this.buildMatches,
|
||||
this.files,
|
||||
routeResult.dest,
|
||||
this
|
||||
this,
|
||||
nowConfig
|
||||
);
|
||||
|
||||
if (
|
||||
@@ -1392,6 +1383,7 @@ export default class DevServer {
|
||||
req.method,
|
||||
missRoutes,
|
||||
this,
|
||||
nowConfig,
|
||||
routeResult.headers,
|
||||
[],
|
||||
'miss'
|
||||
@@ -1401,7 +1393,8 @@ export default class DevServer {
|
||||
this.buildMatches,
|
||||
this.files,
|
||||
routeResult.dest,
|
||||
this
|
||||
this,
|
||||
nowConfig
|
||||
);
|
||||
if (
|
||||
await this.exitWithStatus(
|
||||
@@ -1424,6 +1417,7 @@ export default class DevServer {
|
||||
req.method,
|
||||
hitRoutes,
|
||||
this,
|
||||
nowConfig,
|
||||
routeResult.headers,
|
||||
[],
|
||||
'hit'
|
||||
@@ -1438,6 +1432,7 @@ export default class DevServer {
|
||||
req.method,
|
||||
errorRoutes,
|
||||
this,
|
||||
nowConfig,
|
||||
routeResult.headers,
|
||||
[],
|
||||
'error'
|
||||
@@ -1447,7 +1442,8 @@ export default class DevServer {
|
||||
this.buildMatches,
|
||||
this.files,
|
||||
routeResultForError.dest,
|
||||
this
|
||||
this,
|
||||
nowConfig
|
||||
);
|
||||
|
||||
if (matchForError) {
|
||||
@@ -1535,7 +1531,8 @@ export default class DevServer {
|
||||
newUrl,
|
||||
req.method,
|
||||
buildResult.routes,
|
||||
this
|
||||
this,
|
||||
nowConfig
|
||||
);
|
||||
if (matchedRoute.found && callLevel === 0) {
|
||||
debug(`Found matching route ${matchedRoute.dest} for ${newUrl}`);
|
||||
@@ -1631,7 +1628,7 @@ export default class DevServer {
|
||||
let foundAsset = findAsset(match, requestPath, nowConfig);
|
||||
|
||||
if (!foundAsset && callLevel === 0) {
|
||||
await this.triggerBuild(match, buildRequestPath, req);
|
||||
await this.triggerBuild(match, buildRequestPath, req, nowConfig);
|
||||
|
||||
// Since the `asset` was just built, resolve again to get the new asset
|
||||
foundAsset = findAsset(match, requestPath, nowConfig);
|
||||
@@ -1663,8 +1660,9 @@ export default class DevServer {
|
||||
|
||||
const { asset, assetKey } = foundAsset;
|
||||
debug(
|
||||
`Serving asset: [${asset.type}] ${assetKey} ${(asset as any)
|
||||
.contentType || ''}`
|
||||
`Serving asset: [${asset.type}] ${assetKey} ${
|
||||
(asset as any).contentType || ''
|
||||
}`
|
||||
);
|
||||
|
||||
/* eslint-disable no-case-declarations */
|
||||
@@ -1783,7 +1781,7 @@ export default class DevServer {
|
||||
|
||||
const dirs: Set<string> = new Set();
|
||||
const files = Array.from(this.buildMatches.keys())
|
||||
.filter(p => {
|
||||
.filter((p) => {
|
||||
const base = basename(p);
|
||||
if (
|
||||
base === 'now.json' ||
|
||||
@@ -1804,7 +1802,7 @@ export default class DevServer {
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map(p => {
|
||||
.map((p) => {
|
||||
let base = basename(p);
|
||||
let ext = '';
|
||||
let type = 'file';
|
||||
@@ -1856,8 +1854,17 @@ export default class DevServer {
|
||||
return true;
|
||||
}
|
||||
|
||||
async hasFilesystem(dest: string): Promise<boolean> {
|
||||
if (await findBuildMatch(this.buildMatches, this.files, dest, this, true)) {
|
||||
async hasFilesystem(dest: string, nowConfig: NowConfig): Promise<boolean> {
|
||||
if (
|
||||
await findBuildMatch(
|
||||
this.buildMatches,
|
||||
this.files,
|
||||
dest,
|
||||
this,
|
||||
nowConfig,
|
||||
true
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -2038,13 +2045,23 @@ async function findBuildMatch(
|
||||
files: BuilderInputs,
|
||||
requestPath: string,
|
||||
devServer: DevServer,
|
||||
isFilesystem?: boolean
|
||||
nowConfig: NowConfig,
|
||||
isFilesystem = false
|
||||
): Promise<BuildMatch | null> {
|
||||
requestPath = requestPath.replace(/^\//, '');
|
||||
|
||||
let bestIndexMatch: undefined | BuildMatch;
|
||||
for (const match of matches.values()) {
|
||||
if (await shouldServe(match, files, requestPath, devServer, isFilesystem)) {
|
||||
if (
|
||||
await shouldServe(
|
||||
match,
|
||||
files,
|
||||
requestPath,
|
||||
devServer,
|
||||
nowConfig,
|
||||
isFilesystem
|
||||
)
|
||||
) {
|
||||
if (!isIndex(match.src)) {
|
||||
return match;
|
||||
} else {
|
||||
@@ -2066,14 +2083,14 @@ async function shouldServe(
|
||||
files: BuilderInputs,
|
||||
requestPath: string,
|
||||
devServer: DevServer,
|
||||
isFilesystem?: boolean
|
||||
nowConfig: NowConfig,
|
||||
isFilesystem = false
|
||||
): Promise<boolean> {
|
||||
const {
|
||||
src,
|
||||
config,
|
||||
builderWithPkg: { builder },
|
||||
} = match;
|
||||
const nowConfig = await devServer.getNowConfig();
|
||||
const cleanSrc = src.endsWith('.html') ? src.slice(0, -5) : src;
|
||||
const trimmedPath = requestPath.endsWith('/')
|
||||
? requestPath.slice(0, -1)
|
||||
@@ -2117,7 +2134,7 @@ async function shouldServe(
|
||||
return true;
|
||||
} else if (
|
||||
!isFilesystem &&
|
||||
(await findMatchingRoute(match, requestPath, devServer))
|
||||
(await findMatchingRoute(match, requestPath, devServer, nowConfig))
|
||||
) {
|
||||
// If there's no `shouldServe()` function and no matched asset, then look
|
||||
// up if there's a matching build route on the `match` that has already
|
||||
@@ -2130,7 +2147,8 @@ async function shouldServe(
|
||||
async function findMatchingRoute(
|
||||
match: BuildMatch,
|
||||
requestPath: string,
|
||||
devServer: DevServer
|
||||
devServer: DevServer,
|
||||
nowConfig: NowConfig
|
||||
): Promise<RouteResult | void> {
|
||||
const reqUrl = `/${requestPath}`;
|
||||
for (const buildResult of match.buildResults.values()) {
|
||||
@@ -2139,7 +2157,8 @@ async function findMatchingRoute(
|
||||
reqUrl,
|
||||
undefined,
|
||||
buildResult.routes,
|
||||
devServer
|
||||
devServer,
|
||||
nowConfig
|
||||
);
|
||||
if (route.found) {
|
||||
return route;
|
||||
@@ -2195,7 +2214,7 @@ function isIndex(path: string): boolean {
|
||||
|
||||
function minimatches(files: string[], pattern: string): boolean {
|
||||
return files.some(
|
||||
file => file === pattern || minimatch(file, pattern, { dot: true })
|
||||
(file) => file === pattern || minimatch(file, pattern, { dot: true })
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2225,7 +2244,7 @@ function needsBlockingBuild(buildMatch: BuildMatch): boolean {
|
||||
}
|
||||
|
||||
async function sleep(n: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, n));
|
||||
return new Promise((resolve) => setTimeout(resolve, n));
|
||||
}
|
||||
|
||||
async function checkForPort(
|
||||
|
||||
@@ -34,10 +34,9 @@ const vercelConfigSchema = {
|
||||
};
|
||||
|
||||
const ajv = new Ajv();
|
||||
const validate = ajv.compile(vercelConfigSchema);
|
||||
|
||||
export function validateConfig(config: NowConfig): NowBuildError | null {
|
||||
const validate = ajv.compile(vercelConfigSchema);
|
||||
|
||||
if (!validate(config)) {
|
||||
if (validate.errors && validate.errors[0]) {
|
||||
const error = validate.errors[0];
|
||||
@@ -48,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-canary.4",
|
||||
"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"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import buildUtils from './build-utils';
|
||||
import url from 'url';
|
||||
const {
|
||||
createLambda,
|
||||
debug,
|
||||
@@ -14,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,
|
||||
@@ -33,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,
|
||||
@@ -45,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,
|
||||
@@ -70,7 +71,7 @@ import {
|
||||
syncEnvVars,
|
||||
validateEntrypoint,
|
||||
} from './utils';
|
||||
// import findUp from 'find-up';
|
||||
import findUp from 'find-up';
|
||||
import { Sema } from 'async-sema';
|
||||
|
||||
interface BuildParamsMeta {
|
||||
@@ -90,7 +91,7 @@ export const version = 2;
|
||||
const htmlContentType = 'text/html; charset=utf-8';
|
||||
const nowDevChildProcesses = new Set<ChildProcess>();
|
||||
|
||||
['SIGINT', 'SIGTERM'].forEach(signal => {
|
||||
['SIGINT', 'SIGTERM'].forEach((signal) => {
|
||||
process.once(signal as NodeJS.Signals, () => {
|
||||
for (const child of nowDevChildProcesses) {
|
||||
debug(
|
||||
@@ -107,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 {
|
||||
@@ -138,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;
|
||||
}
|
||||
@@ -217,60 +247,43 @@ 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)),
|
||||
});
|
||||
|
||||
let hasLegacyRoutes = false;
|
||||
const hasFunctionsConfig = !!config.functions;
|
||||
|
||||
if (nowJsonPath) {
|
||||
const nowJsonData = JSON.parse(await readFile(nowJsonPath, 'utf8'));
|
||||
|
||||
if (Array.isArray(nowJsonData.routes) && nowJsonData.routes.length > 0) {
|
||||
hasLegacyRoutes = true;
|
||||
console.warn(
|
||||
`WARNING: your application is being opted out of @vercel/next's optimized lambdas mode due to legacy routes in ${path.basename(
|
||||
nowJsonPath
|
||||
)}. http://err.sh/vercel/vercel/next-legacy-routes-optimized-lambdas`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// let nowJsonPath = Object.keys(files).find(file => {
|
||||
// return file.endsWith('now.json') || file.endsWith('vercel.json')
|
||||
// })
|
||||
|
||||
// if (nowJsonPath) nowJsonPath = files[nowJsonPath].fsPath
|
||||
|
||||
// if (!nowJsonPath) {
|
||||
// nowJsonPath = await findUp(['now.json', 'vercel.json'], {
|
||||
// cwd: path.join(workPath, path.dirname(entrypoint))
|
||||
// })
|
||||
// }
|
||||
|
||||
// let hasLegacyRoutes = false;
|
||||
// const hasFunctionsConfig = !!config.functions;
|
||||
|
||||
// if (nowJsonPath) {
|
||||
// const nowJsonData = JSON.parse(await readFile(nowJsonPath, 'utf8'));
|
||||
|
||||
// if (Array.isArray(nowJsonData.routes) && nowJsonData.routes.length > 0) {
|
||||
// hasLegacyRoutes = true;
|
||||
// console.warn(
|
||||
// `WARNING: your application is being opted out of @vercel/next's optimized lambdas mode due to legacy routes in ${path.basename(
|
||||
// nowJsonPath
|
||||
// )}. http://err.sh/vercel/vercel/next-legacy-routes-optimized-lambdas`
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (hasFunctionsConfig) {
|
||||
// console.warn(
|
||||
// `WARNING: Your application is being opted out of "@vercel/next" optimized lambdas mode due to \`functions\` config.\nMore info: http://err.sh/vercel/vercel/next-functions-config-optimized-lambdas`
|
||||
// );
|
||||
// }
|
||||
if (hasFunctionsConfig) {
|
||||
console.warn(
|
||||
`WARNING: Your application is being opted out of "@vercel/next" optimized lambdas mode due to \`functions\` config.\nMore info: http://err.sh/vercel/vercel/next-functions-config-optimized-lambdas`
|
||||
);
|
||||
}
|
||||
|
||||
// default to true but still allow opting out with the config
|
||||
const isSharedLambdas = !!config.sharedLambdas;
|
||||
// !hasLegacyRoutes &&
|
||||
// !hasFunctionsConfig &&
|
||||
// typeof config.sharedLambdas === 'undefined'
|
||||
// ? true
|
||||
// : !!config.sharedLambdas;
|
||||
const isSharedLambdas =
|
||||
!hasLegacyRoutes &&
|
||||
!hasFunctionsConfig &&
|
||||
typeof config.sharedLambdas === 'undefined'
|
||||
? true
|
||||
: !!config.sharedLambdas;
|
||||
|
||||
if (meta.isDev) {
|
||||
let childProcess: ChildProcess | undefined;
|
||||
@@ -315,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'}`);
|
||||
@@ -369,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...');
|
||||
@@ -417,7 +432,7 @@ export const build = async ({
|
||||
const routesManifest = await getRoutesManifest(
|
||||
entryPath,
|
||||
outputDirectory,
|
||||
realNextVersion
|
||||
nextVersion
|
||||
);
|
||||
const prerenderManifest = await getPrerenderManifest(entryPath);
|
||||
const headers: Route[] = [];
|
||||
@@ -458,21 +473,19 @@ export const build = async ({
|
||||
}
|
||||
|
||||
dataRoutes.push({
|
||||
src: dataRoute.dataRouteRegex.replace(
|
||||
/^\^/,
|
||||
`^${appMountPrefixNoTrailingSlash}`
|
||||
),
|
||||
src: (
|
||||
dataRoute.namedDataRouteRegex || dataRoute.dataRouteRegex
|
||||
).replace(/^\^/, `^${appMountPrefixNoTrailingSlash}`),
|
||||
dest: path.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
// make sure to route SSG data route to the data prerender
|
||||
// output, we don't do this for SSP routes since they don't
|
||||
// have a separate data output
|
||||
(ssgDataRoute && ssgDataRoute.dataRoute) || dataRoute.page,
|
||||
`${
|
||||
`${(ssgDataRoute && ssgDataRoute.dataRoute) || dataRoute.page}${
|
||||
dataRoute.routeKeys
|
||||
? `?${Object.keys(dataRoute.routeKeys)
|
||||
.map(key => `${dataRoute.routeKeys![key]}=$${key}`)
|
||||
.map((key) => `${dataRoute.routeKeys![key]}=$${key}`)
|
||||
.join('&')}`
|
||||
: ''
|
||||
}`
|
||||
@@ -703,7 +716,7 @@ export const build = async ({
|
||||
);
|
||||
const nodeModules = excludeFiles(
|
||||
await glob('node_modules/**', entryPath),
|
||||
file => file.startsWith('node_modules/.cache')
|
||||
(file) => file.startsWith('node_modules/.cache')
|
||||
);
|
||||
const launcherFiles = {
|
||||
'now__bridge.js': new FileFsRef({
|
||||
@@ -732,7 +745,7 @@ export const build = async ({
|
||||
const launcherData = await readFile(launcherPath, 'utf8');
|
||||
|
||||
await Promise.all(
|
||||
Object.keys(pages).map(async page => {
|
||||
Object.keys(pages).map(async (page) => {
|
||||
// These default pages don't have to be handled as they'd always 404
|
||||
if (['_app.js', '_error.js', '_document.js'].includes(page)) {
|
||||
return;
|
||||
@@ -848,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!'
|
||||
@@ -870,6 +880,7 @@ export const build = async ({
|
||||
[filePath: string]: FileFsRef;
|
||||
};
|
||||
|
||||
let canUsePreviewMode = false;
|
||||
let pseudoLayerBytes = 0;
|
||||
let apiPseudoLayerBytes = 0;
|
||||
const pseudoLayers: PseudoLayer[] = [];
|
||||
@@ -892,11 +903,12 @@ export const build = async ({
|
||||
|
||||
const apiPages: string[] = [];
|
||||
const nonApiPages: string[] = [];
|
||||
const allPagePaths = Object.keys(pages).map(page => pages[page].fsPath);
|
||||
const allPagePaths = Object.keys(pages).map((page) => pages[page].fsPath);
|
||||
|
||||
for (const page of allPagePaths) {
|
||||
if (isApiPage(page)) {
|
||||
apiPages.push(page);
|
||||
canUsePreviewMode = true;
|
||||
} else {
|
||||
nonApiPages.push(page);
|
||||
}
|
||||
@@ -907,10 +919,10 @@ export const build = async ({
|
||||
reasons: apiReasons,
|
||||
} = await nodeFileTrace(apiPages, { base: workPath });
|
||||
|
||||
const { fileList, reasons: nonApiReasons } = await nodeFileTrace(
|
||||
nonApiPages,
|
||||
{ base: workPath }
|
||||
);
|
||||
const {
|
||||
fileList,
|
||||
reasons: nonApiReasons,
|
||||
} = await nodeFileTrace(nonApiPages, { base: workPath });
|
||||
|
||||
debug(`node-file-trace result for pages: ${fileList}`);
|
||||
|
||||
@@ -984,7 +996,7 @@ export const build = async ({
|
||||
debug(
|
||||
'detected (legacy) assets to be bundled with serverless function:'
|
||||
);
|
||||
assetKeys.forEach(assetFile => debug(`\t${assetFile}`));
|
||||
assetKeys.forEach((assetFile) => debug(`\t${assetFile}`));
|
||||
debug(
|
||||
'\nPlease upgrade to Next.js 9.1 to leverage modern asset handling.'
|
||||
);
|
||||
@@ -1124,7 +1136,7 @@ export const build = async ({
|
||||
}
|
||||
} else {
|
||||
await Promise.all(
|
||||
pageKeys.map(async page => {
|
||||
pageKeys.map(async (page) => {
|
||||
// These default pages don't have to be handled as they'd always 404
|
||||
if (['_app.js', '_document.js'].includes(page)) {
|
||||
return;
|
||||
@@ -1205,8 +1217,8 @@ export const build = async ({
|
||||
false,
|
||||
routesManifest,
|
||||
new Set(prerenderManifest.omittedRoutes)
|
||||
).then(arr =>
|
||||
arr.map(route => {
|
||||
).then((arr) =>
|
||||
arr.map((route) => {
|
||||
route.src = route.src.replace('^', `^${dynamicPrefix}`);
|
||||
return route;
|
||||
})
|
||||
@@ -1216,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) {
|
||||
@@ -1230,7 +1257,7 @@ export const build = async ({
|
||||
const pages = {
|
||||
${groupPageKeys
|
||||
.map(
|
||||
page =>
|
||||
(page) =>
|
||||
`'${page}': require('./${path.join(
|
||||
'./',
|
||||
group.pages[page].pageFileName
|
||||
@@ -1275,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,
|
||||
}))
|
||||
@@ -1461,33 +1488,41 @@ export const build = async ({
|
||||
message: 'invariant: htmlFsRef != null && jsonFsRef != null',
|
||||
});
|
||||
}
|
||||
|
||||
if (!canUsePreviewMode) {
|
||||
htmlFsRef.contentType = htmlContentType;
|
||||
prerenders[outputPathPage] = htmlFsRef;
|
||||
prerenders[outputPathData] = jsonFsRef;
|
||||
}
|
||||
}
|
||||
|
||||
prerenders[outputPathPage] = new Prerender({
|
||||
expiration: initialRevalidate,
|
||||
lambda,
|
||||
fallback: htmlFsRef,
|
||||
group: prerenderGroup,
|
||||
bypassToken: prerenderManifest.bypassToken,
|
||||
});
|
||||
prerenders[outputPathData] = new Prerender({
|
||||
expiration: initialRevalidate,
|
||||
lambda,
|
||||
fallback: jsonFsRef,
|
||||
group: prerenderGroup,
|
||||
bypassToken: prerenderManifest.bypassToken,
|
||||
});
|
||||
if (prerenders[outputPathPage] == null) {
|
||||
prerenders[outputPathPage] = new Prerender({
|
||||
expiration: initialRevalidate,
|
||||
lambda,
|
||||
fallback: htmlFsRef,
|
||||
group: prerenderGroup,
|
||||
bypassToken: prerenderManifest.bypassToken,
|
||||
});
|
||||
prerenders[outputPathData] = new Prerender({
|
||||
expiration: initialRevalidate,
|
||||
lambda,
|
||||
fallback: jsonFsRef,
|
||||
group: prerenderGroup,
|
||||
bypassToken: prerenderManifest.bypassToken,
|
||||
});
|
||||
|
||||
++prerenderGroup;
|
||||
++prerenderGroup;
|
||||
}
|
||||
};
|
||||
|
||||
Object.keys(prerenderManifest.staticRoutes).forEach(route =>
|
||||
Object.keys(prerenderManifest.staticRoutes).forEach((route) =>
|
||||
onPrerenderRoute(route, { isBlocking: false, isFallback: false })
|
||||
);
|
||||
Object.keys(prerenderManifest.fallbackRoutes).forEach(route =>
|
||||
Object.keys(prerenderManifest.fallbackRoutes).forEach((route) =>
|
||||
onPrerenderRoute(route, { isBlocking: false, isFallback: true })
|
||||
);
|
||||
Object.keys(prerenderManifest.legacyBlockingRoutes).forEach(route =>
|
||||
Object.keys(prerenderManifest.legacyBlockingRoutes).forEach((route) =>
|
||||
onPrerenderRoute(route, { isBlocking: true, isFallback: false })
|
||||
);
|
||||
|
||||
@@ -1551,7 +1586,7 @@ export const build = async ({
|
||||
// We need to delete lambdas from output instead of omitting them from the
|
||||
// start since we rely on them for powering Preview Mode (read above in
|
||||
// onPrerenderRoute).
|
||||
prerenderManifest.omittedRoutes.forEach(routeKey => {
|
||||
prerenderManifest.omittedRoutes.forEach((routeKey) => {
|
||||
// Get the route file as it'd be mounted in the builder output
|
||||
const routeFileNoExt = path.posix.join(
|
||||
entryDirectory,
|
||||
@@ -1566,6 +1601,7 @@ export const build = async ({
|
||||
delete lambdas[routeFileNoExt];
|
||||
});
|
||||
}
|
||||
const mergedDataRoutesLambdaRoutes = [];
|
||||
const mergedDynamicRoutesLambdaRoutes = [];
|
||||
|
||||
if (isSharedLambdas) {
|
||||
@@ -1580,12 +1616,30 @@ export const build = async ({
|
||||
|
||||
mergedDynamicRoutesLambdaRoutes.push(route);
|
||||
|
||||
if (pageLambdaMap[route.dest!]) {
|
||||
const { pathname } = url.parse(route.dest!);
|
||||
|
||||
if (pathname && pageLambdaMap[pathname]) {
|
||||
mergedDynamicRoutesLambdaRoutes.push(
|
||||
dynamicPageLambdaRoutesMap[route.dest!]
|
||||
dynamicPageLambdaRoutesMap[pathname]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < dataRoutes.length; i++) {
|
||||
const route = dataRoutes[i];
|
||||
|
||||
mergedDataRoutesLambdaRoutes.push(route);
|
||||
|
||||
const { pathname } = url.parse(route.dest!);
|
||||
|
||||
if (
|
||||
pathname &&
|
||||
pageLambdaMap[pathname] &&
|
||||
dynamicPageLambdaRoutesMap[pathname]
|
||||
) {
|
||||
mergedDataRoutesLambdaRoutes.push(dynamicPageLambdaRoutesMap[pathname]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -1655,7 +1709,7 @@ export const build = async ({
|
||||
{ handle: 'rewrite' },
|
||||
|
||||
// /_next/data routes for getServerProps/getStaticProps pages
|
||||
...dataRoutes,
|
||||
...(isSharedLambdas ? mergedDataRoutesLambdaRoutes : dataRoutes),
|
||||
|
||||
// re-check page routes to map them to the lambda
|
||||
...pageLambdaRoutes,
|
||||
@@ -1695,9 +1749,12 @@ export const build = async ({
|
||||
src: path.join('/', entryDirectory, '.*'),
|
||||
// if static 404 is not present but we have pages/404.js
|
||||
// it is a lambda due to _app getInitialProps
|
||||
dest: path.join('/', (static404Page
|
||||
? static404Page
|
||||
: pageLambdaMap[page404Path]) as string),
|
||||
dest: path.join(
|
||||
'/',
|
||||
(static404Page
|
||||
? static404Page
|
||||
: pageLambdaMap[page404Path]) as string
|
||||
),
|
||||
|
||||
status: 404,
|
||||
headers: {
|
||||
@@ -1737,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,
|
||||
|
||||
38
packages/now-next/test/fixtures/00-shared-lambdas/now.json
vendored
Normal file
38
packages/now-next/test/fixtures/00-shared-lambdas/now.json
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"version": 2,
|
||||
"uploadNowJson": true,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next"
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/teams/invite/hello",
|
||||
"mustContain": "hello from /teams/invite/[inviteCode] hello"
|
||||
},
|
||||
{
|
||||
"path": "/groups/first",
|
||||
"mustContain": "hello from /groups/[id] first"
|
||||
},
|
||||
{
|
||||
"path": "/groups/second",
|
||||
"mustContain": "hello from /groups/[id] second"
|
||||
},
|
||||
{
|
||||
"path": "/groups/third",
|
||||
"mustContain": "loading..."
|
||||
},
|
||||
{
|
||||
"path": "/another/invite/hello",
|
||||
"mustContain": "hello from /[teamSlug]/[project]/[id]"
|
||||
},
|
||||
{
|
||||
"logMustNotContain": "WARNING: your application is being opted out of @vercel/next's optimized lambdas mode due to legacy routes"
|
||||
},
|
||||
{
|
||||
"logMustNotContain": "WARNING: Your application is being opted out of \"@vercel/next\" optimized lambdas mode due to `functions` config"
|
||||
}
|
||||
]
|
||||
}
|
||||
1
packages/now-next/test/fixtures/00-shared-lambdas/pages/[teamSlug]/[project]/[id].js
vendored
Normal file
1
packages/now-next/test/fixtures/00-shared-lambdas/pages/[teamSlug]/[project]/[id].js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export default () => 'hello from /[teamSlug]/[project]/[id]'
|
||||
18
packages/now-next/test/fixtures/00-shared-lambdas/pages/groups/[id].js
vendored
Normal file
18
packages/now-next/test/fixtures/00-shared-lambdas/pages/groups/[id].js
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
export const getStaticProps = ({ params }) => {
|
||||
return {
|
||||
props: {
|
||||
id: params.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const getStaticPaths = () => ({
|
||||
paths: ['first', 'second'].map(id => ({ params: { id }})),
|
||||
fallback: true
|
||||
})
|
||||
|
||||
export default ({ id }) => useRouter().isFallback
|
||||
? `loading...`
|
||||
: `hello from /groups/[id] ${id}`
|
||||
9
packages/now-next/test/fixtures/00-shared-lambdas/pages/teams/invite/[inviteCode].js
vendored
Normal file
9
packages/now-next/test/fixtures/00-shared-lambdas/pages/teams/invite/[inviteCode].js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
export const getServerSideProps = ({ params }) => {
|
||||
return {
|
||||
props: {
|
||||
code: params.inviteCode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ({ code }) => `hello from /teams/invite/[inviteCode] ${code}`
|
||||
3
packages/now-next/test/fixtures/05-spr-support/pages/api/noop.js
vendored
Normal file
3
packages/now-next/test/fixtures/05-spr-support/pages/api/noop.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default (_, res) => {
|
||||
res.send('OK');
|
||||
};
|
||||
@@ -17,6 +17,9 @@
|
||||
"path": "/api/memory",
|
||||
"status": 200,
|
||||
"mustContain": "128"
|
||||
},
|
||||
{
|
||||
"logMustContain": "WARNING: Your application is being opted out of \"@vercel/next\" optimized lambdas mode due to `functions` config"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,82 +1,80 @@
|
||||
module.exports = {
|
||||
experimental: {
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: '/redir1',
|
||||
destination: '/redir2',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/redir2',
|
||||
destination: '/hello',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/redir/:path',
|
||||
destination: '/:path',
|
||||
permanent: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/rewrite1',
|
||||
destination: '/rewrite2',
|
||||
},
|
||||
{
|
||||
source: '/rewrite2',
|
||||
destination: '/hello',
|
||||
},
|
||||
{
|
||||
source: '/rewrite/:first/:second',
|
||||
destination: '/rewrite-2/hello/:second',
|
||||
},
|
||||
{
|
||||
source: '/rewrite-2/:first/:second',
|
||||
destination: '/params',
|
||||
},
|
||||
{
|
||||
source: '/add-header',
|
||||
destination: '/hello',
|
||||
},
|
||||
{
|
||||
source: '/catchall-header/:path*',
|
||||
destination: '/hello',
|
||||
},
|
||||
];
|
||||
},
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: '/redir1',
|
||||
destination: '/redir2',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/redir2',
|
||||
destination: '/hello',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/redir/:path',
|
||||
destination: '/:path',
|
||||
permanent: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/rewrite1',
|
||||
destination: '/rewrite2',
|
||||
},
|
||||
{
|
||||
source: '/rewrite2',
|
||||
destination: '/hello',
|
||||
},
|
||||
{
|
||||
source: '/rewrite/:first/:second',
|
||||
destination: '/rewrite-2/hello/:second',
|
||||
},
|
||||
{
|
||||
source: '/rewrite-2/:first/:second',
|
||||
destination: '/params',
|
||||
},
|
||||
{
|
||||
source: '/add-header',
|
||||
destination: '/hello',
|
||||
},
|
||||
{
|
||||
source: '/catchall-header/:path*',
|
||||
destination: '/hello',
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/add-header',
|
||||
headers: [
|
||||
{
|
||||
key: 'x-hello',
|
||||
value: 'world',
|
||||
},
|
||||
{
|
||||
key: 'x-another',
|
||||
value: 'value',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/catchall-header/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'x-hello',
|
||||
value: 'world',
|
||||
},
|
||||
{
|
||||
key: 'x-another',
|
||||
value: 'value',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/add-header',
|
||||
headers: [
|
||||
{
|
||||
key: 'x-hello',
|
||||
value: 'world',
|
||||
},
|
||||
{
|
||||
key: 'x-another',
|
||||
value: 'value',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/catchall-header/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'x-hello',
|
||||
value: 'world',
|
||||
},
|
||||
{
|
||||
key: 'x-another',
|
||||
value: 'value',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"version": 2,
|
||||
"uploadNowJson": true,
|
||||
"builds": [{ "src": "package.json", "use": "@vercel/next" }],
|
||||
"probes": [
|
||||
{
|
||||
@@ -61,6 +62,12 @@
|
||||
"x-hello": "world",
|
||||
"x-another": "value"
|
||||
}
|
||||
},
|
||||
{
|
||||
"logMustNotContain": "WARNING: your application is being opted out of @vercel/next's optimized lambdas mode due to legacy routes"
|
||||
},
|
||||
{
|
||||
"logMustNotContain": "WARNING: Your application is being opted out of \"@vercel/next\" optimized lambdas mode due to `functions` config"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,14 +2,12 @@ module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
experimental: {
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/:path*',
|
||||
destination: '/params',
|
||||
},
|
||||
];
|
||||
},
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/:path*',
|
||||
destination: '/params',
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
{
|
||||
"version": 2,
|
||||
"uploadNowJson": true,
|
||||
"builds": [{ "src": "packages/web/next.config.js", "use": "@vercel/next" }],
|
||||
"routes": [{ "src": "/(.*)", "dest": "/packages/web/$1", "continue": true }],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/",
|
||||
"mustContain": "hello world <!-- -->6"
|
||||
},
|
||||
{
|
||||
"logMustContain": "WARNING: your application is being opted out of @vercel/next's optimized lambdas mode due to legacy routes"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,23 +3,21 @@ module.exports = {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
exportPathMap: d => d,
|
||||
experimental: {
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/first',
|
||||
destination: '/',
|
||||
},
|
||||
];
|
||||
},
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: '/second',
|
||||
destination: '/about',
|
||||
permanent: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/first',
|
||||
destination: '/',
|
||||
},
|
||||
];
|
||||
},
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: '/second',
|
||||
destination: '/about',
|
||||
permanent: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
@@ -35,6 +35,26 @@
|
||||
{
|
||||
"path": "/_next/__NEXT_SCRIPT__(/another)",
|
||||
"mustNotContain": "hello from another"
|
||||
},
|
||||
{
|
||||
"path": "/docs/api/my-slug",
|
||||
"mustContain": "index slug: my-slug"
|
||||
},
|
||||
{
|
||||
"path": "/docs/api/my-slug/another",
|
||||
"mustContain": "another slug: my-slug"
|
||||
},
|
||||
{
|
||||
"path": "/docs/api/hello",
|
||||
"mustContain": "hello from hello"
|
||||
},
|
||||
{
|
||||
"path": "/docs/blog/post-1",
|
||||
"mustContain": "index post: post-1"
|
||||
},
|
||||
{
|
||||
"path": "/docs/blog/post-1/comments",
|
||||
"mustContain": "comments post: post-1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
1
packages/now-next/test/fixtures/16-base-path/pages/api/[slug]/another.js
vendored
Normal file
1
packages/now-next/test/fixtures/16-base-path/pages/api/[slug]/another.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export default (req, res) => res.end(`another slug: ${req.query.slug}`)
|
||||
1
packages/now-next/test/fixtures/16-base-path/pages/api/[slug]/index.js
vendored
Normal file
1
packages/now-next/test/fixtures/16-base-path/pages/api/[slug]/index.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export default (req, res) => res.end(`index slug: ${req.query.slug}`)
|
||||
1
packages/now-next/test/fixtures/16-base-path/pages/api/hello.js
vendored
Normal file
1
packages/now-next/test/fixtures/16-base-path/pages/api/hello.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export default (req, res) => res.end('hello from hello');
|
||||
9
packages/now-next/test/fixtures/16-base-path/pages/blog/[post]/comments.js
vendored
Normal file
9
packages/now-next/test/fixtures/16-base-path/pages/blog/[post]/comments.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
export const getServerSideProps = ({ params }) => ({
|
||||
props: {
|
||||
post: params.post
|
||||
}
|
||||
})
|
||||
|
||||
export default function Comment({ post }) {
|
||||
return `comments post: ${post}`
|
||||
}
|
||||
9
packages/now-next/test/fixtures/16-base-path/pages/blog/[post]/index.js
vendored
Normal file
9
packages/now-next/test/fixtures/16-base-path/pages/blog/[post]/index.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
export const getServerSideProps = ({ params }) => ({
|
||||
props: {
|
||||
post: params.post
|
||||
}
|
||||
})
|
||||
|
||||
export default function Post({ post }) {
|
||||
return `index post: ${post}`
|
||||
}
|
||||
3
packages/now-next/test/fixtures/18-ssg-fallback-support/pages/api/noop.js
vendored
Normal file
3
packages/now-next/test/fixtures/18-ssg-fallback-support/pages/api/noop.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default (_, res) => {
|
||||
res.send('OK');
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "9.2.3-canary.13",
|
||||
"next": "canary",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getServerProps() {
|
||||
export async function getServerSideProps() {
|
||||
return {
|
||||
props: {
|
||||
world: 'world',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getServerProps() {
|
||||
export async function getServerSideProps() {
|
||||
return {
|
||||
props: {
|
||||
world: 'world',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getServerProps ({ params }) {
|
||||
export async function getServerSideProps({ params }) {
|
||||
return {
|
||||
props: {
|
||||
post: params.post,
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import React from 'react'
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getServerProps ({ params }) {
|
||||
export async function getServerSideProps({ params }) {
|
||||
if (params.post === 'post-10') {
|
||||
await new Promise(resolve => {
|
||||
setTimeout(() => resolve(), 1000)
|
||||
})
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(() => resolve(), 1000);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
post: params.post,
|
||||
time: (await import('perf_hooks')).performance.now()
|
||||
time: (await import('perf_hooks')).performance.now(),
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default ({ post, time }) => {
|
||||
@@ -22,5 +22,5 @@ export default ({ post, time }) => {
|
||||
<p>Post: {post}</p>
|
||||
<span>time: {time}</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function unstable_getServerProps() {
|
||||
export async function getServerSideProps() {
|
||||
return {
|
||||
props: {
|
||||
world: 'world',
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
/* eslint-env jest */
|
||||
const fetch = require('node-fetch');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = function(ctx) {
|
||||
it('should revalidate content properly from pathname', async () => {
|
||||
const res = await fetch(`${ctx.deploymentUrl}/another`);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
let $ = cheerio.load(await res.text());
|
||||
const initialTime = $('#time').text();
|
||||
const initialRandom = $('#random').text();
|
||||
expect($('#hello').text()).toBe('hello: world');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/another`);
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
$ = cheerio.load(await res2.text());
|
||||
expect($('#hello').text()).toBe('hello: world');
|
||||
expect(initialTime).not.toBe($('#time').text());
|
||||
expect(initialRandom).not.toBe($('#random').text());
|
||||
});
|
||||
|
||||
it('should revalidate content properly from dynamic pathname', async () => {
|
||||
const res = await fetch(`${ctx.deploymentUrl}/blog/post-123`);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
let $ = cheerio.load(await res.text());
|
||||
const initialTime = $('#time').text();
|
||||
const initialRandom = $('#random').text();
|
||||
expect($('#post').text()).toBe('Post: post-123');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/blog/post-123`);
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
$ = cheerio.load(await res2.text());
|
||||
expect($('#post').text()).toBe('Post: post-123');
|
||||
expect(initialTime).not.toBe($('#time').text());
|
||||
expect(initialRandom).not.toBe($('#random').text());
|
||||
});
|
||||
|
||||
it('should revalidate content properly from dynamic pathnames', async () => {
|
||||
const res = await fetch(`${ctx.deploymentUrl}/blog/post-123/comment-321`);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
let $ = cheerio.load(await res.text());
|
||||
const initialTime = $('#time').text();
|
||||
const initialRandom = $('#random').text();
|
||||
expect($('#post').text()).toBe('Post: post-123');
|
||||
expect($('#comment').text()).toBe('Comment: comment-321');
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const res2 = await fetch(`${ctx.deploymentUrl}/blog/post-123/comment-321`);
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
$ = cheerio.load(await res2.text());
|
||||
expect($('#post').text()).toBe('Post: post-123');
|
||||
expect($('#comment').text()).toBe('Comment: comment-321');
|
||||
expect(initialTime).not.toBe($('#time').text());
|
||||
expect(initialRandom).not.toBe($('#random').text());
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /_next/data pathname', async () => {
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/another.json`
|
||||
);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
const { pageProps: data } = await res.json();
|
||||
const initialTime = data.time;
|
||||
const initialRandom = data.random;
|
||||
expect(data.world).toBe('world');
|
||||
expect(isNaN(initialTime)).toBe(false);
|
||||
expect(isNaN(initialRandom)).toBe(false);
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const res2 = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/another.json`
|
||||
);
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
const { pageProps: data2 } = await res2.json();
|
||||
expect(data2.world).toBe('world');
|
||||
expect(isNaN(data2.time)).toBe(false);
|
||||
expect(isNaN(data2.random)).toBe(false);
|
||||
expect(initialTime).not.toBe(data2.time);
|
||||
expect(initialRandom).not.toBe(data2.random);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /_next/data dynamic pathname', async () => {
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/blog/post-123.json`
|
||||
);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
const { pageProps: data } = await res.json();
|
||||
const initialTime = data.time;
|
||||
const initialRandom = data.random;
|
||||
expect(data.post).toBe('post-123');
|
||||
expect(isNaN(initialTime)).toBe(false);
|
||||
expect(isNaN(initialRandom)).toBe(false);
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const res2 = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/blog/post-123.json`
|
||||
);
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
const { pageProps: data2 } = await res2.json();
|
||||
expect(data2.post).toBe('post-123');
|
||||
expect(isNaN(data2.time)).toBe(false);
|
||||
expect(isNaN(data2.random)).toBe(false);
|
||||
expect(initialTime).not.toBe(data2.time);
|
||||
expect(initialRandom).not.toBe(data2.random);
|
||||
});
|
||||
|
||||
it('should revalidate content properly from /_next/data dynamic pathnames', async () => {
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/blog/post-123/comment-321.json`
|
||||
);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
const { pageProps: data } = await res.json();
|
||||
const initialTime = data.time;
|
||||
const initialRandom = data.random;
|
||||
expect(data.post).toBe('post-123');
|
||||
expect(data.comment).toBe('comment-321');
|
||||
expect(isNaN(initialTime)).toBe(false);
|
||||
expect(isNaN(initialRandom)).toBe(false);
|
||||
|
||||
// wait for revalidation to occur
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const res2 = await fetch(
|
||||
`${ctx.deploymentUrl}/_next/data/testing-build-id/blog/post-123/comment-321.json`
|
||||
);
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
const { pageProps: data2 } = await res2.json();
|
||||
expect(data2.post).toBe('post-123');
|
||||
expect(data2.comment).toBe('comment-321');
|
||||
expect(isNaN(data2.time)).toBe(false);
|
||||
expect(isNaN(data2.random)).toBe(false);
|
||||
expect(initialTime).not.toBe(data2.time);
|
||||
expect(initialRandom).not.toBe(data2.random);
|
||||
});
|
||||
};
|
||||
@@ -1,146 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next",
|
||||
"config": {
|
||||
"sharedLambdas": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/lambda",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "MISS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/forever",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "PRERENDER"
|
||||
}
|
||||
},
|
||||
{ "delay": 2000 },
|
||||
{
|
||||
"path": "/forever",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "HIT"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/blog/post-3",
|
||||
"status": 200,
|
||||
"mustContain": "loading..."
|
||||
},
|
||||
{ "delay": 2000 },
|
||||
{
|
||||
"path": "/blog/post-3",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "/HIT|STALE/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/blog/post-4.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "MISS"
|
||||
}
|
||||
},
|
||||
{ "delay": 2000 },
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/blog/post-4.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "/HIT|STALE/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/blog/post-3",
|
||||
"status": 200,
|
||||
"mustContain": "post-3"
|
||||
},
|
||||
{
|
||||
"path": "/blog/post-3/comment-3",
|
||||
"status": 200,
|
||||
"mustContain": "loading..."
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/lambda.json",
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/forever.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "/PRERENDER|HIT/"
|
||||
}
|
||||
},
|
||||
{ "delay": 2000 },
|
||||
{
|
||||
"path": "/blog/post-3/comment-3",
|
||||
"status": 200,
|
||||
"mustContain": "comment-3"
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/forever.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "HIT"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/another2.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "PRERENDER"
|
||||
}
|
||||
},
|
||||
{ "delay": 2000 },
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/another2.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "HIT"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/nofallback/one",
|
||||
"status": 200,
|
||||
"mustContain": "one"
|
||||
},
|
||||
{
|
||||
"path": "/nofallback/two",
|
||||
"status": 200,
|
||||
"mustContain": "two"
|
||||
},
|
||||
{
|
||||
"path": "/nofallback/nope",
|
||||
"status": 404,
|
||||
"mustContain": "This page could not be found"
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/nofallback/one.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "/HIT|STALE|PRERENDER/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/nofallback/two.json",
|
||||
"status": 200,
|
||||
"responseHeaders": {
|
||||
"x-vercel-cache": "/HIT|STALE|PRERENDER/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/nofallback/nope.json",
|
||||
"status": 404
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function getStaticProps() {
|
||||
return {
|
||||
props: {
|
||||
world: 'world',
|
||||
random: Math.random(),
|
||||
time: new Date().getTime(),
|
||||
},
|
||||
unstable_revalidate: 1,
|
||||
};
|
||||
}
|
||||
|
||||
export default ({ world, time, random }) => {
|
||||
return (
|
||||
<>
|
||||
<p id="hello">hello: {world}</p>
|
||||
<span id="time">time: {time}</span>
|
||||
<span id="random">random: {random}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function getStaticProps() {
|
||||
return {
|
||||
props: {
|
||||
world: 'world',
|
||||
time: new Date().getTime(),
|
||||
},
|
||||
unstable_revalidate: 5,
|
||||
};
|
||||
}
|
||||
|
||||
export default ({ world, time }) => {
|
||||
return (
|
||||
<>
|
||||
<p>hello: {world}</p>
|
||||
<span>time: {time}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,40 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function getStaticPaths() {
|
||||
return {
|
||||
paths: [
|
||||
'/blog/post-1/comment-1',
|
||||
{ params: { post: 'post-2', comment: 'comment-2' } },
|
||||
'/blog/post-1337/comment-1337',
|
||||
'/blog/post-123/comment-321'
|
||||
],
|
||||
fallback: true,
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function getStaticProps({ params }) {
|
||||
return {
|
||||
props: {
|
||||
post: params.post,
|
||||
random: Math.random(),
|
||||
comment: params.comment,
|
||||
time: new Date().getTime(),
|
||||
},
|
||||
unstable_revalidate: 1,
|
||||
};
|
||||
}
|
||||
|
||||
export default ({ post, comment, time, random }) => {
|
||||
if (!post) return <p>loading...</p>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id='post'>Post: {post}</p>
|
||||
<p id='comment'>Comment: {comment}</p>
|
||||
<span id='time'>time: {time}</span>
|
||||
<span id='random'>random: {random}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,43 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function getStaticPaths() {
|
||||
return {
|
||||
paths: [
|
||||
'/blog/post-1',
|
||||
{ params: { post: 'post-2' } },
|
||||
'/blog/post-123',
|
||||
],
|
||||
fallback: true,
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function getStaticProps({ params }) {
|
||||
if (params.post === 'post-10') {
|
||||
await new Promise(resolve => {
|
||||
setTimeout(() => resolve(), 1000);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
post: params.post,
|
||||
random: Math.random(),
|
||||
time: (await import('perf_hooks')).performance.now(),
|
||||
},
|
||||
unstable_revalidate: 1,
|
||||
};
|
||||
}
|
||||
|
||||
export default ({ post, time, random }) => {
|
||||
if (!post) return <p>loading...</p>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<p id='post'>Post: {post}</p>
|
||||
<span id='time'>time: {time}</span>
|
||||
<span id='random'>random: {random}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function getStaticProps() {
|
||||
return {
|
||||
props: {
|
||||
world: 'world',
|
||||
time: new Date().getTime(),
|
||||
},
|
||||
unstable_revalidate: false,
|
||||
};
|
||||
}
|
||||
|
||||
export default ({ world, time }) => {
|
||||
return (
|
||||
<>
|
||||
<p>hello: {world}</p>
|
||||
<span>time: {time}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export default () => 'Hi';
|
||||
@@ -1,5 +0,0 @@
|
||||
const Page = ({ data }) => <p>{data} world</p>;
|
||||
|
||||
Page.getInitialProps = () => ({ data: 'hello' });
|
||||
|
||||
export default Page;
|
||||
@@ -1,31 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function getStaticPaths() {
|
||||
return {
|
||||
paths: ['/nofallback/one', { params: { slug: 'two' } }],
|
||||
fallback: false,
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export async function getStaticProps({ params }) {
|
||||
return {
|
||||
props: {
|
||||
slug: params.slug,
|
||||
time: (await import('perf_hooks')).performance.now(),
|
||||
},
|
||||
unstable_revalidate: 10,
|
||||
};
|
||||
}
|
||||
|
||||
export default ({ slug, time }) => {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
Slug ({slug.length}): {slug}
|
||||
</p>
|
||||
<span>time: {time}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"version": 2,
|
||||
"uploadNowJson": true,
|
||||
"builds": [{ "src": "package.json", "use": "@vercel/next" }],
|
||||
"probes": [
|
||||
{
|
||||
@@ -133,6 +134,12 @@
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/nofallback/nope.json",
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"logMustNotContain": "WARNING: your application is being opted out of @vercel/next's optimized lambdas mode due to legacy routes"
|
||||
},
|
||||
{
|
||||
"logMustNotContain": "WARNING: Your application is being opted out of \"@vercel/next\" optimized lambdas mode due to `functions` config"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
3
packages/now-next/test/fixtures/22-ssg-v2/pages/api/noop.js
vendored
Normal file
3
packages/now-next/test/fixtures/22-ssg-v2/pages/api/noop.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default (_, res) => {
|
||||
res.send('OK');
|
||||
};
|
||||
@@ -2,218 +2,216 @@ module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
experimental: {
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/to-another',
|
||||
destination: '/another/one',
|
||||
},
|
||||
{
|
||||
source: '/nav',
|
||||
destination: '/404',
|
||||
},
|
||||
{
|
||||
source: '/hello-world',
|
||||
destination: '/static/hello.txt',
|
||||
},
|
||||
{
|
||||
source: '/',
|
||||
destination: '/another',
|
||||
},
|
||||
{
|
||||
source: '/another',
|
||||
destination: '/multi-rewrites',
|
||||
},
|
||||
{
|
||||
source: '/first',
|
||||
destination: '/hello',
|
||||
},
|
||||
{
|
||||
source: '/second',
|
||||
destination: '/hello-again',
|
||||
},
|
||||
{
|
||||
source: '/to-hello',
|
||||
destination: '/hello',
|
||||
},
|
||||
{
|
||||
source: '/(.*)-:id(\\d+).html',
|
||||
destination: '/blog/:id',
|
||||
},
|
||||
{
|
||||
source: '/blog/post-1',
|
||||
destination: '/blog/post-2',
|
||||
},
|
||||
{
|
||||
source: '/test/:path',
|
||||
destination: '/:path',
|
||||
},
|
||||
{
|
||||
source: '/test-overwrite/:something/:another',
|
||||
destination: '/params/this-should-be-the-value',
|
||||
},
|
||||
{
|
||||
source: '/params/:something',
|
||||
destination: '/with-params',
|
||||
},
|
||||
{
|
||||
source: '/query-rewrite/:section/:name',
|
||||
destination: '/with-params?first=:section&second=:name',
|
||||
},
|
||||
{
|
||||
source: '/hidden/_next/:path*',
|
||||
destination: '/_next/:path*',
|
||||
},
|
||||
{
|
||||
source: '/api-hello',
|
||||
destination: '/api/hello',
|
||||
},
|
||||
{
|
||||
source: '/api-hello-regex/(.*)',
|
||||
destination: '/api/hello?name=:1',
|
||||
},
|
||||
{
|
||||
source: '/api-hello-param/:name',
|
||||
destination: '/api/hello?hello=:name',
|
||||
},
|
||||
{
|
||||
source: '/api-dynamic-param/:name',
|
||||
destination: '/api/dynamic/:name?hello=:name',
|
||||
},
|
||||
{
|
||||
source: '/:path/post-321',
|
||||
destination: '/with-params',
|
||||
},
|
||||
{
|
||||
source: '/a/catch-all/:path*',
|
||||
destination: '/a/catch-all',
|
||||
},
|
||||
];
|
||||
},
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: '/redirect/me/to-about/:lang',
|
||||
destination: '/:lang/about',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/docs/router-status/:code',
|
||||
destination: '/docs/v2/network/status-codes#:code',
|
||||
statusCode: 301,
|
||||
},
|
||||
{
|
||||
source: '/docs/github',
|
||||
destination: '/docs/v2/advanced/now-for-github',
|
||||
statusCode: 301,
|
||||
},
|
||||
{
|
||||
source: '/docs/v2/advanced/:all(.*)',
|
||||
destination: '/docs/v2/more/:all',
|
||||
statusCode: 301,
|
||||
},
|
||||
{
|
||||
source: '/hello/:id/another',
|
||||
destination: '/blog/:id',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/redirect1',
|
||||
destination: '/',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/redirect2',
|
||||
destination: '/',
|
||||
statusCode: 301,
|
||||
},
|
||||
{
|
||||
source: '/redirect3',
|
||||
destination: '/another',
|
||||
statusCode: 302,
|
||||
},
|
||||
{
|
||||
source: '/redirect4',
|
||||
destination: '/',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/redir-chain1',
|
||||
destination: '/redir-chain2',
|
||||
statusCode: 301,
|
||||
},
|
||||
{
|
||||
source: '/redir-chain2',
|
||||
destination: '/redir-chain3',
|
||||
statusCode: 302,
|
||||
},
|
||||
{
|
||||
source: '/redir-chain3',
|
||||
destination: '/',
|
||||
statusCode: 303,
|
||||
},
|
||||
{
|
||||
source: '/to-external',
|
||||
destination: 'https://google.com',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/query-redirect/:section/:name',
|
||||
destination: '/with-params?first=:section&second=:name',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/named-like-unnamed/:0',
|
||||
destination: '/:0',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/redirect-override',
|
||||
destination: '/thank-you-next',
|
||||
permanent: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: '/to-another',
|
||||
destination: '/another/one',
|
||||
},
|
||||
{
|
||||
source: '/nav',
|
||||
destination: '/404',
|
||||
},
|
||||
{
|
||||
source: '/hello-world',
|
||||
destination: '/static/hello.txt',
|
||||
},
|
||||
{
|
||||
source: '/',
|
||||
destination: '/another',
|
||||
},
|
||||
{
|
||||
source: '/another',
|
||||
destination: '/multi-rewrites',
|
||||
},
|
||||
{
|
||||
source: '/first',
|
||||
destination: '/hello',
|
||||
},
|
||||
{
|
||||
source: '/second',
|
||||
destination: '/hello-again',
|
||||
},
|
||||
{
|
||||
source: '/to-hello',
|
||||
destination: '/hello',
|
||||
},
|
||||
{
|
||||
source: '/(.*)-:id(\\d+).html',
|
||||
destination: '/blog/:id',
|
||||
},
|
||||
{
|
||||
source: '/blog/post-1',
|
||||
destination: '/blog/post-2',
|
||||
},
|
||||
{
|
||||
source: '/test/:path',
|
||||
destination: '/:path',
|
||||
},
|
||||
{
|
||||
source: '/test-overwrite/:something/:another',
|
||||
destination: '/params/this-should-be-the-value',
|
||||
},
|
||||
{
|
||||
source: '/params/:something',
|
||||
destination: '/with-params',
|
||||
},
|
||||
{
|
||||
source: '/query-rewrite/:section/:name',
|
||||
destination: '/with-params?first=:section&second=:name',
|
||||
},
|
||||
{
|
||||
source: '/hidden/_next/:path*',
|
||||
destination: '/_next/:path*',
|
||||
},
|
||||
{
|
||||
source: '/api-hello',
|
||||
destination: '/api/hello',
|
||||
},
|
||||
{
|
||||
source: '/api-hello-regex/(.*)',
|
||||
destination: '/api/hello?name=:1',
|
||||
},
|
||||
{
|
||||
source: '/api-hello-param/:name',
|
||||
destination: '/api/hello?hello=:name',
|
||||
},
|
||||
{
|
||||
source: '/api-dynamic-param/:name',
|
||||
destination: '/api/dynamic/:name?hello=:name',
|
||||
},
|
||||
{
|
||||
source: '/:path/post-321',
|
||||
destination: '/with-params',
|
||||
},
|
||||
{
|
||||
source: '/a/catch-all/:path*',
|
||||
destination: '/a/catch-all',
|
||||
},
|
||||
];
|
||||
},
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: '/redirect/me/to-about/:lang',
|
||||
destination: '/:lang/about',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/docs/router-status/:code',
|
||||
destination: '/docs/v2/network/status-codes#:code',
|
||||
statusCode: 301,
|
||||
},
|
||||
{
|
||||
source: '/docs/github',
|
||||
destination: '/docs/v2/advanced/now-for-github',
|
||||
statusCode: 301,
|
||||
},
|
||||
{
|
||||
source: '/docs/v2/advanced/:all(.*)',
|
||||
destination: '/docs/v2/more/:all',
|
||||
statusCode: 301,
|
||||
},
|
||||
{
|
||||
source: '/hello/:id/another',
|
||||
destination: '/blog/:id',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/redirect1',
|
||||
destination: '/',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/redirect2',
|
||||
destination: '/',
|
||||
statusCode: 301,
|
||||
},
|
||||
{
|
||||
source: '/redirect3',
|
||||
destination: '/another',
|
||||
statusCode: 302,
|
||||
},
|
||||
{
|
||||
source: '/redirect4',
|
||||
destination: '/',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/redir-chain1',
|
||||
destination: '/redir-chain2',
|
||||
statusCode: 301,
|
||||
},
|
||||
{
|
||||
source: '/redir-chain2',
|
||||
destination: '/redir-chain3',
|
||||
statusCode: 302,
|
||||
},
|
||||
{
|
||||
source: '/redir-chain3',
|
||||
destination: '/',
|
||||
statusCode: 303,
|
||||
},
|
||||
{
|
||||
source: '/to-external',
|
||||
destination: 'https://google.com',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/query-redirect/:section/:name',
|
||||
destination: '/with-params?first=:section&second=:name',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/named-like-unnamed/:0',
|
||||
destination: '/:0',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/redirect-override',
|
||||
destination: '/thank-you-next',
|
||||
permanent: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/add-header',
|
||||
headers: [
|
||||
{
|
||||
key: 'x-custom-header',
|
||||
value: 'hello world',
|
||||
},
|
||||
{
|
||||
key: 'x-another-header',
|
||||
value: 'hello again',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/my-headers/(.*)',
|
||||
headers: [
|
||||
{
|
||||
key: 'x-first-header',
|
||||
value: 'first',
|
||||
},
|
||||
{
|
||||
key: 'x-second-header',
|
||||
value: 'second',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'x-something',
|
||||
value: 'applied-everywhere',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/add-header',
|
||||
headers: [
|
||||
{
|
||||
key: 'x-custom-header',
|
||||
value: 'hello world',
|
||||
},
|
||||
{
|
||||
key: 'x-another-header',
|
||||
value: 'hello again',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/my-headers/(.*)',
|
||||
headers: [
|
||||
{
|
||||
key: 'x-first-header',
|
||||
value: 'first',
|
||||
},
|
||||
{
|
||||
key: 'x-second-header',
|
||||
value: 'second',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
source: '/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'x-something',
|
||||
value: 'applied-everywhere',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "package.json", "use": "@vercel/next" }],
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next"
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
// should handle one-to-one rewrite successfully
|
||||
{
|
||||
"path": "/first",
|
||||
"mustContain": "hello"
|
||||
},
|
||||
|
||||
// should handle chained rewrites successfully
|
||||
{
|
||||
"path": "/",
|
||||
"mustContain": "multi-rewrites"
|
||||
},
|
||||
|
||||
// should not match dynamic route immediately after applying header
|
||||
{
|
||||
"path": "/blog/post-321",
|
||||
@@ -23,7 +26,6 @@
|
||||
"path": "/blog/post-321",
|
||||
"mustNotContain": "post-321"
|
||||
},
|
||||
|
||||
// should handle chained redirects successfully
|
||||
{
|
||||
"path": "/redir-chain1",
|
||||
@@ -55,7 +57,6 @@
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should redirect successfully with permanent: false
|
||||
{
|
||||
"path": "/redirect1",
|
||||
@@ -67,7 +68,6 @@
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should redirect with params successfully
|
||||
{
|
||||
"path": "/hello/123/another",
|
||||
@@ -79,7 +79,6 @@
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should redirect with hash successfully
|
||||
{
|
||||
"path": "/docs/router-status/500",
|
||||
@@ -91,7 +90,6 @@
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should redirect successfully with provided statusCode
|
||||
{
|
||||
"path": "/redirect2",
|
||||
@@ -103,25 +101,21 @@
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should server static files through a rewrite
|
||||
{
|
||||
"path": "/hello-world",
|
||||
"mustContain": "hello world!"
|
||||
},
|
||||
|
||||
// should rewrite with params successfully
|
||||
{
|
||||
"path": "/test/hello",
|
||||
"mustContain": "Hello"
|
||||
},
|
||||
|
||||
// should double redirect successfully
|
||||
{
|
||||
"path": "/docs/github",
|
||||
"mustContain": "hi there"
|
||||
},
|
||||
|
||||
// should allow params in query for rewrite
|
||||
{
|
||||
"path": "/query-rewrite/hello/world?a=b",
|
||||
@@ -143,13 +137,11 @@
|
||||
"path": "/query-rewrite/hello/world?a=b",
|
||||
"mustContain": "\"second\":\"world\""
|
||||
},
|
||||
|
||||
// should not allow rewrite to override page file
|
||||
{
|
||||
"path": "/nav",
|
||||
"mustContain": "to-hello"
|
||||
},
|
||||
|
||||
// show allow redirect to override the page
|
||||
{
|
||||
"path": "/redirect-override",
|
||||
@@ -161,31 +153,26 @@
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should match a page after a rewrite
|
||||
{
|
||||
"path": "/to-hello",
|
||||
"mustContain": "Hello"
|
||||
},
|
||||
|
||||
// should match dynamic route after rewrite
|
||||
{
|
||||
"path": "/blog/post-1",
|
||||
"mustContain": "post-2"
|
||||
},
|
||||
|
||||
// should match public file after rewrite
|
||||
{
|
||||
"path": "/blog/data.json",
|
||||
"mustContain": "\"hello\": \"world\""
|
||||
},
|
||||
|
||||
// should match /_next file after rewrite
|
||||
{
|
||||
"path": "/hidden/_next/__NEXT_SCRIPT__(/hello)",
|
||||
"mustContain": "createElement"
|
||||
},
|
||||
|
||||
// should allow redirecting to external resource
|
||||
{
|
||||
"path": "/to-external",
|
||||
@@ -197,7 +184,6 @@
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should apply headers for exact match
|
||||
{
|
||||
"path": "/add-header",
|
||||
@@ -206,7 +192,6 @@
|
||||
"x-another-header": "hello again"
|
||||
}
|
||||
},
|
||||
|
||||
// should apply headers for multi match
|
||||
{
|
||||
"path": "/my-headers/first",
|
||||
@@ -215,19 +200,16 @@
|
||||
"x-second-header": "second"
|
||||
}
|
||||
},
|
||||
|
||||
// should handle basic api rewrite successfully
|
||||
{
|
||||
"path": "/api-hello",
|
||||
"mustContain": "{\"query\":{}}"
|
||||
},
|
||||
|
||||
// should handle api rewrite with param successfully
|
||||
{
|
||||
"path": "/api-hello-param/hello",
|
||||
"mustContain": "{\"query\":{\"hello\":\"hello\",\"name\":\"hello\"}}"
|
||||
},
|
||||
|
||||
// should handle encoded value in the pathname correctly
|
||||
{
|
||||
"path": "/redirect/me/to-about/%5Cgoogle.com",
|
||||
@@ -239,21 +221,18 @@
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
|
||||
// should apply un-named multi-match correctly
|
||||
{
|
||||
"path": "/hello/post-123.html",
|
||||
"status": 200,
|
||||
"mustContain": "123"
|
||||
},
|
||||
|
||||
// should rewrite to catch-all with dash in segment name
|
||||
{
|
||||
"path": "/catchall-dash/hello/world",
|
||||
"status": 200,
|
||||
"mustContain": "hello/world"
|
||||
},
|
||||
|
||||
// should rewrite and normalize catch-all rewrite param
|
||||
{
|
||||
"path": "/a/catch-all/hello/world",
|
||||
|
||||
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>;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"version": 2,
|
||||
"uploadNowJson": true,
|
||||
"routes": [
|
||||
{ "src": "/(.*)", "dest": "/packages/webapp/$1", "continue": true }
|
||||
],
|
||||
@@ -31,6 +32,9 @@
|
||||
{
|
||||
"path": "/non-existent",
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"logMustContain": "WARNING: your application is being opted out of @vercel/next's optimized lambdas mode due to legacy routes"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"version": 2,
|
||||
"uploadNowJson": true,
|
||||
"routes": [
|
||||
{ "src": "/(.*)", "dest": "/packages/webapp/$1", "continue": true }
|
||||
],
|
||||
@@ -25,6 +26,9 @@
|
||||
{
|
||||
"path": "/non-existent",
|
||||
"status": 404
|
||||
},
|
||||
{
|
||||
"logMustContain": "WARNING: your application is being opted out of @vercel/next's optimized lambdas mode due to legacy routes"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
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';
|
||||
},
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user