mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-29 19:00:09 +00:00
Compare commits
22 Commits
@vercel/no
...
@vercel/ne
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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",
|
||||
|
||||
@@ -1,4 +1,31 @@
|
||||
[
|
||||
{
|
||||
"name": "Blitz.js",
|
||||
"slug": "blitzjs",
|
||||
"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-canary.1",
|
||||
"main": "frameworks.json",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "2.4.1-canary.1",
|
||||
"version": "2.4.1-canary.2",
|
||||
"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 };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "19.1.2-canary.14",
|
||||
"version": "19.1.2-canary.19",
|
||||
"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/build-utils": "2.4.1-canary.2",
|
||||
"@vercel/go": "1.1.3-canary.1",
|
||||
"@vercel/next": "2.6.8-canary.4",
|
||||
"@vercel/next": "2.6.9",
|
||||
"@vercel/node": "1.7.2-canary.1",
|
||||
"@vercel/python": "1.2.2",
|
||||
"@vercel/ruby": "1.2.3-canary.0",
|
||||
"@vercel/static-build": "0.17.4-canary.1"
|
||||
"@vercel/static-build": "0.17.5-canary.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sentry/node": "5.5.0",
|
||||
|
||||
@@ -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(
|
||||
@@ -29,7 +29,7 @@ export function resolveRouteParameters(
|
||||
export function getRoutesTypes(routes: Route[] = []) {
|
||||
const handleMap = new Map<HandleValue | null, Route[]>();
|
||||
let prevHandle: HandleValue | null = null;
|
||||
routes.forEach(route => {
|
||||
routes.forEach((route) => {
|
||||
if (isHandler(route)) {
|
||||
prevHandle = route.handle;
|
||||
} else {
|
||||
@@ -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;
|
||||
@@ -172,10 +176,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 +215,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
|
||||
@@ -231,10 +233,9 @@ 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),
|
||||
this.getLocalEnv('.env.build', nowConfig.build?.env),
|
||||
]);
|
||||
const allEnv = { ...buildEnv, ...runEnv };
|
||||
this.envConfigs = { buildEnv, runEnv, allEnv };
|
||||
@@ -283,18 +284,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 +380,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.');
|
||||
@@ -522,40 +524,17 @@ export default class DevServer {
|
||||
return this.cachedNowConfig;
|
||||
}
|
||||
|
||||
const pkg = await this.getPackageJson();
|
||||
const configPath = getNowConfigPath(this.cwd);
|
||||
|
||||
// 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 +551,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 +579,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 +589,8 @@ export default class DevServer {
|
||||
|
||||
config.builds = config.builds || [];
|
||||
config.builds.push(...builders);
|
||||
|
||||
delete config.functions;
|
||||
}
|
||||
|
||||
let routes: Route[] = [];
|
||||
@@ -645,27 +631,35 @@ export default class DevServer {
|
||||
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(
|
||||
@@ -763,10 +757,9 @@ export default class DevServer {
|
||||
|
||||
// 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),
|
||||
this.getLocalEnv('.env.build', nowConfig.build?.env),
|
||||
]);
|
||||
const allEnv = { ...buildEnv, ...runEnv };
|
||||
this.envConfigs = { buildEnv, runEnv, allEnv };
|
||||
@@ -797,11 +790,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 +1135,7 @@ export default class DevServer {
|
||||
match: BuildMatch,
|
||||
requestPath: string | null,
|
||||
req: http.IncomingMessage | null,
|
||||
nowConfig: NowConfig,
|
||||
previousBuildResult?: BuildResult,
|
||||
filesChanged?: string[],
|
||||
filesRemoved?: string[]
|
||||
@@ -1157,10 +1151,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 +1166,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 +1249,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 +1339,7 @@ export default class DevServer {
|
||||
req.method,
|
||||
phaseRoutes,
|
||||
this,
|
||||
nowConfig,
|
||||
prevHeaders,
|
||||
missRoutes,
|
||||
phase
|
||||
@@ -1369,7 +1367,8 @@ export default class DevServer {
|
||||
this.buildMatches,
|
||||
this.files,
|
||||
routeResult.dest,
|
||||
this
|
||||
this,
|
||||
nowConfig
|
||||
);
|
||||
|
||||
if (
|
||||
@@ -1392,6 +1391,7 @@ export default class DevServer {
|
||||
req.method,
|
||||
missRoutes,
|
||||
this,
|
||||
nowConfig,
|
||||
routeResult.headers,
|
||||
[],
|
||||
'miss'
|
||||
@@ -1401,7 +1401,8 @@ export default class DevServer {
|
||||
this.buildMatches,
|
||||
this.files,
|
||||
routeResult.dest,
|
||||
this
|
||||
this,
|
||||
nowConfig
|
||||
);
|
||||
if (
|
||||
await this.exitWithStatus(
|
||||
@@ -1424,6 +1425,7 @@ export default class DevServer {
|
||||
req.method,
|
||||
hitRoutes,
|
||||
this,
|
||||
nowConfig,
|
||||
routeResult.headers,
|
||||
[],
|
||||
'hit'
|
||||
@@ -1438,6 +1440,7 @@ export default class DevServer {
|
||||
req.method,
|
||||
errorRoutes,
|
||||
this,
|
||||
nowConfig,
|
||||
routeResult.headers,
|
||||
[],
|
||||
'error'
|
||||
@@ -1447,7 +1450,8 @@ export default class DevServer {
|
||||
this.buildMatches,
|
||||
this.files,
|
||||
routeResultForError.dest,
|
||||
this
|
||||
this,
|
||||
nowConfig
|
||||
);
|
||||
|
||||
if (matchForError) {
|
||||
@@ -1535,7 +1539,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 +1636,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 +1668,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 +1789,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 +1810,7 @@ export default class DevServer {
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map(p => {
|
||||
.map((p) => {
|
||||
let base = basename(p);
|
||||
let ext = '';
|
||||
let type = 'file';
|
||||
@@ -1856,8 +1862,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 +2053,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 +2091,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 +2142,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 +2155,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 +2165,8 @@ async function findMatchingRoute(
|
||||
reqUrl,
|
||||
undefined,
|
||||
buildResult.routes,
|
||||
devServer
|
||||
devServer,
|
||||
nowConfig
|
||||
);
|
||||
if (route.found) {
|
||||
return route;
|
||||
@@ -2195,7 +2222,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 +2252,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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -17,8 +17,8 @@ const isCanary = () => getDistTag(cliVersion) === 'canary';
|
||||
let port = 3000;
|
||||
|
||||
const binaryPath = resolve(__dirname, `../../scripts/start.js`);
|
||||
const fixture = name => join('test', 'dev', 'fixtures', name);
|
||||
const fixtureAbsolute = name => join(__dirname, 'fixtures', name);
|
||||
const fixture = (name) => join('test', 'dev', 'fixtures', name);
|
||||
const fixtureAbsolute = (name) => join(__dirname, 'fixtures', name);
|
||||
|
||||
let processCounter = 0;
|
||||
const processList = new Map();
|
||||
@@ -57,7 +57,7 @@ function fetchWithRetry(url, retries = 3, opts = {}) {
|
||||
|
||||
function createResolver() {
|
||||
let resolver;
|
||||
const p = new Promise(res => (resolver = res));
|
||||
const p = new Promise((res) => (resolver = res));
|
||||
p.resolve = resolver;
|
||||
return p;
|
||||
}
|
||||
@@ -75,7 +75,7 @@ function printOutput(fixture, stdout, stderr) {
|
||||
stderr
|
||||
).split('\n');
|
||||
|
||||
const getPrefix = nr => {
|
||||
const getPrefix = (nr) => {
|
||||
return nr === 0 ? '╭' : nr === lines.length - 1 ? '╰' : '│';
|
||||
};
|
||||
|
||||
@@ -196,10 +196,10 @@ async function testFixture(directory, opts = {}, args = []) {
|
||||
dev.stdout.setEncoding('utf8');
|
||||
dev.stderr.setEncoding('utf8');
|
||||
|
||||
dev.stdout.on('data', data => {
|
||||
dev.stdout.on('data', (data) => {
|
||||
stdout += data;
|
||||
});
|
||||
dev.stderr.on('data', data => {
|
||||
dev.stderr.on('data', (data) => {
|
||||
stderr += data;
|
||||
|
||||
if (stderr.includes('Ready! Available at')) {
|
||||
@@ -243,7 +243,7 @@ function testFixtureStdio(
|
||||
fn,
|
||||
{ expectedCode = 0, skipDeploy } = {}
|
||||
) {
|
||||
return async t => {
|
||||
return async (t) => {
|
||||
const cwd = fixtureAbsolute(directory);
|
||||
const token = await fetchTokenWithRetry();
|
||||
let deploymentUrl;
|
||||
@@ -298,11 +298,11 @@ function testFixtureStdio(
|
||||
dev.stdout.pipe(process.stdout);
|
||||
dev.stderr.pipe(process.stderr);
|
||||
|
||||
dev.stdout.on('data', data => {
|
||||
dev.stdout.on('data', (data) => {
|
||||
stdout += data;
|
||||
});
|
||||
|
||||
dev.stderr.on('data', data => {
|
||||
dev.stderr.on('data', (data) => {
|
||||
stderr += data;
|
||||
|
||||
if (stderr.includes('Ready! Available at')) {
|
||||
@@ -380,7 +380,7 @@ test.afterEach(async () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('[vercel dev] prints `npm install` errors', async t => {
|
||||
test('[vercel dev] prints `npm install` errors', async (t) => {
|
||||
const dir = fixture('runtime-not-installed');
|
||||
const result = await exec(dir);
|
||||
t.truthy(result.stderr.includes('npm ERR! 404'));
|
||||
@@ -392,7 +392,7 @@ test('[vercel dev] prints `npm install` errors', async t => {
|
||||
);
|
||||
});
|
||||
|
||||
test('[vercel dev] `vercel.json` should be invalidated if deleted', async t => {
|
||||
test('[vercel dev] `vercel.json` should be invalidated if deleted', async (t) => {
|
||||
const dir = fixture('invalidate-vercel-config');
|
||||
const configPath = join(dir, 'vercel.json');
|
||||
const originalConfig = await fs.readJSON(configPath);
|
||||
@@ -423,7 +423,7 @@ test('[vercel dev] `vercel.json` should be invalidated if deleted', async t => {
|
||||
}
|
||||
});
|
||||
|
||||
test('[vercel dev] reflects changes to config and env without restart', async t => {
|
||||
test('[vercel dev] reflects changes to config and env without restart', async (t) => {
|
||||
const dir = fixture('node-helpers');
|
||||
const configPath = join(dir, 'vercel.json');
|
||||
const originalConfig = await fs.readJSON(configPath);
|
||||
@@ -527,7 +527,7 @@ test('[vercel dev] reflects changes to config and env without restart', async t
|
||||
}
|
||||
});
|
||||
|
||||
test('[vercel dev] `@vercel/node` TypeScript should be resolved by default', async t => {
|
||||
test('[vercel dev] `@vercel/node` TypeScript should be resolved by default', async (t) => {
|
||||
// The purpose of this test is to test that `@vercel/node` can properly
|
||||
// resolve the default "typescript" module when the project doesn't include
|
||||
// its own version. To properly test for this, a fixture needs to be created
|
||||
@@ -558,14 +558,14 @@ test('[vercel dev] `@vercel/node` TypeScript should be resolved by default', asy
|
||||
|
||||
test(
|
||||
'[vercel dev] validate routes that use `check: true`',
|
||||
testFixtureStdio('routes-check-true', async testPath => {
|
||||
testFixtureStdio('routes-check-true', async (testPath) => {
|
||||
await testPath(200, '/blog/post', 'Blog Home');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] validate routes that use `check: true` and `status` code',
|
||||
testFixtureStdio('routes-check-true-status', async testPath => {
|
||||
testFixtureStdio('routes-check-true-status', async (testPath) => {
|
||||
await testPath(403, '/secret');
|
||||
await testPath(200, '/post', 'This is a post.');
|
||||
await testPath(200, '/post.html', 'This is a post.');
|
||||
@@ -574,7 +574,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] validate routes that use custom 404 page',
|
||||
testFixtureStdio('routes-custom-404', async testPath => {
|
||||
testFixtureStdio('routes-custom-404', async (testPath) => {
|
||||
await testPath(200, '/', 'Home Page');
|
||||
await testPath(404, '/nothing', 'Custom User 404');
|
||||
await testPath(404, '/exact', 'Exact Custom 404');
|
||||
@@ -585,7 +585,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] handles miss after route',
|
||||
testFixtureStdio('handle-miss-after-route', async testPath => {
|
||||
testFixtureStdio('handle-miss-after-route', async (testPath) => {
|
||||
await testPath(200, '/post', 'Blog Post Page', {
|
||||
test: '1',
|
||||
override: 'one',
|
||||
@@ -595,7 +595,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] handles miss after rewrite',
|
||||
testFixtureStdio('handle-miss-after-rewrite', async testPath => {
|
||||
testFixtureStdio('handle-miss-after-rewrite', async (testPath) => {
|
||||
await testPath(200, '/post', 'Blog Post Page', {
|
||||
test: '1',
|
||||
override: 'one',
|
||||
@@ -621,7 +621,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] does not display directory listing after 404',
|
||||
testFixtureStdio('handle-miss-hide-dir-list', async testPath => {
|
||||
testFixtureStdio('handle-miss-hide-dir-list', async (testPath) => {
|
||||
await testPath(404, '/post');
|
||||
await testPath(200, '/post/one.html', 'First Post');
|
||||
})
|
||||
@@ -629,7 +629,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] should preserve query string even after miss phase',
|
||||
testFixtureStdio('handle-miss-querystring', async testPath => {
|
||||
testFixtureStdio('handle-miss-querystring', async (testPath) => {
|
||||
await testPath(200, '/', 'Index Page');
|
||||
if (process.env.CI && process.platform === 'darwin') {
|
||||
console.log('Skipping since GH Actions hangs for some reason');
|
||||
@@ -642,28 +642,28 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] handles hit after handle: filesystem',
|
||||
testFixtureStdio('handle-hit-after-fs', async testPath => {
|
||||
testFixtureStdio('handle-hit-after-fs', async (testPath) => {
|
||||
await testPath(200, '/blog.html', 'Blog Page', { test: '1' });
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] handles hit after dest',
|
||||
testFixtureStdio('handle-hit-after-dest', async testPath => {
|
||||
testFixtureStdio('handle-hit-after-dest', async (testPath) => {
|
||||
await testPath(200, '/post', 'Blog Post', { test: '1', override: 'one' });
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] handles hit after rewrite',
|
||||
testFixtureStdio('handle-hit-after-rewrite', async testPath => {
|
||||
testFixtureStdio('handle-hit-after-rewrite', async (testPath) => {
|
||||
await testPath(200, '/post', 'Blog Post', { test: '1', override: 'one' });
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] should serve the public directory and api functions',
|
||||
testFixtureStdio('public-and-api', async testPath => {
|
||||
testFixtureStdio('public-and-api', async (testPath) => {
|
||||
await testPath(200, '/', 'This is the home page');
|
||||
await testPath(200, '/about.html', 'This is the about page');
|
||||
await testPath(200, '/.well-known/humans.txt', 'We come in peace');
|
||||
@@ -677,7 +677,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] should allow user rewrites for path segment files',
|
||||
testFixtureStdio('test-zero-config-rewrite', async testPath => {
|
||||
testFixtureStdio('test-zero-config-rewrite', async (testPath) => {
|
||||
await testPath(404, '/');
|
||||
await testPath(200, '/echo/1', '{"id":"1"}', {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
@@ -688,7 +688,7 @@ test(
|
||||
})
|
||||
);
|
||||
|
||||
test('[vercel dev] validate builds', async t => {
|
||||
test('[vercel dev] validate builds', async (t) => {
|
||||
const directory = fixture('invalid-builds');
|
||||
const output = await exec(directory);
|
||||
|
||||
@@ -699,7 +699,7 @@ test('[vercel dev] validate builds', async t => {
|
||||
);
|
||||
});
|
||||
|
||||
test('[vercel dev] validate routes', async t => {
|
||||
test('[vercel dev] validate routes', async (t) => {
|
||||
const directory = fixture('invalid-routes');
|
||||
const output = await exec(directory);
|
||||
|
||||
@@ -710,7 +710,7 @@ test('[vercel dev] validate routes', async t => {
|
||||
);
|
||||
});
|
||||
|
||||
test('[vercel dev] validate cleanUrls', async t => {
|
||||
test('[vercel dev] validate cleanUrls', async (t) => {
|
||||
const directory = fixture('invalid-clean-urls');
|
||||
const output = await exec(directory);
|
||||
|
||||
@@ -721,7 +721,7 @@ test('[vercel dev] validate cleanUrls', async t => {
|
||||
);
|
||||
});
|
||||
|
||||
test('[vercel dev] validate trailingSlash', async t => {
|
||||
test('[vercel dev] validate trailingSlash', async (t) => {
|
||||
const directory = fixture('invalid-trailing-slash');
|
||||
const output = await exec(directory);
|
||||
|
||||
@@ -732,7 +732,7 @@ test('[vercel dev] validate trailingSlash', async t => {
|
||||
);
|
||||
});
|
||||
|
||||
test('[vercel dev] validate rewrites', async t => {
|
||||
test('[vercel dev] validate rewrites', async (t) => {
|
||||
const directory = fixture('invalid-rewrites');
|
||||
const output = await exec(directory);
|
||||
|
||||
@@ -743,7 +743,7 @@ test('[vercel dev] validate rewrites', async t => {
|
||||
);
|
||||
});
|
||||
|
||||
test('[vercel dev] validate redirects', async t => {
|
||||
test('[vercel dev] validate redirects', async (t) => {
|
||||
const directory = fixture('invalid-redirects');
|
||||
const output = await exec(directory);
|
||||
|
||||
@@ -754,7 +754,7 @@ test('[vercel dev] validate redirects', async t => {
|
||||
);
|
||||
});
|
||||
|
||||
test('[vercel dev] validate headers', async t => {
|
||||
test('[vercel dev] validate headers', async (t) => {
|
||||
const directory = fixture('invalid-headers');
|
||||
const output = await exec(directory);
|
||||
|
||||
@@ -765,7 +765,7 @@ test('[vercel dev] validate headers', async t => {
|
||||
);
|
||||
});
|
||||
|
||||
test('[vercel dev] validate mixed routes and rewrites', async t => {
|
||||
test('[vercel dev] validate mixed routes and rewrites', async (t) => {
|
||||
const directory = fixture('invalid-mixed-routes-rewrites');
|
||||
const output = await exec(directory);
|
||||
|
||||
@@ -778,7 +778,7 @@ test('[vercel dev] validate mixed routes and rewrites', async t => {
|
||||
});
|
||||
|
||||
// Test seems unstable: It won't return sometimes.
|
||||
test('[vercel dev] validate env var names', async t => {
|
||||
test('[vercel dev] validate env var names', async (t) => {
|
||||
const directory = fixture('invalid-env-var-name');
|
||||
const { dev } = await testFixture(directory, { stdio: 'pipe' });
|
||||
|
||||
@@ -787,7 +787,7 @@ test('[vercel dev] validate env var names', async t => {
|
||||
dev.stderr.setEncoding('utf8');
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
dev.stderr.on('data', b => {
|
||||
dev.stderr.on('data', (b) => {
|
||||
stderr += b.toString();
|
||||
|
||||
if (
|
||||
@@ -817,7 +817,7 @@ test('[vercel dev] validate env var names', async t => {
|
||||
|
||||
test(
|
||||
'[vercel dev] test rewrites with segments serve correct content',
|
||||
testFixtureStdio('test-rewrites-with-segments', async testPath => {
|
||||
testFixtureStdio('test-rewrites-with-segments', async (testPath) => {
|
||||
await testPath(200, '/api/users/first', 'first');
|
||||
await testPath(200, '/api/fourty-two', '42');
|
||||
await testPath(200, '/rand', '42');
|
||||
@@ -828,14 +828,14 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] test rewrites serve correct content',
|
||||
testFixtureStdio('test-rewrites', async testPath => {
|
||||
testFixtureStdio('test-rewrites', async (testPath) => {
|
||||
await testPath(200, '/hello', 'Hello World');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] test rewrites and redirects is case sensitive',
|
||||
testFixtureStdio('test-routing-case-sensitive', async testPath => {
|
||||
testFixtureStdio('test-routing-case-sensitive', async (testPath) => {
|
||||
await testPath(200, '/Path', 'UPPERCASE');
|
||||
await testPath(200, '/path', 'lowercase');
|
||||
await testPath(308, '/GoTo', 'Redirecting to /upper.html (308)', {
|
||||
@@ -849,7 +849,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] test cleanUrls serve correct content',
|
||||
testFixtureStdio('test-clean-urls', async testPath => {
|
||||
testFixtureStdio('test-clean-urls', async (testPath) => {
|
||||
await testPath(200, '/', 'Index Page');
|
||||
await testPath(200, '/about', 'About Page');
|
||||
await testPath(200, '/sub', 'Sub Index Page');
|
||||
@@ -875,7 +875,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] should serve custom 404 when `cleanUrls: true`',
|
||||
testFixtureStdio('test-clean-urls-custom-404', async testPath => {
|
||||
testFixtureStdio('test-clean-urls-custom-404', async (testPath) => {
|
||||
await testPath(200, '/', 'This is the home page');
|
||||
await testPath(200, '/about', 'The about page');
|
||||
await testPath(200, '/contact/me', 'Contact Me Subdirectory');
|
||||
@@ -886,7 +886,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] test cleanUrls and trailingSlash serve correct content',
|
||||
testFixtureStdio('test-clean-urls-trailing-slash', async testPath => {
|
||||
testFixtureStdio('test-clean-urls-trailing-slash', async (testPath) => {
|
||||
await testPath(200, '/', 'Index Page');
|
||||
await testPath(200, '/about/', 'About Page');
|
||||
await testPath(200, '/sub/', 'Sub Index Page');
|
||||
@@ -913,7 +913,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] test cors headers work with OPTIONS',
|
||||
testFixtureStdio('test-cors-routes', async testPath => {
|
||||
testFixtureStdio('test-cors-routes', async (testPath) => {
|
||||
const headers = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers':
|
||||
@@ -932,7 +932,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] test trailingSlash true serve correct content',
|
||||
testFixtureStdio('test-trailing-slash', async testPath => {
|
||||
testFixtureStdio('test-trailing-slash', async (testPath) => {
|
||||
await testPath(200, '/', 'Index Page');
|
||||
await testPath(200, '/index.html', 'Index Page');
|
||||
await testPath(200, '/about.html', 'About Page');
|
||||
@@ -954,7 +954,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] should serve custom 404 when `trailingSlash: true`',
|
||||
testFixtureStdio('test-trailing-slash-custom-404', async testPath => {
|
||||
testFixtureStdio('test-trailing-slash-custom-404', async (testPath) => {
|
||||
await testPath(200, '/', 'This is the home page');
|
||||
await testPath(200, '/about.html', 'The about page');
|
||||
await testPath(200, '/contact/', 'Contact Subdirectory');
|
||||
@@ -964,7 +964,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] test trailingSlash false serve correct content',
|
||||
testFixtureStdio('test-trailing-slash-false', async testPath => {
|
||||
testFixtureStdio('test-trailing-slash-false', async (testPath) => {
|
||||
await testPath(200, '/', 'Index Page');
|
||||
await testPath(200, '/index.html', 'Index Page');
|
||||
await testPath(200, '/about.html', 'About Page');
|
||||
@@ -993,7 +993,7 @@ test(
|
||||
'[vercel dev] throw when invalid builder routes detected',
|
||||
testFixtureStdio(
|
||||
'invalid-builder-routes',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(
|
||||
500,
|
||||
'/',
|
||||
@@ -1006,14 +1006,14 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] support legacy `@now` scope runtimes',
|
||||
testFixtureStdio('legacy-now-runtime', async testPath => {
|
||||
testFixtureStdio('legacy-now-runtime', async (testPath) => {
|
||||
await testPath(200, '/', /A simple deployment with the Vercel API!/m);
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] support dynamic next.js routes in monorepos',
|
||||
testFixtureStdio('monorepo-dynamic-paths', async testPath => {
|
||||
testFixtureStdio('monorepo-dynamic-paths', async (testPath) => {
|
||||
await testPath(200, '/', /This is our homepage/m);
|
||||
await testPath(200, '/about', /This is the about static page./m);
|
||||
await testPath(
|
||||
@@ -1026,7 +1026,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] 00-list-directory',
|
||||
testFixtureStdio('00-list-directory', async testPath => {
|
||||
testFixtureStdio('00-list-directory', async (testPath) => {
|
||||
await testPath(200, '/', /Files within/m);
|
||||
await testPath(200, '/', /test[0-3]\.txt/m);
|
||||
await testPath(200, '/', /\.well-known/m);
|
||||
@@ -1036,13 +1036,13 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] 01-node',
|
||||
testFixtureStdio('01-node', async testPath => {
|
||||
testFixtureStdio('01-node', async (testPath) => {
|
||||
await testPath(200, '/', /A simple deployment with the Vercel API!/m);
|
||||
})
|
||||
);
|
||||
|
||||
// Angular has `engines: { node: "10.x" }` in its `package.json`
|
||||
test('[vercel dev] 02-angular-node', async t => {
|
||||
test('[vercel dev] 02-angular-node', async (t) => {
|
||||
if (shouldSkip(t, '02-angular-node', '10.x')) return;
|
||||
|
||||
const directory = fixture('02-angular-node');
|
||||
@@ -1053,7 +1053,7 @@ test('[vercel dev] 02-angular-node', async t => {
|
||||
let stderr = '';
|
||||
|
||||
try {
|
||||
dev.stderr.on('data', async data => {
|
||||
dev.stderr.on('data', async (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
|
||||
@@ -1083,7 +1083,7 @@ test(
|
||||
'[vercel dev] 03-aurelia',
|
||||
testFixtureStdio(
|
||||
'03-aurelia',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(200, '/', /Aurelia Navigation Skeleton/m);
|
||||
},
|
||||
{ skipDeploy: true }
|
||||
@@ -1092,7 +1092,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] 04-create-react-app',
|
||||
testFixtureStdio('04-create-react-app', async testPath => {
|
||||
testFixtureStdio('04-create-react-app', async (testPath) => {
|
||||
await testPath(200, '/', /React App/m);
|
||||
})
|
||||
);
|
||||
@@ -1106,7 +1106,7 @@ test(
|
||||
*/
|
||||
test(
|
||||
'[vercel dev] 06-gridsome',
|
||||
testFixtureStdio('06-gridsome', async testPath => {
|
||||
testFixtureStdio('06-gridsome', async (testPath) => {
|
||||
await testPath(200, '/');
|
||||
await testPath(200, '/about');
|
||||
await testPath(308, '/support', 'Redirecting to /about?ref=support (308)', {
|
||||
@@ -1120,7 +1120,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] 07-hexo-node',
|
||||
testFixtureStdio('07-hexo-node', async testPath => {
|
||||
testFixtureStdio('07-hexo-node', async (testPath) => {
|
||||
await testPath(200, '/', /Hexo \+ Node.js API/m);
|
||||
await testPath(200, '/api/date', new RegExp(new Date().getFullYear()));
|
||||
await testPath(200, '/contact.html', /Contact Us/m);
|
||||
@@ -1128,13 +1128,13 @@ test(
|
||||
})
|
||||
);
|
||||
|
||||
test('[vercel dev] 08-hugo', async t => {
|
||||
test('[vercel dev] 08-hugo', async (t) => {
|
||||
if (process.platform === 'darwin') {
|
||||
// Update PATH to find the Hugo executable installed via GH Actions
|
||||
process.env.PATH = `${resolve(fixture('08-hugo'))}${delimiter}${
|
||||
process.env.PATH
|
||||
}`;
|
||||
const tester = testFixtureStdio('08-hugo', async testPath => {
|
||||
const tester = testFixtureStdio('08-hugo', async (testPath) => {
|
||||
await testPath(200, '/', /Hugo/m);
|
||||
});
|
||||
await tester(t);
|
||||
@@ -1146,7 +1146,7 @@ test('[vercel dev] 08-hugo', async t => {
|
||||
|
||||
test(
|
||||
'[vercel dev] 10-nextjs-node',
|
||||
testFixtureStdio('10-nextjs-node', async testPath => {
|
||||
testFixtureStdio('10-nextjs-node', async (testPath) => {
|
||||
await testPath(200, '/', /Next.js \+ Node.js API/m);
|
||||
await testPath(200, '/api/date', new RegExp(new Date().getFullYear()));
|
||||
await testPath(200, '/contact', /Contact Page/);
|
||||
@@ -1159,7 +1159,7 @@ test(
|
||||
'[vercel dev] 12-polymer-node',
|
||||
testFixtureStdio(
|
||||
'12-polymer-node',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(200, '/', /Polymer \+ Node.js API/m);
|
||||
await testPath(200, '/api/date', new RegExp(new Date().getFullYear()));
|
||||
},
|
||||
@@ -1171,7 +1171,7 @@ test(
|
||||
'[vercel dev] 13-preact-node',
|
||||
testFixtureStdio(
|
||||
'13-preact-node',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(200, '/', /Preact/m);
|
||||
await testPath(200, '/api/date', new RegExp(new Date().getFullYear()));
|
||||
},
|
||||
@@ -1183,7 +1183,7 @@ test(
|
||||
'[vercel dev] 14-svelte-node',
|
||||
testFixtureStdio(
|
||||
'14-svelte-node',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(200, '/', /Svelte/m);
|
||||
await testPath(200, '/api/date', new RegExp(new Date().getFullYear()));
|
||||
},
|
||||
@@ -1195,7 +1195,7 @@ test(
|
||||
'[vercel dev] 16-vue-node',
|
||||
testFixtureStdio(
|
||||
'16-vue-node',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(200, '/', /Vue.js \+ Node.js API/m);
|
||||
await testPath(200, '/api/date', new RegExp(new Date().getFullYear()));
|
||||
},
|
||||
@@ -1207,7 +1207,7 @@ test(
|
||||
'[vercel dev] 17-vuepress-node',
|
||||
testFixtureStdio(
|
||||
'17-vuepress-node',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(200, '/', /VuePress \+ Node.js API/m);
|
||||
await testPath(200, '/api/date', new RegExp(new Date().getFullYear()));
|
||||
},
|
||||
@@ -1267,7 +1267,7 @@ test(
|
||||
'[vercel dev] 18-marko',
|
||||
testFixtureStdio(
|
||||
'18-marko',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(200, '/', /Marko Starter/m);
|
||||
},
|
||||
{ skipDeploy: true }
|
||||
@@ -1278,7 +1278,7 @@ test(
|
||||
'[vercel dev] 19-mithril',
|
||||
testFixtureStdio(
|
||||
'19-mithril',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(200, '/', /Mithril on Vercel/m);
|
||||
},
|
||||
{ skipDeploy: true }
|
||||
@@ -1289,7 +1289,7 @@ test(
|
||||
'[vercel dev] 20-riot',
|
||||
testFixtureStdio(
|
||||
'20-riot',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(200, '/', /Riot on Vercel/m);
|
||||
},
|
||||
{ skipDeploy: true }
|
||||
@@ -1300,7 +1300,7 @@ test(
|
||||
'[vercel dev] 21-charge',
|
||||
testFixtureStdio(
|
||||
'21-charge',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(200, '/', /Welcome to my new Charge site/m);
|
||||
},
|
||||
{ skipDeploy: true }
|
||||
@@ -1311,7 +1311,7 @@ test(
|
||||
'[vercel dev] 22-brunch',
|
||||
testFixtureStdio(
|
||||
'22-brunch',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(200, '/', /Bon Appétit./m);
|
||||
},
|
||||
{ skipDeploy: true }
|
||||
@@ -1322,19 +1322,19 @@ test(
|
||||
'[vercel dev] 23-docusaurus',
|
||||
testFixtureStdio(
|
||||
'23-docusaurus',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(200, '/', /My Site/m);
|
||||
},
|
||||
{ skipDeploy: true }
|
||||
)
|
||||
);
|
||||
|
||||
test('[vercel dev] 24-ember', async t => {
|
||||
test('[vercel dev] 24-ember', async (t) => {
|
||||
if (shouldSkip(t, '24-ember', '>^6.14.0 || ^8.10.0 || >=9.10.0')) return;
|
||||
|
||||
const tester = await testFixtureStdio(
|
||||
'24-ember',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(200, '/', /HelloWorld/m);
|
||||
},
|
||||
{ skipDeploy: true }
|
||||
@@ -1376,7 +1376,7 @@ test(
|
||||
)
|
||||
);
|
||||
|
||||
test('[vercel dev] add a `package.json` to trigger `@vercel/static-build`', async t => {
|
||||
test('[vercel dev] add a `package.json` to trigger `@vercel/static-build`', async (t) => {
|
||||
const directory = fixture('trigger-static-build');
|
||||
|
||||
await fs.unlink(join(directory, 'package.json')).catch(() => null);
|
||||
@@ -1419,7 +1419,7 @@ test('[vercel dev] add a `package.json` to trigger `@vercel/static-build`', asyn
|
||||
await tester(t);
|
||||
});
|
||||
|
||||
test('[vercel dev] no build matches warning', async t => {
|
||||
test('[vercel dev] no build matches warning', async (t) => {
|
||||
const directory = fixture('no-build-matches');
|
||||
const { dev } = await testFixture(directory, {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
@@ -1430,8 +1430,8 @@ test('[vercel dev] no build matches warning', async t => {
|
||||
dev.unref();
|
||||
|
||||
dev.stderr.setEncoding('utf8');
|
||||
await new Promise(resolve => {
|
||||
dev.stderr.on('data', str => {
|
||||
await new Promise((resolve) => {
|
||||
dev.stderr.on('data', (str) => {
|
||||
if (str.includes('did not match any source files')) {
|
||||
resolve();
|
||||
}
|
||||
@@ -1446,13 +1446,13 @@ test('[vercel dev] no build matches warning', async t => {
|
||||
|
||||
test(
|
||||
'[vercel dev] do not recursivly check the path',
|
||||
testFixtureStdio('handle-filesystem-missing', async testPath => {
|
||||
testFixtureStdio('handle-filesystem-missing', async (testPath) => {
|
||||
await testPath(200, '/', /hello/m);
|
||||
await testPath(404, '/favicon.txt');
|
||||
})
|
||||
);
|
||||
|
||||
test('[vercel dev] render warning for empty cwd dir', async t => {
|
||||
test('[vercel dev] render warning for empty cwd dir', async (t) => {
|
||||
const directory = fixture('empty');
|
||||
const { dev, port } = await testFixture(directory, {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
@@ -1464,8 +1464,8 @@ test('[vercel dev] render warning for empty cwd dir', async t => {
|
||||
// Monitor `stderr` for the warning
|
||||
dev.stderr.setEncoding('utf8');
|
||||
const msg = 'There are no files inside your deployment.';
|
||||
await new Promise(resolve => {
|
||||
dev.stderr.on('data', str => {
|
||||
await new Promise((resolve) => {
|
||||
dev.stderr.on('data', (str) => {
|
||||
if (str.includes(msg)) {
|
||||
resolve();
|
||||
}
|
||||
@@ -1482,7 +1482,7 @@ test('[vercel dev] render warning for empty cwd dir', async t => {
|
||||
}
|
||||
});
|
||||
|
||||
test('[vercel dev] do not rebuild for changes in the output directory', async t => {
|
||||
test('[vercel dev] do not rebuild for changes in the output directory', async (t) => {
|
||||
const directory = fixture('output-is-source');
|
||||
|
||||
// Pack the builder and set it in the `vercel.json`
|
||||
@@ -1511,7 +1511,7 @@ test('[vercel dev] do not rebuild for changes in the output directory', async t
|
||||
let stderr = [];
|
||||
const start = Date.now();
|
||||
|
||||
dev.stderr.on('data', str => stderr.push(str));
|
||||
dev.stderr.on('data', (str) => stderr.push(str));
|
||||
|
||||
while (stderr.join('').includes('Ready') === false) {
|
||||
await sleep(ms('3s'));
|
||||
@@ -1540,7 +1540,7 @@ test('[vercel dev] do not rebuild for changes in the output directory', async t
|
||||
|
||||
test(
|
||||
'[vercel dev] 25-nextjs-src-dir',
|
||||
testFixtureStdio('25-nextjs-src-dir', async testPath => {
|
||||
testFixtureStdio('25-nextjs-src-dir', async (testPath) => {
|
||||
await testPath(200, '/', /Next.js \+ Node.js API/m);
|
||||
})
|
||||
);
|
||||
@@ -1549,7 +1549,7 @@ test(
|
||||
'[vercel dev] 26-nextjs-secrets',
|
||||
testFixtureStdio(
|
||||
'26-nextjs-secrets',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(200, '/api/user', /runtime/m);
|
||||
await testPath(200, '/', /buildtime/m);
|
||||
},
|
||||
@@ -1561,7 +1561,7 @@ test(
|
||||
'[vercel dev] 27-zero-config-env',
|
||||
testFixtureStdio(
|
||||
'27-zero-config-env',
|
||||
async testPath => {
|
||||
async (testPath) => {
|
||||
await testPath(200, '/api/print', /build-and-runtime/m);
|
||||
await testPath(200, '/', /build-and-runtime/m);
|
||||
},
|
||||
@@ -1571,7 +1571,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] 28-vercel-json-and-ignore',
|
||||
testFixtureStdio('28-vercel-json-and-ignore', async testPath => {
|
||||
testFixtureStdio('28-vercel-json-and-ignore', async (testPath) => {
|
||||
await testPath(200, '/api/one', 'One');
|
||||
await testPath(404, '/api/two');
|
||||
await testPath(200, '/api/three', 'One');
|
||||
@@ -1580,7 +1580,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] Use `@vercel/python` with Flask requirements.txt',
|
||||
testFixtureStdio('python-flask', async testPath => {
|
||||
testFixtureStdio('python-flask', async (testPath) => {
|
||||
const name = 'Alice';
|
||||
const year = new Date().getFullYear();
|
||||
await testPath(200, `/api/user?name=${name}`, new RegExp(`Hello ${name}`));
|
||||
@@ -1591,7 +1591,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] Use custom runtime from the "functions" property',
|
||||
testFixtureStdio('custom-runtime', async testPath => {
|
||||
testFixtureStdio('custom-runtime', async (testPath) => {
|
||||
await testPath(200, `/api/user`, /Hello, from Bash!/m);
|
||||
await testPath(200, `/api/user.sh`, /Hello, from Bash!/m);
|
||||
})
|
||||
@@ -1599,7 +1599,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] Should work with nested `tsconfig.json` files',
|
||||
testFixtureStdio('nested-tsconfig', async testPath => {
|
||||
testFixtureStdio('nested-tsconfig', async (testPath) => {
|
||||
await testPath(200, `/`, /Nested tsconfig.json test page/);
|
||||
await testPath(200, `/api`, 'Nested `tsconfig.json` API endpoint');
|
||||
})
|
||||
@@ -1607,7 +1607,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] Should force `tsc` option "module: commonjs" for `startDevServer()`',
|
||||
testFixtureStdio('force-module-commonjs', async testPath => {
|
||||
testFixtureStdio('force-module-commonjs', async (testPath) => {
|
||||
await testPath(200, `/`, /Force "module: commonjs" test page/);
|
||||
await testPath(
|
||||
200,
|
||||
@@ -1624,7 +1624,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] should prioritize index.html over other file named index.*',
|
||||
testFixtureStdio('index-html-priority', async testPath => {
|
||||
testFixtureStdio('index-html-priority', async (testPath) => {
|
||||
await testPath(200, '/', 'This is index.html');
|
||||
await testPath(200, '/index.css', 'This is index.css');
|
||||
})
|
||||
@@ -1632,7 +1632,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] Should support `*.go` API serverless functions',
|
||||
testFixtureStdio('go', async testPath => {
|
||||
testFixtureStdio('go', async (testPath) => {
|
||||
await testPath(200, `/api`, 'This is the index page');
|
||||
await testPath(200, `/api/index`, 'This is the index page');
|
||||
await testPath(200, `/api/index.go`, 'This is the index page');
|
||||
@@ -1645,7 +1645,7 @@ test(
|
||||
|
||||
test(
|
||||
'[vercel dev] Should set the `ts-node` "target" to match Node.js version',
|
||||
testFixtureStdio('node-ts-node-target', async testPath => {
|
||||
testFixtureStdio('node-ts-node-target', async (testPath) => {
|
||||
await testPath(200, `/api/subclass`, '{"ok":true}');
|
||||
await testPath(
|
||||
200,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "8.1.1-canary.1",
|
||||
"version": "8.1.1-canary.2",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -38,6 +38,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.4.1-canary.2",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "3.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "2.6.8-canary.4",
|
||||
"version": "2.6.9",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
|
||||
|
||||
@@ -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;
|
||||
})
|
||||
@@ -1230,7 +1242,7 @@ export const build = async ({
|
||||
const pages = {
|
||||
${groupPageKeys
|
||||
.map(
|
||||
page =>
|
||||
(page) =>
|
||||
`'${page}': require('./${path.join(
|
||||
'./',
|
||||
group.pages[page].pageFileName
|
||||
@@ -1275,7 +1287,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 => ({
|
||||
dynamicRoutes.map((route) => ({
|
||||
src: route.src,
|
||||
dest: route.dest,
|
||||
}))
|
||||
@@ -1461,33 +1473,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 +1571,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 +1586,7 @@ export const build = async ({
|
||||
delete lambdas[routeFileNoExt];
|
||||
});
|
||||
}
|
||||
const mergedDataRoutesLambdaRoutes = [];
|
||||
const mergedDynamicRoutesLambdaRoutes = [];
|
||||
|
||||
if (isSharedLambdas) {
|
||||
@@ -1580,12 +1601,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 +1694,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 +1734,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 +1779,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",
|
||||
|
||||
@@ -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';
|
||||
},
|
||||
};
|
||||
50
packages/now-next/test/fixtures/27-non-word-param/now.json
vendored
Normal file
50
packages/now-next/test/fixtures/27-non-word-param/now.json
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "package.json", "use": "@vercel/next" }],
|
||||
"probes": [
|
||||
// make sure index responds correctly
|
||||
{
|
||||
"path": "/",
|
||||
"mustContain": "Create Next App"
|
||||
},
|
||||
|
||||
// make sure lazy catch-all SSG page matches
|
||||
{
|
||||
"path": "/another",
|
||||
"mustContain": "Loading..."
|
||||
},
|
||||
|
||||
{ "delay": 2000 },
|
||||
|
||||
// make sure lazy catch-all SSG page was cached
|
||||
{
|
||||
"path": "/another",
|
||||
"mustContain": "My awesome article"
|
||||
},
|
||||
|
||||
// make sure correct param is used
|
||||
{
|
||||
"path": "/another",
|
||||
"mustContain": "another"
|
||||
},
|
||||
|
||||
// make sure lazy catch-all SSG data route matches
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/something.json",
|
||||
"mustContain": "My awesome article"
|
||||
},
|
||||
|
||||
// make sure lazy catch-all SSG data route has correct param
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/something.json",
|
||||
"mustContain": "something"
|
||||
},
|
||||
|
||||
// make sure lazy catch-all SSG data route doesn't have HTML
|
||||
// make sure lazy catch-all SSG data route matches
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/one-more.json",
|
||||
"mustNotContain": "<html>"
|
||||
}
|
||||
]
|
||||
}
|
||||
7
packages/now-next/test/fixtures/27-non-word-param/package.json
vendored
Normal file
7
packages/now-next/test/fixtures/27-non-word-param/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "9.4.4",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
65
packages/now-next/test/fixtures/27-non-word-param/pages/[...path-segments].js
vendored
Normal file
65
packages/now-next/test/fixtures/27-non-word-param/pages/[...path-segments].js
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
import { useRouter } from 'next/router';
|
||||
import Error from 'next/error';
|
||||
|
||||
function loadArticle() {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'header',
|
||||
content: 'My awesome article',
|
||||
},
|
||||
{
|
||||
type: 'paragraph',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend.',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const Page = ({ path, article }) => {
|
||||
const router = useRouter();
|
||||
|
||||
if (router.isFallback) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
if (!article.content) {
|
||||
return <Error statusCode={404} />;
|
||||
}
|
||||
|
||||
const [header, ...body] = article.content;
|
||||
|
||||
return (
|
||||
<article>
|
||||
<header>{header.content}</header>
|
||||
<small>path: {path.join('/')}</small>
|
||||
<main>
|
||||
{body.map(({ content }) => (
|
||||
<p>{content}</p>
|
||||
))}
|
||||
</main>
|
||||
</article>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
export async function getStaticProps({ params }) {
|
||||
const path = params['path-segments'];
|
||||
const article = loadArticle(path);
|
||||
|
||||
return {
|
||||
props: {
|
||||
article,
|
||||
path,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return {
|
||||
paths: [],
|
||||
fallback: true,
|
||||
};
|
||||
}
|
||||
201
packages/now-next/test/fixtures/27-non-word-param/pages/index.js
vendored
Normal file
201
packages/now-next/test/fixtures/27-non-word-param/pages/index.js
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
import Head from 'next/head';
|
||||
|
||||
const Home = () => (
|
||||
<div className="container">
|
||||
<Head>
|
||||
<title>Create Next App</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<main>
|
||||
<h1 className="title">
|
||||
Welcome to <a href="https://nextjs.org">Next.js!</a>
|
||||
</h1>
|
||||
|
||||
<p className="description">
|
||||
Get started by editing <code>pages/index.js</code>
|
||||
</p>
|
||||
|
||||
<div className="grid">
|
||||
<a href="https://nextjs.org/docs" className="card">
|
||||
<h3>Documentation →</h3>
|
||||
<p>Find in-depth information about Next.js features and API.</p>
|
||||
</a>
|
||||
|
||||
<a href="https://nextjs.org/learn" className="card">
|
||||
<h3>Learn →</h3>
|
||||
<p>Learn about Next.js in an interactive course with quizzes!</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://github.com/zeit/next.js/tree/master/examples"
|
||||
className="card"
|
||||
>
|
||||
<h3>Examples →</h3>
|
||||
<p>Discover and deploy boilerplate example Next.js projects.</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/import?filter=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
className="card"
|
||||
>
|
||||
<h3>Deploy →</h3>
|
||||
<p>Instantly deploy your Next.js site to a public URL with Vercel.</p>
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<a
|
||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Powered by Vercel
|
||||
</a>
|
||||
</footer>
|
||||
|
||||
<style jsx>{`
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
padding: 0 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 5rem 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
footer {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
border-top: 1px solid #eaeaea;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
footer img {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
footer a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.title a {
|
||||
color: #0070f3;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.title a:hover,
|
||||
.title a:focus,
|
||||
.title a:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
line-height: 1.15;
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
.title,
|
||||
.description {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.description {
|
||||
line-height: 1.5;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #fafafa;
|
||||
border-radius: 5px;
|
||||
padding: 0.75rem;
|
||||
font-size: 1.1rem;
|
||||
font-family: Menlo, Monaco, Lucida Console, Liberation Mono,
|
||||
DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
max-width: 800px;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin: 1rem;
|
||||
flex-basis: 45%;
|
||||
padding: 1.5rem;
|
||||
text-align: left;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
border: 1px solid #eaeaea;
|
||||
border-radius: 10px;
|
||||
transition: color 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.card:hover,
|
||||
.card:focus,
|
||||
.card:active {
|
||||
color: #0070f3;
|
||||
border-color: #0070f3;
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
margin: 0 0 1rem 0;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.card p {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.grid {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<style jsx global>{`
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Home;
|
||||
@@ -15,8 +15,8 @@ it(
|
||||
buildResult: { output },
|
||||
} = await runBuildLambda(path.join(__dirname, 'standard'));
|
||||
expect(output['index']).toBeDefined();
|
||||
expect(output.goodbye).toBeDefined();
|
||||
expect(output.__NEXT_PAGE_LAMBDA_0).not.toBeDefined();
|
||||
expect(output.goodbye).not.toBeDefined();
|
||||
expect(output.__NEXT_PAGE_LAMBDA_0).toBeDefined();
|
||||
const filePaths = Object.keys(output);
|
||||
const serverlessError = filePaths.some(filePath =>
|
||||
filePath.match(/_error/)
|
||||
@@ -34,27 +34,49 @@ it(
|
||||
FOUR_MINUTES
|
||||
);
|
||||
|
||||
// it(
|
||||
// 'Should opt-out of shared lambdas when routes are detected',
|
||||
// async () => {
|
||||
// const {
|
||||
// buildResult: { output },
|
||||
// } = await runBuildLambda(path.join(__dirname, '../fixtures/26-mono-repo-404-lambda'));
|
||||
// expect(output['packages/webapp/404']).toBeDefined();
|
||||
// expect(output['packages/webapp/index']).toBeDefined();
|
||||
// expect(output['packages/webapp/__NEXT_PAGE_LAMBDA_0']).not.toBeDefined();
|
||||
// const filePaths = Object.keys(output);
|
||||
// const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
||||
// filePath.match(/static.*\/pages\/_app\.js$/)
|
||||
// );
|
||||
// const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
||||
// filePath.match(/static.*\/pages\/_error\.js$/)
|
||||
// );
|
||||
// expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
||||
// expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
||||
// },
|
||||
// FOUR_MINUTES
|
||||
// );
|
||||
it(
|
||||
'Should not deploy preview lambdas for static site',
|
||||
async () => {
|
||||
const {
|
||||
buildResult: { output },
|
||||
} = await runBuildLambda(path.join(__dirname, 'static-site'));
|
||||
expect(output['index']).toBeDefined();
|
||||
expect(output['index'].type).toBe('FileFsRef');
|
||||
|
||||
expect(output['another']).toBeDefined();
|
||||
|
||||
expect(output['another'].type).toBe('FileFsRef');
|
||||
|
||||
expect(output['dynamic']).toBeDefined();
|
||||
expect(output['dynamic'].type).toBe('Prerender');
|
||||
expect(output['dynamic'].lambda).toBeDefined();
|
||||
},
|
||||
FOUR_MINUTES
|
||||
);
|
||||
|
||||
it(
|
||||
'Should opt-out of shared lambdas when routes are detected',
|
||||
async () => {
|
||||
const {
|
||||
buildResult: { output },
|
||||
} = await runBuildLambda(
|
||||
path.join(__dirname, '../fixtures/26-mono-repo-404-lambda')
|
||||
);
|
||||
expect(output['packages/webapp/404']).toBeDefined();
|
||||
expect(output['packages/webapp/index']).toBeDefined();
|
||||
expect(output['packages/webapp/__NEXT_PAGE_LAMBDA_0']).not.toBeDefined();
|
||||
const filePaths = Object.keys(output);
|
||||
const hasUnderScoreAppStaticFile = filePaths.some(filePath =>
|
||||
filePath.match(/static.*\/pages\/_app\.js$/)
|
||||
);
|
||||
const hasUnderScoreErrorStaticFile = filePaths.some(filePath =>
|
||||
filePath.match(/static.*\/pages\/_error\.js$/)
|
||||
);
|
||||
expect(hasUnderScoreAppStaticFile).toBeTruthy();
|
||||
expect(hasUnderScoreErrorStaticFile).toBeTruthy();
|
||||
},
|
||||
FOUR_MINUTES
|
||||
);
|
||||
|
||||
it(
|
||||
'Should build the monorepo example',
|
||||
@@ -63,8 +85,8 @@ it(
|
||||
buildResult: { output },
|
||||
} = await runBuildLambda(path.join(__dirname, 'monorepo'));
|
||||
|
||||
expect(output['www/index']).toBeDefined();
|
||||
expect(output['www/__NEXT_PAGE_LAMBDA_0']).not.toBeDefined();
|
||||
expect(output['www/index']).not.toBeDefined();
|
||||
expect(output['www/__NEXT_PAGE_LAMBDA_0']).toBeDefined();
|
||||
expect(output['www/static/test.txt']).toBeDefined();
|
||||
expect(output['www/data.txt']).toBeDefined();
|
||||
const filePaths = Object.keys(output);
|
||||
@@ -164,9 +186,9 @@ it(
|
||||
buildResult: { output },
|
||||
} = await runBuildLambda(path.join(__dirname, 'serverless-config'));
|
||||
|
||||
expect(output.index).toBeDefined();
|
||||
expect(output.goodbye).toBeDefined();
|
||||
expect(output.__NEXT_PAGE_LAMBDA_0).not.toBeDefined();
|
||||
expect(output.index).not.toBeDefined();
|
||||
expect(output.goodbye).not.toBeDefined();
|
||||
expect(output.__NEXT_PAGE_LAMBDA_0).toBeDefined();
|
||||
const filePaths = Object.keys(output);
|
||||
const serverlessError = filePaths.some(filePath =>
|
||||
filePath.match(/_error/)
|
||||
@@ -201,9 +223,9 @@ it(
|
||||
path.join(__dirname, 'serverless-config-monorepo-missing')
|
||||
);
|
||||
|
||||
expect(output['nested/index']).toBeDefined();
|
||||
expect(output['nested/goodbye']).toBeDefined();
|
||||
expect(output['nested/__NEXT_PAGE_LAMBDA_0']).not.toBeDefined();
|
||||
expect(output['nested/index']).not.toBeDefined();
|
||||
expect(output['nested/goodbye']).not.toBeDefined();
|
||||
expect(output['nested/__NEXT_PAGE_LAMBDA_0']).toBeDefined();
|
||||
const filePaths = Object.keys(output);
|
||||
const serverlessError = filePaths.some(filePath =>
|
||||
filePath.match(/_error/)
|
||||
@@ -235,9 +257,9 @@ it(
|
||||
path.join(__dirname, 'serverless-config-monorepo-present')
|
||||
);
|
||||
|
||||
expect(output['nested/index']).toBeDefined();
|
||||
expect(output['nested/goodbye']).toBeDefined();
|
||||
expect(output['nested/__NEXT_PAGE_LAMBDA_0']).not.toBeDefined();
|
||||
expect(output['nested/index']).not.toBeDefined();
|
||||
expect(output['nested/goodbye']).not.toBeDefined();
|
||||
expect(output['nested/__NEXT_PAGE_LAMBDA_0']).toBeDefined();
|
||||
const filePaths = Object.keys(output);
|
||||
const serverlessError = filePaths.some(filePath =>
|
||||
filePath.match(/_error/)
|
||||
@@ -303,8 +325,8 @@ it(
|
||||
} = await runBuildLambda(path.join(__dirname, 'serverless-config-object'));
|
||||
|
||||
expect(output['index']).toBeDefined();
|
||||
expect(output.goodbye).toBeDefined();
|
||||
expect(output.__NEXT_PAGE_LAMBDA_0).not.toBeDefined();
|
||||
expect(output.goodbye).not.toBeDefined();
|
||||
expect(output.__NEXT_PAGE_LAMBDA_0).toBeDefined();
|
||||
const filePaths = Object.keys(output);
|
||||
const serverlessError = filePaths.some(filePath =>
|
||||
filePath.match(/_error/)
|
||||
@@ -338,8 +360,8 @@ it(
|
||||
} = await runBuildLambda(path.join(__dirname, 'serverless-no-config'));
|
||||
|
||||
expect(output['index']).toBeDefined();
|
||||
expect(output.goodbye).toBeDefined();
|
||||
expect(output.__NEXT_PAGE_LAMBDA_0).not.toBeDefined();
|
||||
expect(output.goodbye).not.toBeDefined();
|
||||
expect(output.__NEXT_PAGE_LAMBDA_0).toBeDefined();
|
||||
const filePaths = Object.keys(output);
|
||||
const serverlessError = filePaths.some(filePath =>
|
||||
filePath.match(/_error/)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "latest",
|
||||
"react-dom": "latest"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export default function Page() {
|
||||
return 'hello';
|
||||
}
|
||||
|
||||
export function getStaticProps() {
|
||||
return { props: {} };
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export default function Page() {
|
||||
return 'hello';
|
||||
}
|
||||
|
||||
export function getStaticProps() {
|
||||
return { props: {}, unstable_revalidate: 10 };
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export default function Page() {
|
||||
return 'hello';
|
||||
}
|
||||
|
||||
export function getStaticProps() {
|
||||
return { props: {} };
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "package.json", "use": "@vercel/next" }]
|
||||
}
|
||||
9
packages/now-next/test/test.js
vendored
9
packages/now-next/test/test.js
vendored
@@ -24,9 +24,12 @@ beforeAll(async () => {
|
||||
}
|
||||
|
||||
process.env.NEXT_TELEMETRY_DISABLED = '1';
|
||||
const builderPath = path.resolve(__dirname, '..');
|
||||
builderUrl = await packAndDeploy(builderPath);
|
||||
console.log('builderUrl', builderUrl);
|
||||
|
||||
if (!builderUrl) {
|
||||
const builderPath = path.resolve(__dirname, '..');
|
||||
builderUrl = await packAndDeploy(builderPath);
|
||||
console.log('builderUrl', builderUrl);
|
||||
}
|
||||
});
|
||||
|
||||
const fixturesPath = path.resolve(__dirname, 'fixtures');
|
||||
|
||||
@@ -5,9 +5,11 @@ const _fetch = require('node-fetch');
|
||||
const fetch = require('./fetch-retry.js');
|
||||
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
async function nowDeploy(bodies, randomness) {
|
||||
async function nowDeploy(bodies, randomness, uploadNowJson) {
|
||||
const files = Object.keys(bodies)
|
||||
.filter(n => n !== 'vercel.json' && n !== 'now.json')
|
||||
.filter(n =>
|
||||
uploadNowJson ? true : n !== 'vercel.json' && n !== 'now.json'
|
||||
)
|
||||
.map(n => ({
|
||||
sha: digestOfFile(bodies[n]),
|
||||
size: bodies[n].length,
|
||||
|
||||
@@ -57,6 +57,8 @@ async function testDeployment(
|
||||
|
||||
// we use json5 to allow comments for probes
|
||||
const nowJson = json5.parse(bodies[configName]);
|
||||
const uploadNowJson = nowJson.uploadNowJson;
|
||||
delete nowJson.uploadNowJson;
|
||||
|
||||
if (process.env.VERCEL_BUILDER_DEBUG) {
|
||||
if (!nowJson.build) {
|
||||
@@ -94,8 +96,14 @@ async function testDeployment(
|
||||
|
||||
bodies[configName] = Buffer.from(JSON.stringify(nowJson));
|
||||
delete bodies['probe.js'];
|
||||
const { deploymentId, deploymentUrl } = await nowDeploy(bodies, randomness);
|
||||
|
||||
const { deploymentId, deploymentUrl } = await nowDeploy(
|
||||
bodies,
|
||||
randomness,
|
||||
uploadNowJson
|
||||
);
|
||||
let nextBuildManifest;
|
||||
let deploymentLogs;
|
||||
|
||||
for (const probe of nowJson.probes || []) {
|
||||
console.log('testing', JSON.stringify(probe));
|
||||
@@ -104,6 +112,60 @@ async function testDeployment(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (probe.logMustContain || probe.logMustNotContain) {
|
||||
const shouldContain = !!probe.logMustContain;
|
||||
const toCheck = probe.logMustContain || probe.logMustNotContain;
|
||||
|
||||
if (probe.logMustContain && probe.logMustNotContain) {
|
||||
throw new Error(
|
||||
`probe can not check logMustContain and logMustNotContain in the same check`
|
||||
);
|
||||
}
|
||||
|
||||
if (!deploymentLogs) {
|
||||
try {
|
||||
const logsRes = await fetch(
|
||||
`https://vercel.com/api/v1/now/deployments/${deploymentId}/events?limit=-1`
|
||||
);
|
||||
|
||||
if (!logsRes.ok) {
|
||||
throw new Error(
|
||||
`fetching logs failed with status ${logsRes.status}`
|
||||
);
|
||||
}
|
||||
deploymentLogs = await logsRes.json();
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`Failed to get deployment logs for probe: ${err.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let found = false;
|
||||
|
||||
for (const log of deploymentLogs) {
|
||||
if (log.text && log.text.includes(toCheck)) {
|
||||
if (shouldContain) {
|
||||
found = true;
|
||||
break;
|
||||
} else {
|
||||
throw new Error(
|
||||
`Expected deployment logs not to contain ${toCheck}, but found ${log.text}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found && shouldContain) {
|
||||
throw new Error(
|
||||
`Expected deployment logs to contain ${toCheck}, it was not found`
|
||||
);
|
||||
} else {
|
||||
console.log('finished testing', JSON.stringify(probe));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const nextScriptIndex = probe.path.indexOf('__NEXT_SCRIPT__(');
|
||||
|
||||
if (nextScriptIndex > -1) {
|
||||
|
||||
@@ -9219,10 +9219,10 @@ prepend-http@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
|
||||
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
|
||||
|
||||
prettier@1.18.2:
|
||||
version "1.18.2"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea"
|
||||
integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==
|
||||
prettier@2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4"
|
||||
integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==
|
||||
|
||||
pretty-format@^24.9.0:
|
||||
version "24.9.0"
|
||||
|
||||
Reference in New Issue
Block a user