Compare commits

..

5 Commits

Author SHA1 Message Date
JJ Kasper
be2ae2c539 Publish Canary
- @vercel/build-utils@2.10.2-canary.0
 - vercel@21.3.4-canary.0
 - @vercel/client@9.0.9-canary.0
 - @vercel/frameworks@0.3.2-canary.0
 - @vercel/routing-utils@1.10.2-canary.0
2021-03-11 14:00:10 -06:00
JJ Kasper
4969a65209 [routing-utils] Add has route field to schema (#5919)
### Related Issues

x-ref: https://github.com/vercel/next.js/pull/22341
x-ref: https://github.com/vercel/next.js/issues/22345

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2021-03-11 19:44:15 +00:00
ernestd
7275878b2b [frameworks] Update demo URLs to use *.examples.vercel.com (#5960) 2021-03-10 11:45:52 -08:00
Nathan Rajlich
53d429e3f5 [cli] Convert vercel login command to TypeScript (#5951)
* WIP convert `vc login` command to TypeScript

* Fix build
2021-03-08 15:59:42 -08:00
Nathan Rajlich
3f1384bd1a [cli] Remove "micro" dependency (#5950) 2021-03-08 13:48:18 -08:00
20 changed files with 293 additions and 147 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "2.10.1",
"version": "2.10.2-canary.0",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -29,7 +29,7 @@
"@types/node-fetch": "^2.1.6",
"@types/semver": "6.0.0",
"@types/yazl": "^2.4.1",
"@vercel/frameworks": "0.3.1",
"@vercel/frameworks": "0.3.2-canary.0",
"@vercel/ncc": "0.24.0",
"aggregate-error": "3.0.1",
"async-retry": "1.2.3",

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "21.3.3",
"version": "21.3.4-canary.0",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -61,7 +61,7 @@
"node": ">= 10"
},
"dependencies": {
"@vercel/build-utils": "2.10.1",
"@vercel/build-utils": "2.10.2-canary.0",
"@vercel/go": "1.2.2",
"@vercel/node": "1.9.1",
"@vercel/python": "2.0.1",
@@ -83,7 +83,6 @@
"@types/glob": "7.1.1",
"@types/http-proxy": "1.16.2",
"@types/load-json-file": "2.0.7",
"@types/micro": "7.3.3",
"@types/mime-types": "2.1.0",
"@types/minimatch": "3.0.3",
"@types/mri": "1.1.0",
@@ -100,7 +99,7 @@
"@types/universal-analytics": "0.4.2",
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@vercel/frameworks": "0.3.1",
"@vercel/frameworks": "0.3.2-canary.0",
"@vercel/ncc": "0.24.0",
"@zeit/fun": "0.11.2",
"@zeit/source-map-support": "0.6.2",
@@ -141,7 +140,6 @@
"jaro-winkler": "0.2.8",
"jsonlines": "0.1.1",
"load-json-file": "3.0.0",
"micro": "9.1.2",
"mime-types": "2.1.24",
"minimatch": "3.0.4",
"mri": "1.1.5",

View File

@@ -1,29 +1,37 @@
import { stringify as stringifyQuery } from 'querystring';
import fetch from 'node-fetch';
import debugFactory from 'debug';
import createDebug from 'debug';
// @ts-ignore
import promptEmail from 'email-prompt';
import ms from 'ms';
import { validate as validateEmail } from 'email-validator';
import chalk from 'chalk';
import ua from '../util/ua.ts';
import ua from '../util/ua';
import getArgs from '../util/get-args';
import error from '../util/output/error';
import highlight from '../util/output/highlight';
import ok from '../util/output/ok';
import param from '../util/output/param.ts';
import param from '../util/output/param';
import eraseLines from '../util/output/erase-lines';
import sleep from '../util/sleep';
import { handleError } from '../util/error';
import handleError from '../util/handle-error';
import { writeToAuthConfigFile, writeToConfigFile } from '../util/config/files';
import getGlobalPathConfig from '../util/config/global-path';
import hp from '../util/humanize-path';
import logo from '../util/output/logo';
import exit from '../util/exit';
import executeLogin from '../util/login/login.ts';
import executeLogin from '../util/login/login';
import { prependEmoji, emoji } from '../util/emoji';
import { getCommandName, getPkgName } from '../util/pkg-name.ts';
import { getCommandName, getPkgName } from '../util/pkg-name';
import { NowContext } from '../types';
const debug = debugFactory(`${getPkgName()}:login`);
interface VerifyParams {
apiUrl: string;
email: string;
verificationToken: string;
}
const debug = createDebug(`${getPkgName()}:login`);
const help = () => {
console.log(`
@@ -51,7 +59,7 @@ const help = () => {
`);
};
const verify = async ({ apiUrl, email, verificationToken }) => {
const verify = async ({ apiUrl, email, verificationToken }: VerifyParams) => {
const query = {
email,
token: verificationToken,
@@ -125,7 +133,7 @@ const readEmail = async () => {
return email;
};
const login = async ctx => {
const login = async (ctx: NowContext) => {
let argv;
try {
@@ -197,10 +205,8 @@ const login = async ctx => {
return 1;
}
output.stopSpinner();
// Clear up `Sending email` success message
process.stdout.write(eraseLines(possibleAddress ? 1 : 2));
output.print(eraseLines(possibleAddress ? 1 : 2));
output.print(
`We sent an email to ${highlight(

View File

@@ -1,4 +1,4 @@
export default code =>
export default (code?: number) =>
new Promise(() => {
// We give stdout some time to flush out
// because there's a node bug where

View File

@@ -13,11 +13,11 @@ interface Spec {
export default function getArgs<T extends Spec>(
argv: string[],
argsOptions: T,
argsOptions?: T,
argOptions: ArgOptions = {}
) {
return arg(Object.assign({}, getCommonArgs(), argsOptions), {
...argOptions,
argv
argv,
});
}

View File

@@ -13,11 +13,9 @@ export default async function login(
apiUrl: string,
email: string,
mode: 'login' | 'signup' = 'login'
): Promise<LoginData | InvalidEmail | AccountNotFound> {
): Promise<LoginData> {
const hyphens = new RegExp('-', 'g');
const host = hostname()
.replace(hyphens, ' ')
.replace('.local', '');
const host = hostname().replace(hyphens, ' ').replace('.local', '');
const tokenName = `${getTitleName()} CLI on ${host}`;
const response = await fetch(`${apiUrl}/now/registration?mode=${mode}`, {

View File

@@ -1,6 +0,0 @@
import { cyan } from 'chalk';
import chars from './chars';
const ok = msg => `${cyan(chars.tick)} ${msg}`;
export default ok;

View File

@@ -0,0 +1,6 @@
import chalk from 'chalk';
import chars from './chars';
const ok = (msg: string) => `${chalk.cyan(chars.tick)} ${msg}`;
export default ok;

View File

@@ -1,8 +1,8 @@
// Packages
const http = require('http');
const listen = require('test-listen');
const micro = require('micro');
module.exports = fn => {
const srv = micro(fn);
const srv = http.createServer(fn);
return listen(srv);
};

View File

@@ -1,5 +1,4 @@
import { basename, join, sep } from 'path';
import { send } from 'micro';
import test from 'ava';
import sinon from 'sinon';
import { asc as alpha } from 'alpha-sort';
@@ -22,6 +21,12 @@ const prefix = `${join(__dirname, 'fixtures', 'unit')}${sep}`;
const base = path => path.replace(prefix, '');
const fixture = name => join(prefix, name);
const send = (res, statusCode, body) => {
res.statusCode = statusCode;
res.setHeader('Content-Type', 'application/json; charset=utf8');
res.end(JSON.stringify(body));
};
const getStaticFiles = async dir => {
const files = await getStaticFiles_(dir, {
output,
@@ -163,7 +168,7 @@ test('`wait` utility does not invoke spinner when stopped before delay', async t
});
test('4xx response error with fallback message', async t => {
const fn = async (req, res) => {
const fn = (req, res) => {
send(res, 404, {});
};
@@ -175,7 +180,7 @@ test('4xx response error with fallback message', async t => {
});
test('4xx response error without fallback message', async t => {
const fn = async (req, res) => {
const fn = (req, res) => {
send(res, 404, {});
};
@@ -187,7 +192,7 @@ test('4xx response error without fallback message', async t => {
});
test('5xx response error without fallback message', async t => {
const fn = async (req, res) => {
const fn = (req, res) => {
send(res, 500, '');
};
@@ -199,7 +204,7 @@ test('5xx response error without fallback message', async t => {
});
test('4xx response error as correct JSON', async t => {
const fn = async (req, res) => {
const fn = (req, res) => {
send(res, 400, {
error: {
message: 'The request is not correct',
@@ -215,7 +220,7 @@ test('4xx response error as correct JSON', async t => {
});
test('5xx response error as HTML', async t => {
const fn = async (req, res) => {
const fn = (req, res) => {
send(res, 500, 'This is a malformed error');
};
@@ -227,7 +232,7 @@ test('5xx response error as HTML', async t => {
});
test('5xx response error with random JSON', async t => {
const fn = async (req, res) => {
const fn = (req, res) => {
send(res, 500, {
wrong: 'property',
});
@@ -294,7 +299,7 @@ test('getProjectName with a directory', t => {
});
test('4xx error message with broken JSON', async t => {
const fn = async (req, res) => {
const fn = (req, res) => {
send(res, 403, `32puuuh2332`);
};
@@ -306,7 +311,7 @@ test('4xx error message with broken JSON', async t => {
});
test('4xx error message with proper message', async t => {
const fn = async (req, res) => {
const fn = (req, res) => {
send(res, 403, {
error: {
message: 'This is a test',
@@ -322,7 +327,7 @@ test('4xx error message with proper message', async t => {
});
test('5xx error message with proper message', async t => {
const fn = async (req, res) => {
const fn = (req, res) => {
send(res, 500, {
error: {
message: 'This is a test',
@@ -338,7 +343,7 @@ test('5xx error message with proper message', async t => {
});
test('4xx response error with broken JSON', async t => {
const fn = async (req, res) => {
const fn = (req, res) => {
send(res, 403, `122{"sss"`);
};
@@ -350,7 +355,7 @@ test('4xx response error with broken JSON', async t => {
});
test('4xx response error as correct JSON with more properties', async t => {
const fn = async (req, res) => {
const fn = (req, res) => {
send(res, 403, {
error: {
message: 'The request is not correct',
@@ -368,7 +373,7 @@ test('4xx response error as correct JSON with more properties', async t => {
});
test('429 response error with retry header', async t => {
const fn = async (req, res) => {
const fn = (req, res) => {
res.setHeader('Retry-After', '20');
send(res, 429, {
@@ -387,7 +392,7 @@ test('429 response error with retry header', async t => {
});
test('429 response error without retry header', async t => {
const fn = async (req, res) => {
const fn = (req, res) => {
send(res, 429, {
error: {
message: 'You were rate limited',

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "9.0.8",
"version": "9.0.9-canary.0",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -37,7 +37,7 @@
]
},
"dependencies": {
"@vercel/build-utils": "2.10.1",
"@vercel/build-utils": "2.10.2-canary.0",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",
"async-sema": "3.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/frameworks",
"version": "0.3.1",
"version": "0.3.2-canary.0",
"main": "./dist/frameworks.js",
"types": "./dist/frameworks.d.ts",
"files": [
@@ -20,7 +20,7 @@
"@types/js-yaml": "3.12.1",
"@types/node": "12.0.4",
"@types/node-fetch": "2.5.8",
"@vercel/routing-utils": "1.10.1",
"@vercel/routing-utils": "1.10.2-canary.0",
"ajv": "6.12.2",
"jest": "24.9.0",
"ts-jest": "24.1.0",

View File

@@ -22,7 +22,7 @@ export const frameworks = [
{
name: 'Blitz.js',
slug: 'blitzjs',
demo: 'https://blitzjs.now-examples.now.sh',
demo: 'https://blitzjs.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/blitz.svg',
tagline: 'Blitz.js: The Fullstack React Framework',
@@ -60,7 +60,7 @@ export const frameworks = [
{
name: 'Next.js',
slug: 'nextjs',
demo: 'https://nextjs.now-examples.now.sh',
demo: 'https://nextjs.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/next.svg',
tagline:
@@ -105,7 +105,7 @@ export const frameworks = [
{
name: 'Gatsby.js',
slug: 'gatsby',
demo: 'https://gatsby.now-examples.now.sh',
demo: 'https://gatsby.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/gatsby.svg',
tagline:
@@ -193,7 +193,7 @@ export const frameworks = [
{
name: 'Hexo',
slug: 'hexo',
demo: 'https://hexo.now-examples.now.sh',
demo: 'https://hexo.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/hexo.svg',
tagline:
@@ -232,7 +232,7 @@ export const frameworks = [
{
name: 'Eleventy',
slug: 'eleventy',
demo: 'https://eleventy.now-examples.now.sh',
demo: 'https://eleventy.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/eleventy.svg',
tagline:
@@ -272,7 +272,7 @@ export const frameworks = [
{
name: 'Docusaurus 2',
slug: 'docusaurus-2',
demo: 'https://docusaurus-2.now-examples.now.sh',
demo: 'https://docusaurus-2.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/docusaurus.svg',
tagline:
@@ -364,7 +364,7 @@ export const frameworks = [
{
name: 'Docusaurus 1',
slug: 'docusaurus',
demo: 'https://docusaurus.now-examples.now.sh',
demo: 'https://docusaurus.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/docusaurus.svg',
tagline:
@@ -417,7 +417,7 @@ export const frameworks = [
{
name: 'Preact',
slug: 'preact',
demo: 'https://preact.now-examples.now.sh',
demo: 'https://preact.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/preact.svg',
tagline:
@@ -464,7 +464,7 @@ export const frameworks = [
{
name: 'Dojo',
slug: 'dojo',
demo: 'https://dojo.now-examples.now.sh',
demo: 'https://dojo.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/dojo.png',
tagline: 'Dojo is a modern progressive, TypeScript first framework.',
@@ -519,7 +519,7 @@ export const frameworks = [
{
name: 'Ember.js',
slug: 'ember',
demo: 'https://ember.now-examples.now.sh',
demo: 'https://ember.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/ember.svg',
tagline:
@@ -566,7 +566,7 @@ export const frameworks = [
{
name: 'Vue.js',
slug: 'vue',
demo: 'https://vue.now-examples.now.sh',
demo: 'https://vue.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/vue.svg',
tagline:
@@ -623,7 +623,7 @@ export const frameworks = [
{
name: 'Scully',
slug: 'scully',
demo: 'https://scully.now-examples.now.sh',
demo: 'https://scully.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/scullyio-logo.png',
tagline: 'Scully is a static site generator for Angular.',
@@ -660,7 +660,7 @@ export const frameworks = [
{
name: 'Ionic Angular',
slug: 'ionic-angular',
demo: 'https://ionic-angular.now-examples.now.sh',
demo: 'https://ionic-angular.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/ionic.svg',
tagline:
@@ -707,7 +707,7 @@ export const frameworks = [
{
name: 'Angular',
slug: 'angular',
demo: 'https://angular.now-examples.now.sh',
demo: 'https://angular.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/angular.svg',
tagline:
@@ -768,7 +768,7 @@ export const frameworks = [
{
name: 'Polymer',
slug: 'polymer',
demo: 'https://polymer.now-examples.now.sh',
demo: 'https://polymer.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/polymer.svg',
tagline:
@@ -826,7 +826,7 @@ export const frameworks = [
{
name: 'Svelte',
slug: 'svelte',
demo: 'https://svelte.now-examples.now.sh',
demo: 'https://svelte.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/svelte.svg',
tagline:
@@ -874,7 +874,7 @@ export const frameworks = [
{
name: 'Ionic React',
slug: 'ionic-react',
demo: 'https://ionic-react.now-examples.now.sh',
demo: 'https://ionic-react.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/ionic.svg',
tagline:
@@ -936,7 +936,7 @@ export const frameworks = [
{
name: 'Create React App',
slug: 'create-react-app',
demo: 'https://react-functions.now-examples.now.sh',
demo: 'https://react-functions.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/react.svg',
tagline: 'Create React App allows you to get going with React in no time.',
@@ -1003,7 +1003,7 @@ export const frameworks = [
{
name: 'Gridsome',
slug: 'gridsome',
demo: 'https://gridsome.now-examples.now.sh',
demo: 'https://gridsome.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/gridsome.svg',
tagline:
@@ -1041,7 +1041,7 @@ export const frameworks = [
{
name: 'UmiJS',
slug: 'umijs',
demo: 'https://umijs.now-examples.now.sh',
demo: 'https://umijs.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/umi.svg',
tagline:
@@ -1088,7 +1088,7 @@ export const frameworks = [
{
name: 'Sapper',
slug: 'sapper',
demo: 'https://sapper.now-examples.now.sh',
demo: 'https://sapper.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/svelte.svg',
tagline:
@@ -1126,7 +1126,7 @@ export const frameworks = [
{
name: 'Saber',
slug: 'saber',
demo: 'https://saber.now-examples.now.sh',
demo: 'https://saber.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/saber.svg',
tagline:
@@ -1178,7 +1178,7 @@ export const frameworks = [
{
name: 'Stencil',
slug: 'stencil',
demo: 'https://stencil.now-examples.now.sh',
demo: 'https://stencil.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/stencil.svg',
tagline:
@@ -1240,7 +1240,7 @@ export const frameworks = [
{
name: 'Nuxt.js',
slug: 'nuxtjs',
demo: 'https://nuxtjs.now-examples.now.sh',
demo: 'https://nuxtjs.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/nuxt.svg',
tagline:
@@ -1298,7 +1298,7 @@ export const frameworks = [
{
name: 'RedwoodJS',
slug: 'redwoodjs',
demo: 'https://redwoodjs.now-examples.now.sh',
demo: 'https://redwoodjs.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/redwoodjs.svg',
tagline: 'RedwoodJS is a full-stack framework for the Jamstack.',
@@ -1336,7 +1336,7 @@ export const frameworks = [
{
name: 'Hugo',
slug: 'hugo',
demo: 'https://hugo.now-examples.now.sh',
demo: 'https://hugo.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/hugo.svg',
tagline:
@@ -1391,7 +1391,7 @@ export const frameworks = [
{
name: 'Jekyll',
slug: 'jekyll',
demo: 'https://jekyll.now-examples.vercel.app',
demo: 'https://jekyll.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/jekyll.svg',
tagline:
@@ -1433,7 +1433,7 @@ export const frameworks = [
{
name: 'Brunch',
slug: 'brunch',
demo: 'https://brunch.now-examples.now.sh',
demo: 'https://brunch.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/brunch.svg',
tagline:
@@ -1468,7 +1468,7 @@ export const frameworks = [
{
name: 'Middleman',
slug: 'middleman',
demo: 'https://middleman.now-examples.vercel.app',
demo: 'https://middleman.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/middleman.svg',
tagline:
@@ -1504,7 +1504,7 @@ export const frameworks = [
{
name: 'Zola',
slug: 'zola',
demo: 'https://zola.now-examples.vercel.app',
demo: 'https://zola.examples.vercel.com',
logo:
'https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/zola.png',
tagline: 'Everything you need to make a static site engine in one binary.',

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/routing-utils",
"version": "1.10.1",
"version": "1.10.2-canary.0",
"description": "Vercel routing utilities",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",

View File

@@ -1,3 +1,27 @@
const hasSchema = {
type: 'array',
maxItems: 16,
items: {
type: 'object',
additionalProperties: false,
required: ['type', 'key'],
properties: {
type: {
type: 'string',
enum: ['header', 'cookie', 'query'],
},
key: {
type: 'string',
maxLength: 4096,
},
value: {
type: 'string',
maxLength: 4096,
},
},
},
};
/**
* An ajv schema for the routes array
*/
@@ -91,6 +115,7 @@ export const routesSchema = {
},
},
},
has: hasSchema,
},
},
{
@@ -125,6 +150,7 @@ export const rewritesSchema = {
type: 'string',
maxLength: 4096,
},
has: hasSchema,
},
},
};
@@ -154,6 +180,7 @@ export const redirectsSchema = {
minimum: 100,
maximum: 999,
},
has: hasSchema,
},
},
};
@@ -189,6 +216,7 @@ export const headersSchema = {
},
},
},
has: hasSchema,
},
},
};

View File

@@ -65,6 +65,10 @@ export function convertRedirects(
headers: { Location: loc },
status,
};
if (r.has) {
route.has = r.has;
}
return route;
} catch (e) {
throw new Error(`Failed to parse redirect: ${JSON.stringify(r)}`);
@@ -78,6 +82,10 @@ export function convertRewrites(rewrites: Rewrite[]): Route[] {
try {
const dest = replaceSegments(segments, r.destination);
const route: Route = { src, dest, check: true };
if (r.has) {
route.has = r.has;
}
return route;
} catch (e) {
throw new Error(`Failed to parse rewrite: ${JSON.stringify(r)}`);
@@ -112,6 +120,10 @@ export function convertHeaders(headers: Header[]): Route[] {
headers: obj,
continue: true,
};
if (h.has) {
route.has = h.has;
}
return route;
});
}

View File

@@ -9,6 +9,12 @@ export type RouteApiError = {
errors?: string[]; // array of all error messages
};
export type HasField = Array<{
type: 'header' | 'cookie' | 'query';
key: string;
value?: string;
}>;
export type Source = {
src: string;
dest?: string;
@@ -18,6 +24,7 @@ export type Source = {
check?: boolean;
important?: boolean;
status?: number;
has?: HasField;
locale?: {
redirect?: Record<string, string>;
cookie?: string;
@@ -67,6 +74,7 @@ export interface VercelConfig {
export interface Rewrite {
source: string;
destination: string;
has?: HasField;
}
export interface Redirect {
@@ -74,11 +82,13 @@ export interface Redirect {
destination: string;
permanent?: boolean;
statusCode?: number;
has?: HasField;
}
export interface Header {
source: string;
headers: HeaderKeyValue[];
has?: HasField;
}
export interface HeaderKeyValue {

View File

@@ -82,6 +82,15 @@ describe('normalizeRoutes', () => {
dest: '/404',
status: 404,
},
{
src: '^/hello$',
dest: '/another',
has: [
{ type: 'header', key: 'x-rewrite' },
{ type: 'cookie', key: 'loggedIn', value: 'yup' },
{ type: 'query', key: 'authorized', value: 'yup' },
],
},
];
assertValid(routes);
@@ -942,11 +951,30 @@ describe('getTransformedRoutes', () => {
rewrites: [
{ source: '/page', destination: '/page.html' },
{ source: '/home', destination: '/index.html' },
{
source: '/home',
destination: '/another',
has: [
{ type: 'header', key: 'x-rewrite' },
{ type: 'cookie', key: 'loggedIn', value: 'yup' },
{ type: 'query', key: 'authorized', value: 'yup' },
],
},
],
redirects: [
{ source: '/version1', destination: '/api1.py' },
{ source: '/version2', destination: '/api2.py', statusCode: 302 },
{ source: '/version3', destination: '/api3.py', permanent: true },
{
source: '/version4',
destination: '/api4.py',
has: [
{ type: 'header', key: 'x-redirect' },
{ type: 'cookie', key: 'loggedIn', value: 'yup' },
{ type: 'query', key: 'authorized', value: 'yup' },
],
permanent: false,
},
],
headers: [
{
@@ -971,6 +999,20 @@ describe('getTransformedRoutes', () => {
},
],
},
{
source: '/add-header',
has: [
{ type: 'header', key: 'x-header' },
{ type: 'cookie', key: 'loggedIn', value: 'yup' },
{ type: 'query', key: 'authorized', value: 'yup' },
],
headers: [
{
key: 'Cache-Control',
value: 'max-age=forever',
},
],
},
],
trailingSlashSchema: false,
};

View File

@@ -198,6 +198,22 @@ test('convertRedirects', () => {
source: '/hello/:world',
destination: '/somewhere?else={:world}',
},
{
source: '/hello',
destination: '/another',
has: [
{
type: 'header',
key: 'x-rewrite',
},
{
type: 'cookie',
key: 'loggedIn',
value: '1',
},
],
permanent: false,
},
]);
const expected = [
@@ -288,6 +304,24 @@ test('convertRedirects', () => {
src: '^\\/hello(?:\\/([^\\/]+?))$',
status: 308,
},
{
has: [
{
key: 'x-rewrite',
type: 'header',
},
{
key: 'loggedIn',
type: 'cookie',
value: '1',
},
],
headers: {
Location: '/another',
},
src: '^\\/hello$',
status: 307,
},
];
deepEqual(actual, expected);
@@ -309,6 +343,7 @@ test('convertRedirects', () => {
['/optional', '/optional/1'],
['/feature-first', '/feature-second'],
['/hello/world', '/hello/again'],
['/hello'],
];
const mustNotMatch = [
@@ -328,6 +363,7 @@ test('convertRedirects', () => {
['/optionalnope', '/optionally'],
['/feature/first', '/feature'],
['/hello', '/hello/another/one'],
['/helloooo'],
];
assertRegexMatches(actual, mustMatch, mustNotMatch);
@@ -384,6 +420,21 @@ test('convertRewrites', () => {
source: '/hello/:world',
destination: '/somewhere?else={:world}',
},
{
source: '/hello',
destination: '/another',
has: [
{
type: 'header',
key: 'x-rewrite',
},
{
type: 'cookie',
key: 'loggedIn',
value: '1',
},
],
},
]);
const expected = [
@@ -470,6 +521,22 @@ test('convertRewrites', () => {
src: '^\\/hello(?:\\/([^\\/]+?))$',
check: true,
},
{
check: true,
dest: '/another',
has: [
{
key: 'x-rewrite',
type: 'header',
},
{
key: 'loggedIn',
type: 'cookie',
value: '1',
},
],
src: '^\\/hello$',
},
];
deepEqual(actual, expected);
@@ -493,6 +560,7 @@ test('convertRewrites', () => {
['/hello/post-123.html', '/post-123.html'],
['/feature-first', '/feature-second'],
['/hello/world', '/hello/again'],
['/hello'],
];
const mustNotMatch = [
@@ -514,6 +582,7 @@ test('convertRewrites', () => {
['/hello/post.html'],
['/feature/first', '/feature'],
['/hello', '/hello/another/one'],
['/hllooo'],
];
assertRegexMatches(actual, mustMatch, mustNotMatch);
@@ -610,6 +679,26 @@ test('convertHeaders', () => {
},
],
},
{
source: '/hello',
headers: [
{
key: 'x-header',
value: 'something',
},
],
has: [
{
key: 'x-rewrite',
type: 'header',
},
{
key: 'loggedIn',
type: 'cookie',
value: '1',
},
],
},
]);
const expected = [
@@ -647,6 +736,24 @@ test('convertHeaders', () => {
},
src: '^\\/like\\/params(?:\\/([^\\/]+?))$',
},
{
continue: true,
has: [
{
key: 'x-rewrite',
type: 'header',
},
{
key: 'loggedIn',
type: 'cookie',
value: '1',
},
],
headers: {
'x-header': 'something',
},
src: '^\\/hello$',
},
];
deepEqual(actual, expected);
@@ -656,6 +763,7 @@ test('convertHeaders', () => {
['404.html'],
['/blog/first-post', '/blog/another/one'],
['/like/params/first', '/like/params/second'],
['/hello'],
];
const mustNotMatch = [
@@ -663,6 +771,7 @@ test('convertHeaders', () => {
['403.html', '500.html'],
['/blogg', '/random'],
['/non-match', '/like/params', '/like/params/'],
['/hellooo'],
];
assertRegexMatches(actual, mustMatch, mustNotMatch);

View File

@@ -1798,13 +1798,6 @@
resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-4.1.1.tgz#b2d87a5e3df8d4b18ca426c5105cd701c2306d40"
integrity sha512-8mNEUG6diOrI6pMqOHrHPDBB1JsrpedeMK9AWGzVCQ7StRRribiT9BRvUmF8aUws9iBbVlgVekOT5Sgzc1MTKw==
"@types/micro@7.3.3":
version "7.3.3"
resolved "https://registry.yarnpkg.com/@types/micro/-/micro-7.3.3.tgz#31ead8df18ac10d58b7be1186d4b2d977b13a938"
integrity sha512-I3n3QYT7lqAxkyAoTZyg1yrvo38BxW/7ZafLAXZF/zZQOnAnQzg6j9XOuSmUEL5GGVFKWw4iqM+ZLnqb2154TA==
dependencies:
"@types/node" "*"
"@types/mime-types@2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.0.tgz#9ca52cda363f699c69466c2a6ccdaad913ea7a73"
@@ -4107,11 +4100,6 @@ delegates@^1.0.0:
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
depd@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
integrity sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=
depd@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
@@ -5645,16 +5633,6 @@ http-cache-semantics@^4.0.0:
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
http-errors@1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736"
integrity sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=
dependencies:
depd "1.1.1"
inherits "2.0.3"
setprototypeof "1.0.3"
statuses ">= 1.3.1 < 2"
http-errors@1.7.3:
version "1.7.3"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
@@ -5762,11 +5740,6 @@ husky@3.0.4:
run-node "^1.0.0"
slash "^3.0.0"
iconv-lite@0.4.19:
version "0.4.19"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@@ -5890,11 +5863,6 @@ inherits@2.0.1:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
inherits@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ini@^1.3.2, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
version "1.3.7"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84"
@@ -6281,7 +6249,7 @@ is-ssh@^1.3.0:
dependencies:
protocols "^1.1.0"
is-stream@1.1.0, is-stream@^1.0.1, is-stream@^1.1.0:
is-stream@^1.0.1, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
@@ -7569,16 +7537,6 @@ merge2@^1.2.3, merge2@^1.3.0:
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
micro@9.1.2:
version "9.1.2"
resolved "https://registry.yarnpkg.com/micro/-/micro-9.1.2.tgz#fabb79ca60bf7696551943d4b932dcd9eddbbc96"
integrity sha512-wteCRJBbzcatTkUSsD/IKqMiLkq1yK3KL5MlimiFxT8SWbVtD3R6CirpglildwRUvadItM9pScLM2AiBpqiCuQ==
dependencies:
content-type "1.0.4"
is-stream "1.1.0"
mri "1.1.0"
raw-body "2.3.2"
micro@9.3.5-canary.3:
version "9.3.5-canary.3"
resolved "https://registry.yarnpkg.com/micro/-/micro-9.3.5-canary.3.tgz#e957598abb9ab05aea8453e0150a521fe22135c3"
@@ -7775,11 +7733,6 @@ move-concurrently@^1.0.1:
rimraf "^2.5.4"
run-queue "^1.0.3"
mri@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.0.tgz#5c0a3f29c8ccffbbb1ec941dcec09d71fa32f36a"
integrity sha1-XAo/KcjM/7ux7JQdzsCdcfoy82o=
mri@1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.5.tgz#ce21dba2c69f74a9b7cf8a1ec62307e089e223e0"
@@ -8981,16 +8934,6 @@ range-parser@1.2.0:
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=
raw-body@2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89"
integrity sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=
dependencies:
bytes "3.0.0"
http-errors "1.6.2"
iconv-lite "0.4.19"
unpipe "1.0.0"
raw-body@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c"
@@ -9655,11 +9598,6 @@ set-value@^2.0.0, set-value@^2.0.1:
is-plain-object "^2.0.3"
split-string "^3.0.1"
setprototypeof@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04"
integrity sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=
setprototypeof@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
@@ -9971,7 +9909,7 @@ static-extend@^0.1.1:
define-property "^0.2.5"
object-copy "^0.1.0"
"statuses@>= 1.2.1 < 2", "statuses@>= 1.3.1 < 2", "statuses@>= 1.5.0 < 2":
"statuses@>= 1.2.1 < 2", "statuses@>= 1.5.0 < 2":
version "1.5.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=