mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-28 03:39:12 +00:00
Compare commits
47 Commits
@vercel/ne
...
@vercel/no
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d4be3f5f2 | ||
|
|
a971c2aa3a | ||
|
|
66414bd65a | ||
|
|
3b021993b5 | ||
|
|
c9597dc199 | ||
|
|
1b5c94b392 | ||
|
|
bd1393a9d6 | ||
|
|
47ddd36fec | ||
|
|
bd047cdc6a | ||
|
|
477f9e8753 | ||
|
|
183ea42f94 | ||
|
|
57a17eb416 | ||
|
|
be24510f16 | ||
|
|
05c534d855 | ||
|
|
910169dba7 | ||
|
|
0fc74feddc | ||
|
|
a9ea294514 | ||
|
|
42e0450d87 | ||
|
|
08c0feb58a | ||
|
|
60aaf76ed2 | ||
|
|
bd706beba5 | ||
|
|
b85a0a349a | ||
|
|
a97bc2af6d | ||
|
|
a5dc90c8cd | ||
|
|
e90521b5a4 | ||
|
|
10c598834a | ||
|
|
e58b34b82c | ||
|
|
02e1c921ac | ||
|
|
fcb14fa70e | ||
|
|
46e18128c7 | ||
|
|
e06d5247ef | ||
|
|
02195b92a7 | ||
|
|
7945155a0f | ||
|
|
edb6043c2c | ||
|
|
971481ba51 | ||
|
|
db8e456603 | ||
|
|
d1b4f24a4a | ||
|
|
4eb39ed53b | ||
|
|
6c7236ffd5 | ||
|
|
f0def040ac | ||
|
|
ee8dff2dc9 | ||
|
|
df86a6d48c | ||
|
|
b3958b3d12 | ||
|
|
8dd22703a7 | ||
|
|
c7e2280d23 | ||
|
|
2c239a6ee2 | ||
|
|
43709fbc74 |
8
.github/CONTRIBUTING.md
vendored
8
.github/CONTRIBUTING.md
vendored
@@ -68,12 +68,12 @@ In such cases you can visit the URL of the failed deployment and append `/_logs`
|
||||
|
||||
The logs of this deployment will contain the actual error which may help you to understand what went wrong.
|
||||
|
||||
### @zeit/node-file-trace
|
||||
### @vercel/nft
|
||||
|
||||
Some of the Builders use `@zeit/node-file-trace` to tree-shake files before deployment. If you suspect an error with this tree-shaking mechanism, you can create the following script in your project:
|
||||
Some of the Builders use `@vercel/nft` to tree-shake files before deployment. If you suspect an error with this tree-shaking mechanism, you can create the following script in your project:
|
||||
|
||||
```js
|
||||
const trace = require('@zeit/node-file-trace');
|
||||
const trace = require('@vercel/nft');
|
||||
trace(['path/to/entrypoint.js'], {
|
||||
ts: true,
|
||||
mixedModules: true,
|
||||
@@ -82,7 +82,7 @@ trace(['path/to/entrypoint.js'], {
|
||||
.then(e => console.error(e));
|
||||
```
|
||||
|
||||
When you run this script, you'll see all imported files. If anything file is missing, the bug is in [@zeit/node-file-trace](https://github.com/vercel/node-file-trace) and not the Builder.
|
||||
When you run this script, you'll see all imported files. If anything file is missing, the bug is in [@vercel/nft](https://github.com/vercel/nft) and not the Builder.
|
||||
|
||||
## Deploy a Builder with existing project
|
||||
|
||||
|
||||
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 10
|
||||
node-version: 12
|
||||
- name: Install
|
||||
run: yarn install --check-files --frozen-lockfile
|
||||
- name: Build
|
||||
|
||||
@@ -17,11 +17,3 @@ To get started deploying AMP with Vercel, you can use the [Vercel CLI](https://v
|
||||
```shell
|
||||
$ vercel init amp
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new AMP project with a single command from your terminal using Vercel CLI:
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ To get started with Angular, you can use the [Angular CLI](https://cli.angular.i
|
||||
```shell
|
||||
$ ng new
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Angular project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||

|
||||
|
||||
This is a [Blitz.js](https://blitzjs.com/) project bootstrapped with `npx blitz new`.
|
||||
# Blitz.js Example
|
||||
|
||||
## Getting Started
|
||||
This directory is a brief example of a [Blitz.js](https://blitzjs.com/) project that can be deployed with Vercel and zero configuration.
|
||||
|
||||
First, run the development server:
|
||||
## Deploy Your Own
|
||||
|
||||
```bash
|
||||
npx blitz start
|
||||
Deploy your own Blitz.js project with Vercel by viewing the [documentation on deploying to Vercel](https://blitzjs.com/docs/deploy-vercel)
|
||||
|
||||
[](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/blitzjs)
|
||||
|
||||
### How We Created This Example
|
||||
|
||||
To get started with Blitz.js, you can use [npx](https://www.npmjs.com/package/npx) to initialize the project:
|
||||
|
||||
```shell
|
||||
$ npx blitz new
|
||||
```
|
||||
|
||||
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)
|
||||
|
||||
@@ -17,11 +17,3 @@ To get started deploying Brunch with Vercel, you can use the [Brunch CLI](https:
|
||||
```shell
|
||||
$ brunch new project-name -s es6
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Brunch project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ To get started with React, along with [Serverless Functions](https://vercel.com/
|
||||
```shell
|
||||
$ npx create-react-app my-app
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new React project, along with [Serverless Functions](https://vercel.com/docs/v2/serverless-functions/introduction), with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -17,11 +17,3 @@ To get started deploying a Custom Built project with Vercel, you can use the [Ve
|
||||
```shell
|
||||
$ vercel init custom-build
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Custom Built project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -15,11 +15,3 @@ To get started with Docusaurus on Vercel, you can use the [Docusaurus CLI](https
|
||||
```shell
|
||||
$ npx @docusaurus/init@next init my-website classic
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Docusaurus project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ To get started with Docusaurus for deployment with Vercel, you can use the [Docu
|
||||
```shell
|
||||
$ docusaurus-init
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Docusaurus project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -15,11 +15,3 @@ To get started with Dojo on Vercel, you can use the [Dojo CLI](https://github.co
|
||||
```shell
|
||||
$ vercel init dojo
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Dojo project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ To get started with Eleventy for deployment with Vercel, you can use [npx](https
|
||||
```shell
|
||||
$ npx degit 11ty/eleventy-base-blog my-11ty-project
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Eleventy project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ To get started with Ember for deployment with Vercel, you can use the [Ember CLI
|
||||
```shell
|
||||
$ npx ember-cli new ember-project
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Ember project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ To get started with Gatsby on Vercel, you can use the [Gatsby CLI](https://www.g
|
||||
```shell
|
||||
$ gatsby new gatsby-site
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Gatsby project, along with [Serverless Functions](https://vercel.com/docs/v2/serverless-functions/introduction), with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ To get started with Gridsome for deployment with Vercel, you can use the [Gridso
|
||||
```shell
|
||||
$ gridsome create my-website
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Gridsome project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ To get started with Hexo for deployment with Vercel, you can use the [Hexo CLI](
|
||||
```shell
|
||||
$ hexo init project-name
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Hexo project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -17,11 +17,3 @@ To get started with Hugo for deployment with Vercel, you can use the [Hugo CLI](
|
||||
```shell
|
||||
$ hugo new site project-name
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Hugo project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -17,11 +17,3 @@ To get started with Ionic Angular deployed with Vercel, you can use the [Ionic C
|
||||
```shell
|
||||
$ npx @ionic/cli start [project-name] conference --type angular && cd [project-name]
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Ionic Angular project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -27,7 +27,7 @@ export class MapPage implements AfterViewInit {
|
||||
}
|
||||
|
||||
const googleMaps = await getGoogleMaps(
|
||||
'AIzaSyB8pf6ZdFQj5qw7rc_HSGrhUwQKfIe9ICw'
|
||||
'YOUR_API_KEY_HERE'
|
||||
);
|
||||
|
||||
let map;
|
||||
|
||||
@@ -17,11 +17,3 @@ To get started with Ionic React deployed with Vercel, you can use the [Ionic CLI
|
||||
```shell
|
||||
$ npx ionic start [project-name] conference --type react && cd [project-name]
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Ionic React project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -17,11 +17,3 @@ To get started with Jekyll for deployment with Vercel, you can use the [Jekyll C
|
||||
```shell
|
||||
$ jekyll new my-blog
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Jekyll project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -17,11 +17,3 @@ To get started with Middleman for deployment with Vercel, you can use the [Middl
|
||||
```shell
|
||||
$ middleman init project-name
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Middleman project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ $ npx create-nuxt-app my-app
|
||||
```
|
||||
|
||||
> The only change made is to amend the output directory in `nuxt.config.js` to `"/public"`.
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Nuxt.js project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ To get started with Polymer deployed with Vercel, you can use the [Polymer CLI](
|
||||
```shell
|
||||
$ polymer init
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Polymer project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ To get started with Preact for deployment with Vercel, you can use the [Preact C
|
||||
```shell
|
||||
$ preact create default my-project
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Preact project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ To get started with RedwoodJS on Vercel, you can [use Yarn to initialize](https:
|
||||
```shell
|
||||
$ yarn create redwood-app ./my-redwood-app
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new RedwoodJS project, along with [Serverless Functions](https://vercel.com/docs/v2/serverless-functions/introduction), with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ To get started with Saber on Vercel, you can use [`npm init`](https://docs.npmjs
|
||||
```shell
|
||||
$ npm init site my-saber-site
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Saber project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ $ npx degit "sveltejs/sapper-template#webpack" my-sapper-app
|
||||
```
|
||||
|
||||
> The only change made is to change the build script in `package.json` to be `"sapper export"`.
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Sapper project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ To get started deploying Scully with Vercel, you can use the [Vercel CLI](https:
|
||||
```shell
|
||||
$ vercel init scully
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Scully project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -17,11 +17,3 @@ To get started with Stencil deployed with Vercel, you can use the [Stencil proje
|
||||
```shell
|
||||
$ npm init stencil
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Stencil project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -17,11 +17,3 @@ To get started with Svelte, along with [Serverless Functions](https://vercel.com
|
||||
```shell
|
||||
$ npx degit sveltejs/template my-svelte-project
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new Svelte project, along with [Serverless Functions](https://vercel.com/docs/v2/serverless-functions/introduction), with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -19,11 +19,3 @@ To get started with UmiJS deployed with Vercel, you can use the [Umi CLI](https:
|
||||
```shell
|
||||
$ yarn create umi app-name
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
You can deploy your new UmiJS project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
|
||||
|
||||
```shell
|
||||
$ vercel
|
||||
```
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "2.0.0",
|
||||
"@typescript-eslint/parser": "2.0.0",
|
||||
"@zeit/ncc": "0.20.4",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"async-retry": "1.2.3",
|
||||
"buffer-replace": "1.0.0",
|
||||
"cheerio": "1.0.0-rc.3",
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Gatsby",
|
||||
"name": "Gatsby.js",
|
||||
"slug": "gatsby",
|
||||
"demo": "https://gatsby.now-examples.now.sh",
|
||||
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/gatsby.svg",
|
||||
@@ -261,7 +261,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Ember",
|
||||
"name": "Ember.js",
|
||||
"slug": "ember",
|
||||
"demo": "https://ember.now-examples.now.sh",
|
||||
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/ember.svg",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/frameworks",
|
||||
"version": "0.0.18-canary.5",
|
||||
"version": "0.1.1",
|
||||
"main": "frameworks.json",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "2.4.3-canary.4",
|
||||
"version": "2.5.2-canary.2",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
@@ -29,7 +29,8 @@
|
||||
"@types/node-fetch": "^2.1.6",
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/yazl": "^2.4.1",
|
||||
"@vercel/frameworks": "0.0.18-canary.5",
|
||||
"@vercel/frameworks": "0.1.1",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"aggregate-error": "3.0.1",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "2.1.4",
|
||||
|
||||
@@ -26,6 +26,7 @@ interface Options {
|
||||
devCommand?: string | null;
|
||||
buildCommand?: string | null;
|
||||
outputDirectory?: string | null;
|
||||
createdAt?: number;
|
||||
};
|
||||
cleanUrls?: boolean;
|
||||
trailingSlash?: boolean;
|
||||
@@ -434,6 +435,7 @@ function detectFrontBuilder(
|
||||
): Builder {
|
||||
const { tag, projectSettings = {} } = options;
|
||||
const withTag = tag ? `@${tag}` : '';
|
||||
const { createdAt = 0 } = projectSettings;
|
||||
let { framework } = projectSettings;
|
||||
|
||||
const config: Config = {
|
||||
@@ -456,7 +458,7 @@ function detectFrontBuilder(
|
||||
config.outputDirectory = projectSettings.outputDirectory;
|
||||
}
|
||||
|
||||
if (pkg && framework !== null) {
|
||||
if (pkg && (framework !== null || createdAt < Date.parse('2020-03-01'))) {
|
||||
const deps: PackageJson['dependencies'] = {
|
||||
...pkg.dependencies,
|
||||
...pkg.devDependencies,
|
||||
@@ -929,11 +931,10 @@ function getRouteResult(
|
||||
const redirectRoutes: Route[] = [];
|
||||
const rewriteRoutes: Route[] = [];
|
||||
const errorRoutes: Route[] = [];
|
||||
const isNextjs =
|
||||
frontendBuilder &&
|
||||
((frontendBuilder.use && frontendBuilder.use.startsWith('@vercel/next')) ||
|
||||
(frontendBuilder.config &&
|
||||
frontendBuilder.config.framework === 'nextjs'));
|
||||
const framework = frontendBuilder?.config?.framework || '';
|
||||
const use = frontendBuilder?.use || '';
|
||||
const isNextjs = framework === 'nextjs' || use.startsWith('@vercel/next');
|
||||
const ignoreRuntimes = slugToFramework.get(framework)?.ignoreRuntimes;
|
||||
|
||||
if (apiRoutes && apiRoutes.length > 0) {
|
||||
if (options.featHandleMiss) {
|
||||
@@ -971,11 +972,18 @@ function getRouteResult(
|
||||
}
|
||||
|
||||
rewriteRoutes.push(...dynamicRoutes);
|
||||
rewriteRoutes.push({
|
||||
src: '^/api(/.*)?$',
|
||||
status: 404,
|
||||
continue: true,
|
||||
});
|
||||
|
||||
if (typeof ignoreRuntimes === 'undefined') {
|
||||
// This route is only necessary to hide the directory listing
|
||||
// to avoid enumerating serverless function names.
|
||||
// But it causes issues in `vc dev` for frameworks that handle
|
||||
// their own functions such as redwood, so we ignore.
|
||||
rewriteRoutes.push({
|
||||
src: '^/api(/.*)?$',
|
||||
status: 404,
|
||||
continue: true,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
defaultRoutes.push(...apiRoutes);
|
||||
|
||||
|
||||
@@ -107,6 +107,13 @@ export interface BuildOptions {
|
||||
*/
|
||||
workPath: string;
|
||||
|
||||
/**
|
||||
* The "Root Directory" is assigned to the `workPath` so the `repoRootPath`
|
||||
* is the Git Repository Root. This is only relevant for Monorepos.
|
||||
* See https://vercel.com/blog/monorepos
|
||||
*/
|
||||
repoRootPath?: string;
|
||||
|
||||
/**
|
||||
* An arbitrary object passed by the user in the build definition defined
|
||||
* in `vercel.json`.
|
||||
|
||||
@@ -1080,7 +1080,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect(errorRoutes).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it('Using "Other" framework with Storybook should NOT autodetect Next.js', async () => {
|
||||
it('Using "Other" framework with Storybook should NOT autodetect Next.js for new projects', async () => {
|
||||
const pkg = {
|
||||
scripts: {
|
||||
dev: 'next dev',
|
||||
@@ -1104,6 +1104,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
const projectSettings = {
|
||||
framework: null, // Selected "Other" framework
|
||||
buildCommand: 'yarn build-storybook',
|
||||
createdAt: Date.parse('2020-07-01'),
|
||||
};
|
||||
|
||||
const { builders, errorRoutes } = await detectBuilders(files, pkg, {
|
||||
@@ -1125,6 +1126,41 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
expect((errorRoutes![0] as Source).status).toBe(404);
|
||||
});
|
||||
|
||||
it('Using "Other" framework should autodetect Next.js for old projects', async () => {
|
||||
const pkg = {
|
||||
scripts: {
|
||||
dev: 'next dev',
|
||||
build: 'next build',
|
||||
},
|
||||
dependencies: {
|
||||
next: '9.3.5',
|
||||
react: '16.13.1',
|
||||
'react-dom': '16.13.1',
|
||||
},
|
||||
};
|
||||
const files = ['package.json', 'pages/api/foo.js', 'index.html'];
|
||||
const projectSettings = {
|
||||
framework: null, // Selected "Other" framework
|
||||
createdAt: Date.parse('2020-02-01'),
|
||||
};
|
||||
|
||||
const { builders, errorRoutes } = await detectBuilders(files, pkg, {
|
||||
projectSettings,
|
||||
featHandleMiss,
|
||||
});
|
||||
|
||||
expect(builders).toEqual([
|
||||
{
|
||||
use: '@vercel/next',
|
||||
src: 'package.json',
|
||||
config: {
|
||||
zeroConfig: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(errorRoutes).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it('api + raw static', async () => {
|
||||
const files = ['api/endpoint.js', 'index.html', 'favicon.ico'];
|
||||
|
||||
@@ -1905,7 +1941,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('RedwoodJS should allow usage of non-js API', async () => {
|
||||
it('RedwoodJS should allow usage of non-js API and not add 404 api route', async () => {
|
||||
const files = [...redwoodFiles, 'api/golang.go', 'api/python.py'].sort();
|
||||
const projectSettings = {
|
||||
framework: 'redwoodjs',
|
||||
@@ -1953,13 +1989,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
check: true,
|
||||
},
|
||||
]);
|
||||
expect(rewriteRoutes).toStrictEqual([
|
||||
{
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
continue: true,
|
||||
},
|
||||
]);
|
||||
expect(rewriteRoutes).toStrictEqual([]);
|
||||
expect(errorRoutes).toStrictEqual([
|
||||
{
|
||||
status: 404,
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
declare module 'which-promise' {
|
||||
export default function (name: string): Promise<string>;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "20.0.0-canary.21",
|
||||
"version": "20.1.1-canary.3",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -61,14 +61,11 @@
|
||||
"node": ">= 10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.4.3-canary.4",
|
||||
"@vercel/go": "1.1.5-canary.0",
|
||||
"@vercel/next": "2.6.20",
|
||||
"@vercel/node": "1.7.5-canary.1",
|
||||
"@vercel/python": "1.2.2",
|
||||
"@vercel/redwood": "0.0.2-canary.7",
|
||||
"@vercel/ruby": "1.2.3",
|
||||
"@vercel/static-build": "0.17.7-canary.3",
|
||||
"@vercel/build-utils": "2.5.2-canary.2",
|
||||
"@vercel/go": "1.1.6",
|
||||
"@vercel/node": "1.8.2-canary.2",
|
||||
"@vercel/python": "1.2.3",
|
||||
"@vercel/ruby": "1.2.4",
|
||||
"update-notifier": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -103,9 +100,9 @@
|
||||
"@types/universal-analytics": "0.4.2",
|
||||
"@types/which": "1.3.2",
|
||||
"@types/write-json-file": "2.2.1",
|
||||
"@vercel/frameworks": "0.0.18-canary.5",
|
||||
"@vercel/frameworks": "0.1.1",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@zeit/fun": "0.11.2",
|
||||
"@zeit/ncc": "0.18.5",
|
||||
"@zeit/source-map-support": "0.6.2",
|
||||
"ajv": "6.12.2",
|
||||
"alpha-sort": "2.0.1",
|
||||
@@ -124,13 +121,9 @@
|
||||
"cpy": "7.2.0",
|
||||
"credit-card": "3.0.1",
|
||||
"date-fns": "1.29.0",
|
||||
"death": "1.1.0",
|
||||
"debug": "3.1.0",
|
||||
"deployment-type": "1.0.1",
|
||||
"docker-file-parser": "1.0.2",
|
||||
"dot": "1.1.3",
|
||||
"dotenv": "4.0.0",
|
||||
"download": "6.2.5",
|
||||
"email-prompt": "0.3.2",
|
||||
"email-validator": "1.1.1",
|
||||
"epipebomb": "1.0.0",
|
||||
@@ -141,8 +134,7 @@
|
||||
"fs-extra": "7.0.1",
|
||||
"get-port": "5.1.1",
|
||||
"glob": "7.1.2",
|
||||
"http-proxy": "1.17.0",
|
||||
"ignore": "4.0.6",
|
||||
"http-proxy": "1.18.1",
|
||||
"inquirer": "7.0.4",
|
||||
"is-port-reachable": "3.0.0",
|
||||
"is-url": "1.2.2",
|
||||
@@ -161,14 +153,11 @@
|
||||
"ora": "3.4.0",
|
||||
"pcre-to-regexp": "1.0.0",
|
||||
"pluralize": "7.0.0",
|
||||
"printf": "0.2.5",
|
||||
"progress": "2.0.3",
|
||||
"promisepipe": "3.0.0",
|
||||
"psl": "1.1.31",
|
||||
"qr-image": "3.2.0",
|
||||
"raw-body": "2.4.1",
|
||||
"read-pkg": "2.0.0",
|
||||
"rx-lite-aggregates": "4.0.8",
|
||||
"semver": "5.5.0",
|
||||
"serve-handler": "6.1.1",
|
||||
"sinon": "4.4.2",
|
||||
@@ -177,17 +166,14 @@
|
||||
"tar-fs": "1.16.3",
|
||||
"test-listen": "1.1.0",
|
||||
"text-table": "0.2.0",
|
||||
"then-sleep": "1.0.1",
|
||||
"through2": "2.0.3",
|
||||
"title": "3.4.1",
|
||||
"tmp-promise": "1.0.3",
|
||||
"tree-kill": "1.2.1",
|
||||
"tree-kill": "1.2.2",
|
||||
"ts-node": "8.3.0",
|
||||
"typescript": "3.9.3",
|
||||
"universal-analytics": "0.4.20",
|
||||
"utility-types": "2.1.0",
|
||||
"which": "2.0.2",
|
||||
"which-promise": "1.0.0",
|
||||
"write-json-file": "2.2.0",
|
||||
"xdg-app-paths": "5.1.0"
|
||||
}
|
||||
|
||||
@@ -49,18 +49,12 @@ async function main() {
|
||||
// Do the initial `ncc` build
|
||||
console.log();
|
||||
const src = join(dirRoot, 'src');
|
||||
const args = [
|
||||
'@zeit/ncc',
|
||||
'build',
|
||||
'--source-map',
|
||||
'--external',
|
||||
'update-notifier',
|
||||
];
|
||||
if (!isDev) {
|
||||
args.push('--minify');
|
||||
const args = ['ncc', 'build', '--external', 'update-notifier'];
|
||||
if (isDev) {
|
||||
args.push('--source-map');
|
||||
}
|
||||
args.push(src);
|
||||
await execa('npx', args, { stdio: 'inherit' });
|
||||
await execa('yarn', args, { stdio: 'inherit' });
|
||||
|
||||
// `ncc` has some issues with `@zeit/fun`'s runtime files:
|
||||
// - Executable bits on the `bootstrap` files appear to be lost:
|
||||
|
||||
@@ -157,13 +157,22 @@ const printDeploymentStatus = async (
|
||||
}
|
||||
|
||||
if (indications) {
|
||||
const indent = process.stdout.isTTY ? ' ' : ''; // if using emojis
|
||||
const newline = '\n';
|
||||
for (let indication of indications) {
|
||||
output.print(
|
||||
prependEmoji(
|
||||
`${chalk.dim(indication.payload)}`,
|
||||
emoji(indication.type)
|
||||
) + `\n`
|
||||
);
|
||||
const message =
|
||||
prependEmoji(chalk.dim(indication.payload), emoji(indication.type)) +
|
||||
newline;
|
||||
let link = '';
|
||||
if (indication.link)
|
||||
link =
|
||||
indent +
|
||||
chalk.dim(
|
||||
`${indication.action || 'Learn More'}: ${indication.link}`
|
||||
) +
|
||||
newline +
|
||||
newline;
|
||||
output.print(message + link);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -272,6 +281,7 @@ export default async function main(
|
||||
let { org, project, status } = link;
|
||||
let newProjectName = null;
|
||||
let rootDirectory = project ? project.rootDirectory : null;
|
||||
let sourceFilesOutsideRootDirectory = true;
|
||||
|
||||
if (status === 'not_linked') {
|
||||
const shouldStartSetup =
|
||||
@@ -329,6 +339,7 @@ export default async function main(
|
||||
} else {
|
||||
project = projectOrNewProjectName;
|
||||
rootDirectory = project.rootDirectory;
|
||||
sourceFilesOutsideRootDirectory = project.sourceFilesOutsideRootDirectory;
|
||||
|
||||
// we can already link the project
|
||||
await linkFolderToProject(
|
||||
@@ -345,7 +356,12 @@ export default async function main(
|
||||
}
|
||||
}
|
||||
|
||||
const sourcePath = rootDirectory ? join(path, rootDirectory) : path;
|
||||
// if we have `sourceFilesOutsideRootDirectory` set to `true`, we use the current path
|
||||
// and upload the entire directory.
|
||||
const sourcePath =
|
||||
rootDirectory && !sourceFilesOutsideRootDirectory
|
||||
? join(path, rootDirectory)
|
||||
: path;
|
||||
|
||||
if (
|
||||
rootDirectory &&
|
||||
@@ -364,7 +380,7 @@ export default async function main(
|
||||
// If Root Directory is used we'll try to read the config
|
||||
// from there instead and use it if it exists.
|
||||
if (rootDirectory) {
|
||||
const rootDirectoryConfig = readLocalConfig(sourcePath);
|
||||
const rootDirectoryConfig = readLocalConfig(join(path, rootDirectory));
|
||||
|
||||
if (rootDirectoryConfig) {
|
||||
debug(`Read local config from root directory (${rootDirectory})`);
|
||||
@@ -521,6 +537,11 @@ export default async function main(
|
||||
skipAutoDetectionConfirmation: autoConfirm,
|
||||
};
|
||||
|
||||
if (!localConfig.builds || localConfig.builds.length === 0) {
|
||||
// Only add projectSettings for zero config deployments
|
||||
createArgs.projectSettings = { sourceFilesOutsideRootDirectory };
|
||||
}
|
||||
|
||||
deployment = await createDeploy(
|
||||
output,
|
||||
now,
|
||||
@@ -542,6 +563,10 @@ export default async function main(
|
||||
projectSettings.rootDirectory = rootDirectory;
|
||||
}
|
||||
|
||||
if (typeof sourceFilesOutsideRootDirectory !== 'undefined') {
|
||||
projectSettings.sourceFilesOutsideRootDirectory = sourceFilesOutsideRootDirectory;
|
||||
}
|
||||
|
||||
const settings = await editProjectSettings(
|
||||
output,
|
||||
projectSettings,
|
||||
|
||||
@@ -111,12 +111,11 @@ export default async function inspect(
|
||||
` ${chalk.cyan('Created At')}\t\t\t${formatDate(domain.createdAt)}\n`
|
||||
);
|
||||
output.print(` ${chalk.cyan('Edge Network')}\t\tyes\n`);
|
||||
|
||||
if (renewalPrice && domain.boughtAt) {
|
||||
output.print(
|
||||
` ${chalk.cyan('Renewal Price')}\t\t$${renewalPrice} USD\n`
|
||||
);
|
||||
}
|
||||
output.print(
|
||||
` ${chalk.cyan('Renewal Price')}\t\t${
|
||||
domain.boughtAt && renewalPrice ? `$${renewalPrice} USD` : chalk.gray('-')
|
||||
}\n`
|
||||
);
|
||||
|
||||
output.print('\n');
|
||||
|
||||
|
||||
@@ -66,7 +66,10 @@ export default async function ls(
|
||||
|
||||
const cancelWait = wait(`Fetching Domains under ${chalk.bold(contextName)}`);
|
||||
|
||||
const { domains, pagination } = await getDomains(client).finally(() => {
|
||||
const { domains, pagination } = await getDomains(
|
||||
client,
|
||||
nextTimestamp
|
||||
).finally(() => {
|
||||
cancelWait();
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ try {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
import 'core-js/modules/es7.symbol.async-iterator';
|
||||
import { join } from 'path';
|
||||
import { existsSync, lstatSync } from 'fs';
|
||||
import sourceMap from '@zeit/source-map-support';
|
||||
@@ -135,7 +134,7 @@ const main = async argv_ => {
|
||||
// (as in: `vercel ls`)
|
||||
const targetOrSubcommand = argv._[2];
|
||||
|
||||
if (notifier.update && isTTY) {
|
||||
if (notifier.update && notifier.update.latest !== pkg.version && isTTY) {
|
||||
const { latest } = notifier.update;
|
||||
console.log(
|
||||
info(
|
||||
|
||||
@@ -1475,34 +1475,6 @@ export default class DevServer {
|
||||
routeResult.status = prevStatus;
|
||||
}
|
||||
|
||||
if (!match && errorRoutes.length > 0) {
|
||||
// error phase
|
||||
const routeResultForError = await devRouter(
|
||||
getReqUrl(routeResult),
|
||||
req.method,
|
||||
errorRoutes,
|
||||
this,
|
||||
nowConfig,
|
||||
routeResult.headers,
|
||||
[],
|
||||
'error'
|
||||
);
|
||||
|
||||
const matchForError = await findBuildMatch(
|
||||
this.buildMatches,
|
||||
this.files,
|
||||
routeResultForError.dest,
|
||||
this,
|
||||
nowConfig
|
||||
);
|
||||
|
||||
if (matchForError) {
|
||||
// error phase only applies if the file was found
|
||||
routeResult = routeResultForError;
|
||||
match = matchForError;
|
||||
}
|
||||
}
|
||||
|
||||
statusCode = routeResult.status;
|
||||
|
||||
if (match) {
|
||||
@@ -1511,6 +1483,52 @@ export default class DevServer {
|
||||
}
|
||||
}
|
||||
|
||||
if (!match && routeResult && errorRoutes.length > 0) {
|
||||
// error phase
|
||||
const routeResultForError = await devRouter(
|
||||
getReqUrl(routeResult),
|
||||
req.method,
|
||||
errorRoutes,
|
||||
this,
|
||||
nowConfig,
|
||||
routeResult.headers,
|
||||
[],
|
||||
'error'
|
||||
);
|
||||
const { matched_route } = routeResultForError;
|
||||
|
||||
const matchForError = await findBuildMatch(
|
||||
this.buildMatches,
|
||||
this.files,
|
||||
routeResultForError.dest,
|
||||
this,
|
||||
nowConfig
|
||||
);
|
||||
|
||||
if (matchForError) {
|
||||
debug(`Route match detected in error phase, breaking loop`);
|
||||
routeResult = routeResultForError;
|
||||
statusCode = routeResultForError.status;
|
||||
match = matchForError;
|
||||
} else if (matched_route && matched_route.src && !matched_route.dest) {
|
||||
debug(
|
||||
'Route without `dest` detected in error phase, attempting to exit early'
|
||||
);
|
||||
if (
|
||||
await this.exitWithStatus(
|
||||
matchForError,
|
||||
routeResultForError,
|
||||
'error',
|
||||
req,
|
||||
res,
|
||||
nowRequestId
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!routeResult) {
|
||||
throw new Error('Expected Route Result but none was found.');
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { resolve } from 'path';
|
||||
import ignore from 'ignore';
|
||||
import _glob, { IOptions as GlobOptions } from 'glob';
|
||||
import fs from 'fs-extra';
|
||||
import { getVercelIgnore } from '@vercel/client';
|
||||
import IGNORED from './ignored';
|
||||
import uniqueStrings from './unique-strings';
|
||||
import getLocalConfigPath from './config/local-path';
|
||||
import { Output } from './output/create-output';
|
||||
import { NowConfig } from './dev/types';
|
||||
|
||||
type NullableString = string | null;
|
||||
|
||||
@@ -33,103 +29,6 @@ async function glob(pattern: string, options: GlobOptions): Promise<string[]> {
|
||||
});
|
||||
}
|
||||
|
||||
interface WalkOptions {
|
||||
output: Output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will recursivly walk through a directory and return an array of the files found within.
|
||||
* @param {string} dir the directory to walk
|
||||
* @param {string} path the path to this directory
|
||||
* @param {Array[string]} filelist a list of files so far identified
|
||||
* @param {Object} options
|
||||
* - `output` {Object} "output" helper object
|
||||
* @returns {Array}
|
||||
*/
|
||||
async function walk(
|
||||
dir: string,
|
||||
path: string,
|
||||
filelist: string[] = [],
|
||||
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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return an array containing the expaneded list of all files included in the whitelist.
|
||||
* @param {Array[string]} whitelist array of files and directories to include.
|
||||
* @param {string} path the path of the deployment.
|
||||
* @param {Object} options
|
||||
* - `output` {Object} "output" helper object
|
||||
* @returns {Array} the expanded list of whitelisted files.
|
||||
*/
|
||||
const getFilesInWhitelist = async function (
|
||||
whitelist: string[],
|
||||
path: string,
|
||||
opts: FilesInWhitelistOptions
|
||||
) {
|
||||
const { debug } = opts.output;
|
||||
const files: string[] = [];
|
||||
|
||||
await Promise.all(
|
||||
whitelist.map(async (file: string) => {
|
||||
file = asAbsolute(file, path);
|
||||
try {
|
||||
const fileStat = await fs.stat(file);
|
||||
if (fileStat.isDirectory()) {
|
||||
const dirFiles = await walk(file, path, [], opts);
|
||||
files.push(...dirFiles);
|
||||
} else {
|
||||
files.push(file);
|
||||
}
|
||||
} catch (e) {
|
||||
debug(`Ignoring invalid file ${file}`);
|
||||
}
|
||||
})
|
||||
);
|
||||
return files;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove leading `./` from the beginning of ignores
|
||||
* because ignore doesn't like them :|
|
||||
*/
|
||||
|
||||
const clearRelative = function (str: string) {
|
||||
return str.replace(/(\n|^)\.\//g, '$1');
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the contents of a file if it exists.
|
||||
*
|
||||
* @return {String} results or `''`
|
||||
*/
|
||||
|
||||
const maybeRead = async function <T>(path: string, default_: T) {
|
||||
try {
|
||||
return await fs.readFile(path, 'utf8');
|
||||
} catch (err) {
|
||||
return default_;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform relative paths into absolutes,
|
||||
* and maintains absolutes as such.
|
||||
@@ -215,100 +114,6 @@ export async function staticFiles(
|
||||
return uniqueStrings(files);
|
||||
}
|
||||
|
||||
interface NpmOptions {
|
||||
hasNowJson: boolean;
|
||||
output: Output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of files in the given
|
||||
* directory that are subject to be
|
||||
* synchronized for npm.
|
||||
*
|
||||
* @param {String} full path to directory
|
||||
* @param {String} contents of `package.json` to avoid lookup
|
||||
* @param {Object} options:
|
||||
* - `limit` {Number|null} byte limit
|
||||
* - `output` {Object} "output" helper object
|
||||
* @return {Array} comprehensive list of paths to sync
|
||||
*/
|
||||
export async function npm(
|
||||
path: string,
|
||||
pkg: { files?: string[]; now?: { files?: string[] } } = {},
|
||||
nowConfig: NowConfig = {},
|
||||
{ hasNowJson = false, output }: NpmOptions
|
||||
) {
|
||||
const { debug, time } = output;
|
||||
const whitelist = nowConfig.files || pkg.files || (pkg.now && pkg.now.files);
|
||||
let files: string[] = [];
|
||||
|
||||
if (whitelist) {
|
||||
files = await getFilesInWhitelist(whitelist, path, { output });
|
||||
} else {
|
||||
// The package.json `files` whitelist still
|
||||
// honors ignores: https://docs.npmjs.com/files/package.json#files
|
||||
const search_ = ['.'];
|
||||
// Convert all filenames into absolute paths
|
||||
const search = Array.prototype.concat.apply(
|
||||
[],
|
||||
await Promise.all(
|
||||
search_.map(file =>
|
||||
glob(file, { cwd: path, absolute: true, dot: true })
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// Compile list of ignored patterns and files
|
||||
const npmIgnore = await maybeRead(resolve(path, '.npmignore'), null);
|
||||
|
||||
const filter = ignore()
|
||||
.add(
|
||||
`${IGNORED}\n${clearRelative(
|
||||
npmIgnore === null
|
||||
? await maybeRead(resolve(path, '.gitignore'), '')
|
||||
: npmIgnore
|
||||
)}`
|
||||
)
|
||||
.createFilter();
|
||||
|
||||
const prefixLength = path.length + 1;
|
||||
|
||||
const accepts = (file: string) => {
|
||||
const relativePath = file.substr(prefixLength);
|
||||
|
||||
if (relativePath === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
const accepted = filter(relativePath);
|
||||
if (!accepted) {
|
||||
debug(`Ignoring ${file}`);
|
||||
}
|
||||
return accepted;
|
||||
};
|
||||
|
||||
// Locate files
|
||||
files = await time(
|
||||
`Locating files ${path}`,
|
||||
explode(search, {
|
||||
accepts,
|
||||
output,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Always include manifest as npm does not allow ignoring it
|
||||
// source: https://docs.npmjs.com/files/package.json#files
|
||||
files.push(asAbsolute('package.json', path));
|
||||
|
||||
if (hasNowJson) {
|
||||
files.push(asAbsolute(getLocalConfigPath(path), path));
|
||||
}
|
||||
|
||||
// Get files
|
||||
return uniqueStrings(files);
|
||||
}
|
||||
|
||||
interface ExplodeOptions {
|
||||
accepts: (file: string) => boolean;
|
||||
output: Output;
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Base `.gitignore` to which we add entries
|
||||
// supplied by the user
|
||||
export default `.hg
|
||||
.git
|
||||
.gitmodules
|
||||
.svn
|
||||
.cache
|
||||
.next
|
||||
.now
|
||||
.vercel
|
||||
.npmignore
|
||||
.dockerignore
|
||||
.gitignore
|
||||
.*.swp
|
||||
.DS_Store
|
||||
.wafpicke-*
|
||||
.lock-wscript
|
||||
.env.local
|
||||
.env.*.local
|
||||
.venv
|
||||
npm-debug.log
|
||||
config.gypi
|
||||
node_modules
|
||||
__pycache__
|
||||
venv
|
||||
CVS`;
|
||||
@@ -59,6 +59,7 @@ export default async function setupAndLink(
|
||||
const isTTY = process.stdout.isTTY;
|
||||
const quiet = !isTTY;
|
||||
let rootDirectory: string | null = null;
|
||||
let sourceFilesOutsideRootDirectory = true;
|
||||
let newProjectName: string;
|
||||
let org;
|
||||
|
||||
@@ -129,7 +130,13 @@ export default async function setupAndLink(
|
||||
);
|
||||
return { status: 'linked', org, project };
|
||||
}
|
||||
const sourcePath = rootDirectory ? join(path, rootDirectory) : path;
|
||||
|
||||
// if we have `sourceFilesOutsideRootDirectory` set to `true`, we use the current path
|
||||
// and upload the entire directory.
|
||||
const sourcePath =
|
||||
rootDirectory && !sourceFilesOutsideRootDirectory
|
||||
? join(path, rootDirectory)
|
||||
: path;
|
||||
|
||||
if (
|
||||
rootDirectory &&
|
||||
@@ -174,6 +181,11 @@ export default async function setupAndLink(
|
||||
skipAutoDetectionConfirmation: false,
|
||||
};
|
||||
|
||||
if (!localConfig.builds || localConfig.builds.length === 0) {
|
||||
// Only add projectSettings for zero config deployments
|
||||
createArgs.projectSettings = { sourceFilesOutsideRootDirectory };
|
||||
}
|
||||
|
||||
const deployment = await createDeploy(
|
||||
output,
|
||||
now,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import chalk from 'chalk';
|
||||
import printf from 'printf';
|
||||
|
||||
const printLine = (data, sizes) =>
|
||||
data.reduce((line, col, i) => line + printf(`%-${sizes[i]}s`, col), '');
|
||||
data.reduce((line, col, i) => line + col.padEnd(sizes[i]), '');
|
||||
|
||||
// Print a table
|
||||
export default (fieldNames = [], data = [], margins = []) => {
|
||||
|
||||
@@ -6,13 +6,13 @@ import { isIP } from 'net';
|
||||
import { join, resolve, delimiter } from 'path';
|
||||
import _execa from 'execa';
|
||||
import fetch from 'node-fetch';
|
||||
import sleep from 'then-sleep';
|
||||
import retry from 'async-retry';
|
||||
import { satisfies } from 'semver';
|
||||
import { getDistTag } from '../../src/util/get-dist-tag';
|
||||
import { version as cliVersion } from '../../package.json';
|
||||
import { fetchTokenWithRetry } from '../../../../test/lib/deployment/now-deploy';
|
||||
|
||||
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
const isCanary = () => getDistTag(cliVersion) === 'canary';
|
||||
|
||||
let port = 3000;
|
||||
|
||||
@@ -241,6 +241,24 @@ module.exports = async function prepare(session) {
|
||||
},
|
||||
}),
|
||||
},
|
||||
'zero-config-next-js-functions-warning': {
|
||||
'pages/index.js':
|
||||
'export default () => <div><h1>Vercel CLI test</h1><p>Zero-config + Next.js</p></div>',
|
||||
'vercel.json':
|
||||
'{"version":2,"functions":{"pages/index.js":{"runtime": "@vercel/php@0.1.0"}}}',
|
||||
'package.json': JSON.stringify({
|
||||
scripts: {
|
||||
dev: 'next',
|
||||
start: 'next start',
|
||||
build: 'next build',
|
||||
},
|
||||
dependencies: {
|
||||
next: 'latest',
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
},
|
||||
}),
|
||||
},
|
||||
'lambda-with-128-memory': {
|
||||
'api/memory.js': `
|
||||
module.exports = (req, res) => {
|
||||
|
||||
29
packages/now-cli/test/integration.js
vendored
29
packages/now-cli/test/integration.js
vendored
@@ -1052,7 +1052,7 @@ test('domains inspect', async t => {
|
||||
}
|
||||
);
|
||||
|
||||
t.true(!stderr.includes(`Renewal Price`));
|
||||
t.true(stderr.includes(`Renewal Price`));
|
||||
t.is(exitCode, 0, formatOutput({ stdout, stderr }));
|
||||
|
||||
{
|
||||
@@ -2219,6 +2219,33 @@ test('create zero-config deployment', async t => {
|
||||
);
|
||||
});
|
||||
|
||||
test('next unsupported functions config shows warning link', async t => {
|
||||
const fixturePath = fixture('zero-config-next-js-functions-warning');
|
||||
const output = await execute([
|
||||
fixturePath,
|
||||
'--force',
|
||||
'--public',
|
||||
'--confirm',
|
||||
]);
|
||||
|
||||
console.log('isCanary', isCanary);
|
||||
console.log(output.stderr);
|
||||
console.log(output.stdout);
|
||||
console.log(output.exitCode);
|
||||
|
||||
t.is(output.exitCode, 0, formatOutput(output));
|
||||
t.regex(
|
||||
output.stderr,
|
||||
/Ignoring function property `runtime`\. When using Next\.js, only `memory` and `maxDuration` can be used\./gm,
|
||||
formatOutput(output)
|
||||
);
|
||||
t.regex(
|
||||
output.stderr,
|
||||
/Learn More: https:\/\/vercel\.link\/functions-property-next/gm,
|
||||
formatOutput(output)
|
||||
);
|
||||
});
|
||||
|
||||
test('vercel secret add', async t => {
|
||||
context.secretName = `my-secret-${Date.now().toString(36)}`;
|
||||
const value = 'https://my-secret-endpoint.com';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "8.2.2-canary.8",
|
||||
"version": "9.0.2-canary.2",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -26,7 +26,6 @@
|
||||
"@types/node": "12.0.4",
|
||||
"@types/node-fetch": "2.5.4",
|
||||
"@types/recursive-readdir": "2.2.0",
|
||||
"@zeit/ncc": "0.18.5",
|
||||
"typescript": "3.9.3"
|
||||
},
|
||||
"jest": {
|
||||
@@ -38,7 +37,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.4.3-canary.4",
|
||||
"@vercel/build-utils": "2.5.2-canary.2",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "3.0.0",
|
||||
|
||||
@@ -19,7 +19,12 @@ async function* postDeployment(
|
||||
files: Map<string, DeploymentFile>,
|
||||
clientOptions: NowClientOptions,
|
||||
deploymentOptions: DeploymentOptions
|
||||
): AsyncIterableIterator<{ type: DeploymentEventType; payload: any }> {
|
||||
): AsyncIterableIterator<{
|
||||
type: DeploymentEventType;
|
||||
payload: any;
|
||||
action?: string;
|
||||
link?: string;
|
||||
}> {
|
||||
const debug = createDebug(clientOptions.debug);
|
||||
const preparedFiles = prepareFiles(files, clientOptions);
|
||||
const apiDeployments = getApiDeploymentsUrl(deploymentOptions);
|
||||
@@ -63,19 +68,19 @@ async function* postDeployment(
|
||||
};
|
||||
}
|
||||
|
||||
for (const [name, value] of response.headers.entries()) {
|
||||
if (name.startsWith('x-now-warning-')) {
|
||||
debug('Deployment created with a warning:', value);
|
||||
yield { type: 'warning', payload: value };
|
||||
}
|
||||
const indications = new Set(['warning', 'notice', 'tip']);
|
||||
const regex = /^x-(?:vercel|now)-(warning|notice|tip)-(.*)$/;
|
||||
for (const [name, payload] of response.headers.entries()) {
|
||||
const match = name.match(regex);
|
||||
if (match) {
|
||||
const [, type, identifier] = match;
|
||||
const action = response.headers.get(`x-vercel-action-${identifier}`);
|
||||
const link = response.headers.get(`x-vercel-link-${identifier}`);
|
||||
|
||||
if (name.startsWith('x-now-notice-')) {
|
||||
debug('Deployment created with a notice:', value);
|
||||
yield { type: 'notice', payload: value };
|
||||
}
|
||||
if (name.startsWith('x-now-tip-')) {
|
||||
debug('Deployment created with a tip:', value);
|
||||
yield { type: 'tip', payload: value };
|
||||
if (indications.has(type)) {
|
||||
debug(`Deployment created with a ${type}: `, payload);
|
||||
yield { type, payload, action, link };
|
||||
}
|
||||
}
|
||||
}
|
||||
yield { type: 'created', payload: deployment };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/go",
|
||||
"version": "1.1.5-canary.0",
|
||||
"version": "1.1.6",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
|
||||
@@ -25,6 +25,7 @@
|
||||
"@types/fs-extra": "^5.0.5",
|
||||
"@types/node-fetch": "^2.3.0",
|
||||
"@types/tar": "^4.0.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"async-retry": "1.3.1",
|
||||
"execa": "^1.0.0",
|
||||
"fs-extra": "^7.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "2.6.20",
|
||||
"version": "2.6.25-canary.2",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
|
||||
@@ -26,7 +26,7 @@
|
||||
"@types/resolve-from": "5.0.1",
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/yazl": "2.4.1",
|
||||
"@zeit/node-file-trace": "0.8.2",
|
||||
"@vercel/nft": "0.9.2",
|
||||
"async-sema": "3.0.1",
|
||||
"buffer-crc32": "0.2.13",
|
||||
"escape-string-regexp": "3.0.0",
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
convertRedirects,
|
||||
convertRewrites,
|
||||
} from '@vercel/routing-utils/dist/superstatic';
|
||||
import { nodeFileTrace, NodeFileTraceReasons } from '@zeit/node-file-trace';
|
||||
import { nodeFileTrace, NodeFileTraceReasons } from '@vercel/nft';
|
||||
import { Sema } from 'async-sema';
|
||||
import { ChildProcess, fork } from 'child_process';
|
||||
import escapeStringRegexp from 'escape-string-regexp';
|
||||
@@ -213,6 +213,7 @@ function startDevServer(entryPath: string, runtimeEnv: EnvConfig) {
|
||||
export const build = async ({
|
||||
files,
|
||||
workPath,
|
||||
repoRootPath,
|
||||
entrypoint,
|
||||
config = {} as Config,
|
||||
meta = {} as BuildParamsMeta,
|
||||
@@ -231,6 +232,7 @@ export const build = async ({
|
||||
const entryPath = path.join(workPath, entryDirectory);
|
||||
const outputDirectory = config.outputDirectory || '.next';
|
||||
const dotNextStatic = path.join(entryPath, outputDirectory, 'static');
|
||||
const baseDir = repoRootPath || workPath;
|
||||
|
||||
await download(files, workPath, meta);
|
||||
|
||||
@@ -240,7 +242,7 @@ export const build = async ({
|
||||
const spawnOpts = getSpawnOptions(meta, nodeVersion);
|
||||
|
||||
const nowJsonPath = await findUp(['now.json', 'vercel.json'], {
|
||||
cwd: path.join(workPath, path.dirname(entrypoint)),
|
||||
cwd: entryPath,
|
||||
});
|
||||
|
||||
let hasLegacyRoutes = false;
|
||||
@@ -623,7 +625,8 @@ export const build = async ({
|
||||
},
|
||||
|
||||
// error handling
|
||||
...(output[path.join('./', entryDirectory, '404')]
|
||||
...(output[path.join('./', entryDirectory, '404')] ||
|
||||
output[path.join('./', entryDirectory, '404/index')]
|
||||
? [
|
||||
{ handle: 'error' } as Handler,
|
||||
|
||||
@@ -755,7 +758,10 @@ export const build = async ({
|
||||
};
|
||||
|
||||
const lambdaOptions = await getLambdaOptionsFromFunction({
|
||||
sourceFile: await getSourceFilePathFromPage({ workPath, page }),
|
||||
sourceFile: await getSourceFilePathFromPage({
|
||||
workPath: entryPath,
|
||||
page,
|
||||
}),
|
||||
config,
|
||||
});
|
||||
|
||||
@@ -938,12 +944,18 @@ export const build = async ({
|
||||
const {
|
||||
fileList: apiFileList,
|
||||
reasons: apiReasons,
|
||||
} = await nodeFileTrace(apiPages, { base: workPath });
|
||||
} = await nodeFileTrace(apiPages, {
|
||||
base: baseDir,
|
||||
processCwd: entryPath,
|
||||
});
|
||||
|
||||
const {
|
||||
fileList,
|
||||
reasons: nonApiReasons,
|
||||
} = await nodeFileTrace(nonApiPages, { base: workPath });
|
||||
const { fileList, reasons: nonApiReasons } = await nodeFileTrace(
|
||||
nonApiPages,
|
||||
{
|
||||
base: baseDir,
|
||||
processCwd: entryPath,
|
||||
}
|
||||
);
|
||||
|
||||
debug(`node-file-trace result for pages: ${fileList}`);
|
||||
|
||||
@@ -961,7 +973,7 @@ export const build = async ({
|
||||
// Initial files are manually added to the lambda later
|
||||
return;
|
||||
}
|
||||
const filePath = path.join(workPath, file);
|
||||
const filePath = path.join(baseDir, file);
|
||||
|
||||
if (!lstatResults[filePath]) {
|
||||
lstatResults[filePath] = lstatSema
|
||||
@@ -972,7 +984,7 @@ export const build = async ({
|
||||
const { mode } = await lstatResults[filePath];
|
||||
|
||||
files[file] = new FileFsRef({
|
||||
fsPath: path.join(workPath, file),
|
||||
fsPath: path.join(baseDir, file),
|
||||
mode,
|
||||
});
|
||||
};
|
||||
@@ -1157,7 +1169,11 @@ export const build = async ({
|
||||
const {
|
||||
pseudoLayer: pageLayer,
|
||||
pseudoLayerBytes: pageLayerBytes,
|
||||
} = await createPseudoLayer({ [pageFileName]: pages[page] });
|
||||
} = await createPseudoLayer({
|
||||
[path.join(path.relative(baseDir, entryPath), pageFileName)]: pages[
|
||||
page
|
||||
],
|
||||
});
|
||||
|
||||
currentLambdaGroup.pages[outputName] = {
|
||||
pageLayer,
|
||||
@@ -1193,21 +1209,31 @@ export const build = async ({
|
||||
}
|
||||
|
||||
const pageFileName = path.normalize(
|
||||
path.relative(workPath, pages[page].fsPath)
|
||||
path.relative(entryPath, pages[page].fsPath)
|
||||
);
|
||||
|
||||
const launcher = launcherData.replace(
|
||||
/__LAUNCHER_PAGE_PATH__/g,
|
||||
JSON.stringify(requiresTracing ? `./${pageFileName}` : './page')
|
||||
);
|
||||
const launcherFiles: { [name: string]: FileFsRef | FileBlob } = {
|
||||
'now__bridge.js': new FileFsRef({
|
||||
[path.join(
|
||||
path.relative(baseDir, entryPath),
|
||||
'now__bridge.js'
|
||||
)]: new FileFsRef({
|
||||
fsPath: path.join(__dirname, 'now__bridge.js'),
|
||||
}),
|
||||
'now__launcher.js': new FileBlob({ data: launcher }),
|
||||
[path.join(
|
||||
path.relative(baseDir, entryPath),
|
||||
'now__launcher.js'
|
||||
)]: new FileBlob({ data: launcher }),
|
||||
};
|
||||
|
||||
const lambdaOptions = await getLambdaOptionsFromFunction({
|
||||
sourceFile: await getSourceFilePathFromPage({ workPath, page }),
|
||||
sourceFile: await getSourceFilePathFromPage({
|
||||
workPath: entryPath,
|
||||
page,
|
||||
}),
|
||||
config,
|
||||
});
|
||||
|
||||
@@ -1217,10 +1243,16 @@ export const build = async ({
|
||||
lambdas[outputName] = await createLambdaFromPseudoLayers({
|
||||
files: {
|
||||
...launcherFiles,
|
||||
[requiresTracing ? pageFileName : 'page.js']: pages[page],
|
||||
[path.join(
|
||||
path.relative(baseDir, entryPath),
|
||||
pageFileName
|
||||
)]: pages[page],
|
||||
},
|
||||
layers: isApiPage(pageFileName) ? apiPseudoLayers : pseudoLayers,
|
||||
handler: 'now__launcher.launcher',
|
||||
handler: path.join(
|
||||
path.relative(baseDir, entryPath),
|
||||
'now__launcher.launcher'
|
||||
),
|
||||
runtime: nodeVersion.runtime,
|
||||
...lambdaOptions,
|
||||
});
|
||||
@@ -1230,7 +1262,7 @@ export const build = async ({
|
||||
...launcherFiles,
|
||||
...assets,
|
||||
...tracedFiles,
|
||||
[requiresTracing ? pageFileName : 'page.js']: pages[page],
|
||||
['page.js']: pages[page],
|
||||
},
|
||||
handler: 'now__launcher.launcher',
|
||||
runtime: nodeVersion.runtime,
|
||||
@@ -1371,10 +1403,16 @@ export const build = async ({
|
||||
`
|
||||
);
|
||||
const launcherFiles: { [name: string]: FileFsRef | FileBlob } = {
|
||||
'now__bridge.js': new FileFsRef({
|
||||
[path.join(
|
||||
path.relative(baseDir, entryPath),
|
||||
'now__bridge.js'
|
||||
)]: new FileFsRef({
|
||||
fsPath: path.join(__dirname, 'now__bridge.js'),
|
||||
}),
|
||||
'now__launcher.js': new FileBlob({ data: launcher }),
|
||||
[path.join(
|
||||
path.relative(baseDir, entryPath),
|
||||
'now__launcher.js'
|
||||
)]: new FileBlob({ data: launcher }),
|
||||
};
|
||||
|
||||
const pageLayers: PseudoLayer[] = [];
|
||||
@@ -1396,7 +1434,10 @@ export const build = async ({
|
||||
...(group.isApiLambda ? apiPseudoLayers : pseudoLayers),
|
||||
...pageLayers,
|
||||
],
|
||||
handler: 'now__launcher.launcher',
|
||||
handler: path.join(
|
||||
path.relative(baseDir, entryPath),
|
||||
'now__launcher.launcher'
|
||||
),
|
||||
runtime: nodeVersion.runtime,
|
||||
});
|
||||
} else {
|
||||
@@ -1408,7 +1449,10 @@ export const build = async ({
|
||||
...assets,
|
||||
},
|
||||
layers: pageLayers,
|
||||
handler: 'now__launcher.launcher',
|
||||
handler: path.join(
|
||||
path.relative(baseDir, entryPath),
|
||||
'now__launcher.launcher'
|
||||
),
|
||||
runtime: nodeVersion.runtime,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// The Next.js builder can emit the project in a subdirectory depending on how
|
||||
// many folder levels of `node_modules` are traced. To ensure `process.cwd()`
|
||||
// returns the proper path, we change the directory to the folder with the
|
||||
// launcher. This mimics `yarn workspace run` behavior.
|
||||
process.chdir(__dirname);
|
||||
|
||||
if (!process.env.NODE_ENV) {
|
||||
const region = process.env.VERCEL_REGION || process.env.NOW_REGION;
|
||||
process.env.NODE_ENV = region === 'dev1' ? 'development' : 'production';
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
// The Next.js builder can emit the project in a subdirectory depending on how
|
||||
// many folder levels of `node_modules` are traced. To ensure `process.cwd()`
|
||||
// returns the proper path, we change the directory to the folder with the
|
||||
// launcher. This mimics `yarn workspace run` behavior.
|
||||
process.chdir(__dirname);
|
||||
|
||||
if (!process.env.NODE_ENV) {
|
||||
const region = process.env.VERCEL_REGION || process.env.NOW_REGION;
|
||||
process.env.NODE_ENV = region === 'dev1' ? 'development' : 'production';
|
||||
|
||||
52
packages/now-next/test/fixtures/00-root-optional-revalidate/additional.js
vendored
Normal file
52
packages/now-next/test/fixtures/00-root-optional-revalidate/additional.js
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
/* eslint-env jest */
|
||||
const fetch = require('node-fetch');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
const waitFor = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
module.exports = function (ctx) {
|
||||
const getProps = async path => {
|
||||
const html = await fetch(`${ctx.deploymentUrl}/${path}`).then(res =>
|
||||
res.text()
|
||||
);
|
||||
|
||||
const $ = cheerio.load(html);
|
||||
return JSON.parse($('#props').text());
|
||||
};
|
||||
|
||||
it('should render / correctly', async () => {
|
||||
const props = await getProps('/', { params: {} });
|
||||
expect(props.params).toEqual({});
|
||||
|
||||
await waitFor(2000);
|
||||
await getProps('/');
|
||||
|
||||
const newProps = await getProps('/', { params: {} });
|
||||
expect(newProps.params).toEqual({});
|
||||
expect(props.random).not.toBe(newProps.random);
|
||||
});
|
||||
|
||||
it('should render /a correctly', async () => {
|
||||
const props = await getProps('/a');
|
||||
expect(props.params).toEqual({ slug: ['a'] });
|
||||
|
||||
await waitFor(2000);
|
||||
await getProps('/a');
|
||||
|
||||
const newProps = await getProps('/a');
|
||||
expect(newProps.params).toEqual({ slug: ['a'] });
|
||||
expect(props.random).not.toBe(newProps.random);
|
||||
});
|
||||
|
||||
it('should render /hello/world correctly', async () => {
|
||||
const props = await getProps('/hello/world');
|
||||
expect(props.params).toEqual({ slug: ['hello', 'world'] });
|
||||
|
||||
await waitFor(2000);
|
||||
await getProps('/hello/world');
|
||||
|
||||
const newProps = await getProps('/hello/world');
|
||||
expect(newProps.params).toEqual({ slug: ['hello', 'world'] });
|
||||
expect(props.random).not.toBe(newProps.random);
|
||||
});
|
||||
};
|
||||
15
packages/now-next/test/fixtures/00-root-optional-revalidate/now.json
vendored
Normal file
15
packages/now-next/test/fixtures/00-root-optional-revalidate/now.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next"
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/non-existent",
|
||||
"status": 404
|
||||
}
|
||||
]
|
||||
}
|
||||
7
packages/now-next/test/fixtures/00-root-optional-revalidate/package.json
vendored
Normal file
7
packages/now-next/test/fixtures/00-root-optional-revalidate/package.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
24
packages/now-next/test/fixtures/00-root-optional-revalidate/pages/[[...slug]].js
vendored
Normal file
24
packages/now-next/test/fixtures/00-root-optional-revalidate/pages/[[...slug]].js
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
export default function Home(props) {
|
||||
return <pre id="props">{JSON.stringify(props)}</pre>;
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return {
|
||||
paths: [
|
||||
{ params: { slug: false } },
|
||||
{ params: { slug: ['a'] } },
|
||||
{ params: { slug: ['hello', 'world'] } },
|
||||
],
|
||||
fallback: false,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getStaticProps({ params }) {
|
||||
return {
|
||||
props: {
|
||||
params,
|
||||
random: Math.random(),
|
||||
},
|
||||
revalidate: 1,
|
||||
};
|
||||
}
|
||||
6
packages/now-next/test/fixtures/00-trailing-slash-add-export/next.config.js
vendored
Normal file
6
packages/now-next/test/fixtures/00-trailing-slash-add-export/next.config.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
trailingSlash: true,
|
||||
};
|
||||
26
packages/now-next/test/fixtures/00-trailing-slash-add-export/now.json
vendored
Normal file
26
packages/now-next/test/fixtures/00-trailing-slash-add-export/now.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "package.json", "use": "@vercel/next" }],
|
||||
"probes": [
|
||||
{ "path": "/foo/", "status": 200, "mustContain": "foo page" },
|
||||
{
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"path": "/foo",
|
||||
"status": 308,
|
||||
"responseHeaders": {
|
||||
"refresh": "/url=/foo/$/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/non-existent",
|
||||
"status": 404,
|
||||
"mustContain": "oops 404"
|
||||
},
|
||||
{
|
||||
"path": "/non-existent/",
|
||||
"status": 404,
|
||||
"fetchOptions": { "redirect": "manual" },
|
||||
"mustContain": "oops 404"
|
||||
}
|
||||
]
|
||||
}
|
||||
10
packages/now-next/test/fixtures/00-trailing-slash-add-export/package.json
vendored
Normal file
10
packages/now-next/test/fixtures/00-trailing-slash-add-export/package.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "next build && next export"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
}
|
||||
}
|
||||
1
packages/now-next/test/fixtures/00-trailing-slash-add-export/pages/404.js
vendored
Normal file
1
packages/now-next/test/fixtures/00-trailing-slash-add-export/pages/404.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export default () => 'oops 404';
|
||||
3
packages/now-next/test/fixtures/00-trailing-slash-add-export/pages/foo.js
vendored
Normal file
3
packages/now-next/test/fixtures/00-trailing-slash-add-export/pages/foo.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Page() {
|
||||
return <p>foo page</p>;
|
||||
}
|
||||
@@ -40,6 +40,6 @@ declare namespace ncc {
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@zeit/ncc' {
|
||||
declare module '@vercel/ncc' {
|
||||
export = ncc;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/node",
|
||||
"version": "1.7.5-canary.1",
|
||||
"version": "1.8.2-canary.2",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
||||
@@ -32,8 +32,8 @@
|
||||
"@types/cookie": "0.3.3",
|
||||
"@types/etag": "1.8.0",
|
||||
"@types/test-listen": "1.1.0",
|
||||
"@zeit/ncc": "0.20.4",
|
||||
"@zeit/node-file-trace": "0.8.2",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/nft": "0.9.2",
|
||||
"content-type": "1.0.4",
|
||||
"cookie": "0.4.0",
|
||||
"etag": "1.8.1",
|
||||
|
||||
@@ -73,6 +73,24 @@ function status(res: NowResponse, statusCode: number): NowResponse {
|
||||
return res;
|
||||
}
|
||||
|
||||
function redirect(
|
||||
res: NowResponse,
|
||||
statusOrUrl: string | number,
|
||||
url?: string
|
||||
): NowResponse {
|
||||
if (typeof statusOrUrl === 'string') {
|
||||
url = statusOrUrl;
|
||||
statusOrUrl = 307;
|
||||
}
|
||||
if (typeof statusOrUrl !== 'number' || typeof url !== 'string') {
|
||||
throw new Error(
|
||||
`Invalid redirect arguments. Please use a single argument URL, e.g. res.redirect('/destination') or use a status code and URL, e.g. res.redirect(307, '/destination').`
|
||||
);
|
||||
}
|
||||
res.writeHead(statusOrUrl, { Location: url }).end();
|
||||
return res;
|
||||
}
|
||||
|
||||
function setCharset(type: string, charset: string) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { parse, format } = require('content-type');
|
||||
@@ -261,6 +279,7 @@ export function createServerWithHelpers(
|
||||
setLazyProp<NowRequestBody>(req, 'body', getBodyParser(req, event.body));
|
||||
|
||||
res.status = statusCode => status(res, statusCode);
|
||||
res.redirect = (statusOrUrl, url) => redirect(res, statusOrUrl, url);
|
||||
res.send = body => send(req, res, body);
|
||||
res.json = jsonBody => json(req, res, jsonBody);
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
// @ts-ignore - `@types/mkdirp-promise` is broken
|
||||
import mkdirp from 'mkdirp-promise';
|
||||
import once from '@tootallnate/once';
|
||||
import { nodeFileTrace } from '@zeit/node-file-trace';
|
||||
import { nodeFileTrace } from '@vercel/nft';
|
||||
import buildUtils from './build-utils';
|
||||
import {
|
||||
File,
|
||||
@@ -52,12 +52,6 @@ import { Register, register } from './typescript';
|
||||
export { shouldServe };
|
||||
export { NowRequest, NowResponse } from './types';
|
||||
|
||||
interface CompilerConfig {
|
||||
debug?: boolean;
|
||||
includeFiles?: string | string[];
|
||||
excludeFiles?: string | string[];
|
||||
}
|
||||
|
||||
interface DownloadOptions {
|
||||
files: Files;
|
||||
entrypoint: string;
|
||||
@@ -125,9 +119,10 @@ async function downloadInstallAndBundle({
|
||||
|
||||
async function compile(
|
||||
workPath: string,
|
||||
baseDir: string,
|
||||
entrypointPath: string,
|
||||
entrypoint: string,
|
||||
config: CompilerConfig
|
||||
config: Config
|
||||
): Promise<{
|
||||
preparedFiles: Files;
|
||||
shouldAddSourcemapSupport: boolean;
|
||||
@@ -150,20 +145,21 @@ async function compile(
|
||||
for (const pattern of includeFiles) {
|
||||
const files = await glob(pattern, workPath);
|
||||
await Promise.all(
|
||||
Object.keys(files).map(async file => {
|
||||
const entry = files[file];
|
||||
fsCache.set(file, entry);
|
||||
Object.values(files).map(async entry => {
|
||||
const { fsPath } = entry;
|
||||
const relPath = relative(baseDir, fsPath);
|
||||
fsCache.set(relPath, entry);
|
||||
const stream = entry.toStream();
|
||||
const { data } = await FileBlob.fromStream({ stream });
|
||||
if (file.endsWith('.ts') || file.endsWith('.tsx')) {
|
||||
if (relPath.endsWith('.ts') || relPath.endsWith('.tsx')) {
|
||||
sourceCache.set(
|
||||
file,
|
||||
compileTypeScript(resolve(workPath, file), data.toString())
|
||||
relPath,
|
||||
compileTypeScript(fsPath, data.toString())
|
||||
);
|
||||
} else {
|
||||
sourceCache.set(file, data);
|
||||
sourceCache.set(relPath, data);
|
||||
}
|
||||
inputFiles.add(resolve(workPath, file));
|
||||
inputFiles.add(fsPath);
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -178,7 +174,7 @@ async function compile(
|
||||
|
||||
let tsCompile: Register;
|
||||
function compileTypeScript(path: string, source: string): string {
|
||||
const relPath = relative(workPath, path);
|
||||
const relPath = relative(baseDir, path);
|
||||
if (!tsCompile) {
|
||||
tsCompile = register({
|
||||
basePath: workPath, // The base is the same as root now.json dir
|
||||
@@ -201,12 +197,13 @@ async function compile(
|
||||
const { fileList, esmFileList, warnings } = await nodeFileTrace(
|
||||
[...inputFiles],
|
||||
{
|
||||
base: workPath,
|
||||
base: baseDir,
|
||||
processCwd: workPath,
|
||||
ts: true,
|
||||
mixedModules: true,
|
||||
ignore: config.excludeFiles,
|
||||
readFile(fsPath: string): Buffer | string | null {
|
||||
const relPath = relative(workPath, fsPath);
|
||||
const relPath = relative(baseDir, fsPath);
|
||||
const cached = sourceCache.get(relPath);
|
||||
if (cached) return cached.toString();
|
||||
// null represents a not found
|
||||
@@ -246,7 +243,7 @@ async function compile(
|
||||
for (const path of fileList) {
|
||||
let entry = fsCache.get(path);
|
||||
if (!entry) {
|
||||
const fsPath = resolve(workPath, path);
|
||||
const fsPath = resolve(baseDir, path);
|
||||
const { mode } = lstatSync(fsPath);
|
||||
if (isSymbolicLink(mode)) {
|
||||
entry = new FileFsRef({ fsPath, mode });
|
||||
@@ -258,14 +255,14 @@ async function compile(
|
||||
if (isSymbolicLink(entry.mode) && entry.fsPath) {
|
||||
// ensure the symlink target is added to the file list
|
||||
const symlinkTarget = relative(
|
||||
workPath,
|
||||
baseDir,
|
||||
resolve(dirname(entry.fsPath), readlinkSync(entry.fsPath))
|
||||
);
|
||||
if (
|
||||
!symlinkTarget.startsWith('..' + sep) &&
|
||||
fileList.indexOf(symlinkTarget) === -1
|
||||
) {
|
||||
const stats = statSync(resolve(workPath, symlinkTarget));
|
||||
const stats = statSync(resolve(baseDir, symlinkTarget));
|
||||
if (stats.isFile()) {
|
||||
fileList.push(symlinkTarget);
|
||||
}
|
||||
@@ -275,7 +272,7 @@ async function compile(
|
||||
// There is a bug on Windows where entrypoint uses forward slashes
|
||||
// and workPath uses backslashes so we use resolve before comparing.
|
||||
if (
|
||||
resolve(workPath, path) !== resolve(workPath, entrypoint) &&
|
||||
resolve(baseDir, path) !== resolve(workPath, entrypoint) &&
|
||||
tsCompiled.has(path)
|
||||
) {
|
||||
preparedFiles[
|
||||
@@ -339,6 +336,7 @@ export async function build({
|
||||
files,
|
||||
entrypoint,
|
||||
workPath,
|
||||
repoRootPath,
|
||||
config = {},
|
||||
meta = {},
|
||||
}: BuildOptions) {
|
||||
@@ -346,6 +344,7 @@ export async function build({
|
||||
config.helpers === false || process.env.NODEJS_HELPERS === '0'
|
||||
);
|
||||
|
||||
const baseDir = repoRootPath || workPath;
|
||||
const awsLambdaHandler = getAWSLambdaHandler(entrypoint, config);
|
||||
|
||||
const {
|
||||
@@ -372,6 +371,7 @@ export async function build({
|
||||
const traceTime = Date.now();
|
||||
const { preparedFiles, shouldAddSourcemapSupport, watch } = await compile(
|
||||
workPath,
|
||||
baseDir,
|
||||
entrypointPath,
|
||||
entrypoint,
|
||||
config
|
||||
@@ -383,7 +383,7 @@ export async function build({
|
||||
const launcherFiles: Files = {
|
||||
[`${LAUNCHER_FILENAME}.js`]: new FileBlob({
|
||||
data: makeLauncher({
|
||||
entrypointPath: `./${entrypoint}`,
|
||||
entrypointPath: `./${relative(baseDir, entrypointPath)}`,
|
||||
bridgePath: `./${BRIDGE_FILENAME}`,
|
||||
helpersPath: `./${HELPERS_FILENAME}`,
|
||||
sourcemapSupportPath: `./${SOURCEMAP_SUPPORT_FILENAME}`,
|
||||
|
||||
@@ -14,9 +14,7 @@ export type NowResponse = ServerResponse & {
|
||||
send: (body: any) => NowResponse;
|
||||
json: (jsonBody: any) => NowResponse;
|
||||
status: (statusCode: number) => NowResponse;
|
||||
redirect: (statusOrUrl: string | number, url?: string) => NowResponse;
|
||||
};
|
||||
|
||||
export type NowApiHandler = (
|
||||
req: NowRequest,
|
||||
res: NowResponse
|
||||
) => void;
|
||||
export type NowApiHandler = (req: NowRequest, res: NowResponse) => void;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"sourceMap": true,
|
||||
"jsx": "react",
|
||||
"lib": ["esnext"],
|
||||
"target": "esnext",
|
||||
"module": "commonjs"
|
||||
|
||||
54
packages/now-node/test/helpers.test.js
vendored
54
packages/now-node/test/helpers.test.js
vendored
@@ -65,6 +65,7 @@ describe('all helpers', () => {
|
||||
['cookies', 0],
|
||||
['body', 0],
|
||||
['status', 1],
|
||||
['redirect', 1],
|
||||
['send', 1],
|
||||
['json', 1],
|
||||
];
|
||||
@@ -307,6 +308,59 @@ describe('res.status', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('res.redirect', () => {
|
||||
test('should redirect to login', async () => {
|
||||
mockListener.mockImplementation((req, res) => {
|
||||
res.redirect('/login');
|
||||
res.end();
|
||||
});
|
||||
|
||||
const res = await fetchWithProxyReq(url, { redirect: 'manual' });
|
||||
|
||||
expect(res.status).toBe(307);
|
||||
expect(res.headers.get('location')).toBe(url + '/login');
|
||||
});
|
||||
test('should redirect with status code 301', async () => {
|
||||
mockListener.mockImplementation((req, res) => {
|
||||
res.redirect(301, '/login');
|
||||
res.end();
|
||||
});
|
||||
const res = await fetchWithProxyReq(url, { redirect: 'manual' });
|
||||
expect(res.status).toBe(301);
|
||||
expect(res.headers.get('location')).toBe(url + '/login');
|
||||
});
|
||||
test('should show friendly error for invalid redirect', async () => {
|
||||
let error;
|
||||
mockListener.mockImplementation((req, res) => {
|
||||
try {
|
||||
res.redirect(307);
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
res.end();
|
||||
});
|
||||
await fetchWithProxyReq(url, { redirect: 'manual' });
|
||||
expect(error.message).toBe(
|
||||
`Invalid redirect arguments. Please use a single argument URL, e.g. res.redirect('/destination') or use a status code and URL, e.g. res.redirect(307, '/destination').`
|
||||
);
|
||||
});
|
||||
test('should show friendly error in case of passing null as first argument redirect', async () => {
|
||||
let error;
|
||||
mockListener.mockImplementation((req, res) => {
|
||||
try {
|
||||
res.redirect(null);
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
res.end();
|
||||
});
|
||||
await fetchWithProxyReq(url, { redirect: 'manual' });
|
||||
expect(error.message).toBe(
|
||||
`Invalid redirect arguments. Please use a single argument URL, e.g. res.redirect('/destination') or use a status code and URL, e.g. res.redirect(307, '/destination').`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// tests based on expressjs test suite
|
||||
// see https://github.com/expressjs/express/blob/master/test/res.send.js
|
||||
describe('res.send', () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/python",
|
||||
"version": "1.2.2",
|
||||
"version": "1.2.3",
|
||||
"main": "./dist/index.js",
|
||||
"license": "MIT",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
||||
@@ -20,6 +20,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/execa": "^0.9.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"execa": "^1.0.0",
|
||||
"typescript": "3.9.3"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/routing-utils",
|
||||
"version": "1.8.4-canary.1",
|
||||
"version": "1.8.4",
|
||||
"description": "Vercel routing utilities",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -201,13 +201,13 @@ function replaceSegments(
|
||||
.filter(val => typeof val === 'string') as string[]
|
||||
);
|
||||
|
||||
pathname = safelyCompile(pathname, indexes);
|
||||
hash = hash ? safelyCompile(hash, indexes) : null;
|
||||
pathname = safelyCompile(pathname, indexes, true);
|
||||
hash = hash ? safelyCompile(hash, indexes, true) : null;
|
||||
|
||||
for (const [key, strOrArray] of Object.entries(query)) {
|
||||
let value = Array.isArray(strOrArray) ? strOrArray[0] : strOrArray;
|
||||
if (value) {
|
||||
value = safelyCompile(value, indexes);
|
||||
value = safelyCompile(value, indexes, true);
|
||||
}
|
||||
query[key] = value;
|
||||
}
|
||||
@@ -242,12 +242,24 @@ function replaceSegments(
|
||||
|
||||
function safelyCompile(
|
||||
value: string,
|
||||
indexes: { [k: string]: string }
|
||||
indexes: { [k: string]: string },
|
||||
attemptDirectCompile?: boolean
|
||||
): string {
|
||||
if (!value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (attemptDirectCompile) {
|
||||
try {
|
||||
// Attempt compiling normally with path-to-regexp first and fall back
|
||||
// to safely compiling to handle edge cases if path-to-regexp compile
|
||||
// fails
|
||||
return compile(value, { validate: false })(indexes);
|
||||
} catch (e) {
|
||||
// non-fatal, we continue to safely compile
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of Object.keys(indexes)) {
|
||||
if (value.includes(`:${key}`)) {
|
||||
value = value
|
||||
|
||||
@@ -190,6 +190,14 @@ test('convertRedirects', () => {
|
||||
source: '/optional/:id?',
|
||||
destination: '/api/optional/:id?',
|
||||
},
|
||||
{
|
||||
source: '/feature-{:slug}',
|
||||
destination: '/blog-{:slug}',
|
||||
},
|
||||
{
|
||||
source: '/hello/:world',
|
||||
destination: '/somewhere?else={:world}',
|
||||
},
|
||||
]);
|
||||
|
||||
const expected = [
|
||||
@@ -266,6 +274,20 @@ test('convertRedirects', () => {
|
||||
headers: { Location: '/api/optional/$1' },
|
||||
status: 308,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Location: '/blog-$1',
|
||||
},
|
||||
src: '^\\/feature-([^\\/]+?)$',
|
||||
status: 308,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Location: '/somewhere?else=$1',
|
||||
},
|
||||
src: '^\\/hello(?:\\/([^\\/]+?))$',
|
||||
status: 308,
|
||||
},
|
||||
];
|
||||
|
||||
deepEqual(actual, expected);
|
||||
@@ -285,6 +307,8 @@ test('convertRedirects', () => {
|
||||
['/hello/world', '/hello/another/world'],
|
||||
['/external/1', '/external/2'],
|
||||
['/optional', '/optional/1'],
|
||||
['/feature-first', '/feature-second'],
|
||||
['/hello/world', '/hello/again'],
|
||||
];
|
||||
|
||||
const mustNotMatch = [
|
||||
@@ -302,6 +326,8 @@ test('convertRedirects', () => {
|
||||
['/not-this-one', '/helloo'],
|
||||
['/externalnope', '/externally'],
|
||||
['/optionalnope', '/optionally'],
|
||||
['/feature/first', '/feature'],
|
||||
['/hello', '/hello/another/one'],
|
||||
];
|
||||
|
||||
assertRegexMatches(actual, mustMatch, mustNotMatch);
|
||||
@@ -350,6 +376,14 @@ test('convertRewrites', () => {
|
||||
{ source: '/:path', destination: '/test?path=:path' },
|
||||
{ source: '/:path/:two', destination: '/test?path=:path' },
|
||||
{ source: '/(.*)-:id(\\d+).html', destination: '/blog/:id' },
|
||||
{
|
||||
source: '/feature-{:slug}',
|
||||
destination: '/blog-{:slug}',
|
||||
},
|
||||
{
|
||||
source: '/hello/:world',
|
||||
destination: '/somewhere?else={:world}',
|
||||
},
|
||||
]);
|
||||
|
||||
const expected = [
|
||||
@@ -426,6 +460,16 @@ test('convertRewrites', () => {
|
||||
src: '^(?:\\/([^\\/]+?))(?:\\/([^\\/]+?))$',
|
||||
},
|
||||
{ check: true, dest: '/blog/$2', src: '^(?:\\/(.*))-(\\d+)\\.html$' },
|
||||
{
|
||||
dest: '/blog-$1',
|
||||
src: '^\\/feature-([^\\/]+?)$',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
dest: '/somewhere?else=$1&world=$1',
|
||||
src: '^\\/hello(?:\\/([^\\/]+?))$',
|
||||
check: true,
|
||||
},
|
||||
];
|
||||
|
||||
deepEqual(actual, expected);
|
||||
@@ -447,6 +491,8 @@ test('convertRewrites', () => {
|
||||
['/first', '/another'],
|
||||
['/first/second', '/one/two'],
|
||||
['/hello/post-123.html', '/post-123.html'],
|
||||
['/feature-first', '/feature-second'],
|
||||
['/hello/world', '/hello/again'],
|
||||
];
|
||||
|
||||
const mustNotMatch = [
|
||||
@@ -466,6 +512,8 @@ test('convertRewrites', () => {
|
||||
['/another/one'],
|
||||
['/not', '/these'],
|
||||
['/hello/post.html'],
|
||||
['/feature/first', '/feature'],
|
||||
['/hello', '/hello/another/one'],
|
||||
];
|
||||
|
||||
assertRegexMatches(actual, mustMatch, mustNotMatch);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@vercel/ruby",
|
||||
"author": "Nathan Cahill <nathan@nathancahill.com>",
|
||||
"version": "1.2.3",
|
||||
"version": "1.2.4",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
|
||||
@@ -22,6 +22,7 @@
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "8.0.0",
|
||||
"@types/semver": "6.0.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"execa": "2.0.4",
|
||||
"fs-extra": "^7.0.1",
|
||||
"semver": "6.1.1",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "@vercel/static-build",
|
||||
"version": "0.17.7-canary.3",
|
||||
"version": "0.17.8",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/static-builds",
|
||||
"homepage": "https://vercel.com/docs/build-step",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
@@ -23,6 +23,7 @@
|
||||
"@types/ms": "0.7.31",
|
||||
"@types/node-fetch": "2.5.4",
|
||||
"@types/promise-timeout": "1.3.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"get-port": "5.0.0",
|
||||
"is-port-reachable": "2.0.1",
|
||||
"ms": "2.1.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/redwood",
|
||||
"version": "0.0.2-canary.7",
|
||||
"version": "0.1.1",
|
||||
"main": "./dist/index.js",
|
||||
"license": "MIT",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@netlify/zip-it-and-ship-it": "1.2.0",
|
||||
"@vercel/frameworks": "0.0.18-canary.5"
|
||||
"@vercel/frameworks": "0.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/aws-lambda": "8.10.19",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { join, dirname, relative, parse as parsePath, sep } from 'path';
|
||||
import { readFileSync } from 'fs';
|
||||
import {
|
||||
BuildOptions,
|
||||
Lambda,
|
||||
@@ -18,6 +17,8 @@ import {
|
||||
FileFsRef,
|
||||
PackageJson,
|
||||
NowBuildError,
|
||||
getLambdaOptionsFromFunction,
|
||||
readConfigFile,
|
||||
} from '@vercel/build-utils';
|
||||
import { makeAwsLauncher } from './launcher';
|
||||
import _frameworks, { Framework } from '@vercel/frameworks';
|
||||
@@ -32,6 +33,12 @@ const BRIDGE_FILENAME = '___vc_bridge';
|
||||
const HELPERS_FILENAME = '___vc_helpers';
|
||||
const SOURCEMAP_SUPPORT_FILENAME = '__vc_sourcemap_support';
|
||||
|
||||
interface RedwoodToml {
|
||||
web: { port?: number; apiProxyPath?: string };
|
||||
api: { port?: number };
|
||||
browser: { open?: boolean };
|
||||
}
|
||||
|
||||
export const version = 2;
|
||||
|
||||
export async function build({
|
||||
@@ -67,8 +74,11 @@ export async function build({
|
||||
const { buildCommand } = config;
|
||||
const frmwrkCmd = frameworks.find(f => f.slug === 'redwoodjs')?.settings
|
||||
.buildCommand;
|
||||
const pkgPath = join(workPath, 'package.json');
|
||||
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as PackageJson;
|
||||
const pkg = await readConfigFile<PackageJson>(join(workPath, 'package.json'));
|
||||
const toml = await readConfigFile<RedwoodToml>(
|
||||
join(workPath, 'redwood.toml')
|
||||
);
|
||||
|
||||
if (buildCommand) {
|
||||
debug(`Executing build command "${buildCommand}"`);
|
||||
await execCommand(buildCommand, {
|
||||
@@ -98,6 +108,7 @@ export async function build({
|
||||
});
|
||||
}
|
||||
|
||||
const apiDir = toml?.web?.apiProxyPath?.replace(/^\//, '') ?? 'api';
|
||||
const apiDistPath = join(workPath, 'api', 'dist', 'functions');
|
||||
const webDistPath = join(workPath, 'web', 'dist');
|
||||
const lambdaOutputs: { [filePath: string]: Lambda } = {};
|
||||
@@ -107,7 +118,7 @@ export async function build({
|
||||
const functionFiles = await glob('*.js', apiDistPath);
|
||||
|
||||
for (const [funcName, fileFsRef] of Object.entries(functionFiles)) {
|
||||
const outputName = join('api', parsePath(funcName).name); // remove `.js` extension
|
||||
const outputName = join(apiDir, parsePath(funcName).name); // remove `.js` extension
|
||||
const absEntrypoint = fileFsRef.fsPath;
|
||||
const dependencies: string[] = await getDependencies(
|
||||
absEntrypoint,
|
||||
@@ -115,6 +126,7 @@ export async function build({
|
||||
);
|
||||
const relativeEntrypoint = relative(workPath, absEntrypoint);
|
||||
const awsLambdaHandler = getAWSLambdaHandler(relativeEntrypoint, 'handler');
|
||||
const sourceFile = relativeEntrypoint.replace('/dist/', '/src/');
|
||||
|
||||
const lambdaFiles: Files = {
|
||||
[`${LAUNCHER_FILENAME}.js`]: new FileBlob({
|
||||
@@ -141,11 +153,18 @@ export async function build({
|
||||
|
||||
lambdaFiles[relative(workPath, fileFsRef.fsPath)] = fileFsRef;
|
||||
|
||||
const { memory, maxDuration } = await getLambdaOptionsFromFunction({
|
||||
sourceFile,
|
||||
config,
|
||||
});
|
||||
|
||||
const lambda = await createLambda({
|
||||
files: lambdaFiles,
|
||||
handler: `${LAUNCHER_FILENAME}.launcher`,
|
||||
runtime: nodeVersion.runtime,
|
||||
environment: {},
|
||||
memory,
|
||||
maxDuration,
|
||||
});
|
||||
lambdaOutputs[outputName] = lambda;
|
||||
}
|
||||
@@ -162,7 +181,7 @@ function getAWSLambdaHandler(filePath: string, handlerName: string) {
|
||||
return `${dir}${dir ? sep : ''}${name}.${handlerName}`;
|
||||
}
|
||||
|
||||
function hasScript(scriptName: string, pkg: PackageJson) {
|
||||
function hasScript(scriptName: string, pkg: PackageJson | null) {
|
||||
const scripts = (pkg && pkg.scripts) || {};
|
||||
return typeof scripts[scriptName] === 'string';
|
||||
}
|
||||
@@ -170,6 +189,6 @@ function hasScript(scriptName: string, pkg: PackageJson) {
|
||||
export async function prepareCache({
|
||||
workPath,
|
||||
}: PrepareCacheOptions): Promise<Files> {
|
||||
const cache = await glob('node_modules/**', workPath);
|
||||
const cache = await glob('**/node_modules/**', workPath);
|
||||
return cache;
|
||||
}
|
||||
|
||||
9
packages/redwood/test/fixtures/01-create-redwood-app/api/src/functions/memory.js
vendored
Normal file
9
packages/redwood/test/fixtures/01-create-redwood-app/api/src/functions/memory.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
async function handler() {
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {},
|
||||
body: `Memory is: ${process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE}`,
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { handler }
|
||||
@@ -1,6 +1,19 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "package.json", "use": "@vercel/redwood" }],
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/redwood",
|
||||
"config": {
|
||||
"zeroConfig": true,
|
||||
"functions": {
|
||||
"api/src/functions/memory.js": {
|
||||
"memory": 128
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "<div id=\"redwood-app\">" },
|
||||
{ "path": "/about", "mustContain": "<div id=\"redwood-app\">" },
|
||||
@@ -19,6 +32,10 @@
|
||||
{
|
||||
"path": "/api/permission",
|
||||
"mustContain": "File is executable: true"
|
||||
},
|
||||
{
|
||||
"path": "/api/memory",
|
||||
"mustContain": "Memory is: 128"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
# This file contains the configuration settings for your Redwood app.
|
||||
# This file is also what makes your Redwood app a Redwood app.
|
||||
# If you remove it and try to run `yarn rw dev`, you'll get an error.
|
||||
#
|
||||
# For the full list of options, see the "App Configuration: redwood.toml" doc:
|
||||
# https://redwoodjs.com/docs/app-configuration-redwood-toml
|
||||
#
|
||||
# By default, a Redwood app's apiProxyPath is the same as Netlify's.
|
||||
|
||||
[web]
|
||||
port = 8910
|
||||
apiProxyPath = "/api/functions"
|
||||
apiProxyPath = "/api"
|
||||
[api]
|
||||
port = 8911
|
||||
[browser]
|
||||
|
||||
10
packages/redwood/test/fixtures/16-create-redwood-app/.env.defaults
vendored
Normal file
10
packages/redwood/test/fixtures/16-create-redwood-app/.env.defaults
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# These environment variables will be used by default if you do not create any
|
||||
# yourself in .env. This file should be safe to check into your version control
|
||||
# system. Any custom values should go in .env and .env should *not* be checked
|
||||
# into version control.
|
||||
|
||||
# schema.prisma defaults
|
||||
DATABASE_URL=file:./dev.db
|
||||
|
||||
# disables Prisma CLI update notifier
|
||||
PRISMA_HIDE_UPDATE_MESSAGE=true
|
||||
10
packages/redwood/test/fixtures/16-create-redwood-app/.gitignore
vendored
Normal file
10
packages/redwood/test/fixtures/16-create-redwood-app/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
.idea
|
||||
.DS_Store
|
||||
.env
|
||||
.netlify
|
||||
dev.db
|
||||
dist
|
||||
dist-babel
|
||||
node_modules
|
||||
yarn-error.log
|
||||
web/public/mockServiceWorker.js
|
||||
21
packages/redwood/test/fixtures/16-create-redwood-app/LICENSE
vendored
Normal file
21
packages/redwood/test/fixtures/16-create-redwood-app/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Redwood
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
25
packages/redwood/test/fixtures/16-create-redwood-app/README.md
vendored
Normal file
25
packages/redwood/test/fixtures/16-create-redwood-app/README.md
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# Redwood
|
||||
|
||||
> **WARNING:** RedwoodJS software has not reached a stable version 1.0 and should not be considered suitable for production use. In the "make it work; make it right; make it fast" paradigm, Redwood is in the later stages of the "make it work" phase.
|
||||
|
||||
## Getting Started
|
||||
|
||||
- [Tutorial](https://redwoodjs.com/tutorial/welcome-to-redwood): getting started and complete overview guide.
|
||||
- [Docs](https://redwoodjs.com/docs/introduction): using the Redwood Router, handling assets and files, list of command-line tools, and more.
|
||||
- [Redwood Community](https://community.redwoodjs.com): get help, share tips and tricks, and collaborate on everything about RedwoodJS.
|
||||
|
||||
### Setup
|
||||
|
||||
We use Yarn as our package manager. To get the dependencies installed, just do this in the root directory:
|
||||
|
||||
```terminal
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Fire it up
|
||||
|
||||
```terminal
|
||||
yarn redwood dev
|
||||
```
|
||||
|
||||
Your browser should open automatically to `http://localhost:8910` to see the web app. Lambda functions run on `http://localhost:8911` and are also proxied to `http://localhost:8910/.redwood/functions/*`.
|
||||
1
packages/redwood/test/fixtures/16-create-redwood-app/api/.babelrc.js
vendored
Normal file
1
packages/redwood/test/fixtures/16-create-redwood-app/api/.babelrc.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = { extends: '../babel.config.js' }
|
||||
6
packages/redwood/test/fixtures/16-create-redwood-app/api/jest.config.js
vendored
Normal file
6
packages/redwood/test/fixtures/16-create-redwood-app/api/jest.config.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
const { getConfig } = require('@redwoodjs/core')
|
||||
|
||||
const config = getConfig({ type: 'jest', target: 'node' })
|
||||
config.displayName.name = 'api'
|
||||
|
||||
module.exports = config
|
||||
9
packages/redwood/test/fixtures/16-create-redwood-app/api/jsconfig.json
vendored
Normal file
9
packages/redwood/test/fixtures/16-create-redwood-app/api/jsconfig.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"src/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
8
packages/redwood/test/fixtures/16-create-redwood-app/api/package.json
vendored
Normal file
8
packages/redwood/test/fixtures/16-create-redwood-app/api/package.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "api",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@redwoodjs/api": "^0.16.0"
|
||||
}
|
||||
}
|
||||
20
packages/redwood/test/fixtures/16-create-redwood-app/api/prisma/schema.prisma
vendored
Normal file
20
packages/redwood/test/fixtures/16-create-redwood-app/api/prisma/schema.prisma
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
datasource DS {
|
||||
// optionally set multiple providers
|
||||
// example: provider = ["sqlite", "postgresql"]
|
||||
provider = "sqlite"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
binaryTargets = "native"
|
||||
}
|
||||
|
||||
// Define your own datamodels here and run `yarn redwood db save` to create
|
||||
// migrations for them.
|
||||
// TODO: Please remove the following example:
|
||||
model UserExample {
|
||||
id Int @id @default(autoincrement())
|
||||
email String @unique
|
||||
name String?
|
||||
}
|
||||
26
packages/redwood/test/fixtures/16-create-redwood-app/api/prisma/seeds.js
vendored
Normal file
26
packages/redwood/test/fixtures/16-create-redwood-app/api/prisma/seeds.js
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/* eslint-disable no-console */
|
||||
const { PrismaClient } = require('@prisma/client')
|
||||
const dotenv = require('dotenv')
|
||||
|
||||
dotenv.config()
|
||||
const db = new PrismaClient()
|
||||
|
||||
async function main() {
|
||||
// Seed data is database data that needs to exist for your app to run.
|
||||
// Ideally this file should be idempotent: running it multiple times
|
||||
// will result in the same database state (usually by checking for the
|
||||
// existence of a record before trying to create it). For example:
|
||||
//
|
||||
// const existing = await db.user.findMany({ where: { email: 'admin@email.com' }})
|
||||
// if (!existing.length) {
|
||||
// await db.user.create({ data: { name: 'Admin', email: 'admin@email.com' }})
|
||||
// }
|
||||
|
||||
console.info('No data to seed. See api/prisma/seeds.js for info.')
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => console.error(e))
|
||||
.finally(async () => {
|
||||
await db.disconnect()
|
||||
})
|
||||
17
packages/redwood/test/fixtures/16-create-redwood-app/api/src/functions/graphql.js
vendored
Normal file
17
packages/redwood/test/fixtures/16-create-redwood-app/api/src/functions/graphql.js
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import {
|
||||
createGraphQLHandler,
|
||||
makeMergedSchema,
|
||||
makeServices,
|
||||
} from '@redwoodjs/api'
|
||||
import schemas from 'src/graphql/**/*.{js,ts}'
|
||||
import services from 'src/services/**/*.{js,ts}'
|
||||
|
||||
import { db } from 'src/lib/db'
|
||||
|
||||
export const handler = createGraphQLHandler({
|
||||
schema: makeMergedSchema({
|
||||
schemas,
|
||||
services: makeServices({ services }),
|
||||
}),
|
||||
db,
|
||||
})
|
||||
0
packages/redwood/test/fixtures/16-create-redwood-app/api/src/graphql/.keep
vendored
Normal file
0
packages/redwood/test/fixtures/16-create-redwood-app/api/src/graphql/.keep
vendored
Normal file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user