Compare commits

...

6 Commits

Author SHA1 Message Date
Andy Bitz
3559531e4c Publish Canary
- @vercel/build-utils@2.12.3-canary.24
 - vercel@23.1.3-canary.42
 - @vercel/client@10.2.3-canary.25
 - @vercel/frameworks@0.5.1-canary.14
 - vercel-plugin-go@1.0.0-canary.9
 - vercel-plugin-node@1.12.2-canary.15
 - vercel-plugin-python@1.0.0-canary.10
 - vercel-plugin-ruby@1.0.0-canary.8
2021-11-25 12:01:09 +01:00
Leo Lamprecht
449a3b3648 Updated Remix template and ensured correct headers (#7064)
* Updated Remix Example

* Adjusted config as necessary

* Updated gitignore

* Fixed default Remix headers

* Removed useless files

* Fixed README

* Added newline
2021-11-25 11:57:57 +01:00
Steven
7bd338618c Publish Canary
- @vercel/build-utils@2.12.3-canary.23
 - vercel@23.1.3-canary.41
 - @vercel/client@10.2.3-canary.24
 - vercel-plugin-go@1.0.0-canary.8
 - vercel-plugin-node@1.12.2-canary.14
 - vercel-plugin-python@1.0.0-canary.9
 - vercel-plugin-ruby@1.0.0-canary.7
2021-11-24 22:03:54 -05:00
Steven
9048a6f584 [build-utils] Fix zero config routes for vercel build (#7063) 2021-11-24 22:00:49 -05:00
Steven
0cacb1bdac Publish Canary
- @vercel/build-utils@2.12.3-canary.22
 - vercel@23.1.3-canary.40
 - @vercel/client@10.2.3-canary.23
 - vercel-plugin-go@1.0.0-canary.7
 - vercel-plugin-node@1.12.2-canary.13
 - vercel-plugin-python@1.0.0-canary.8
 - vercel-plugin-ruby@1.0.0-canary.6
2021-11-24 18:12:26 -05:00
Steven
318bf35f82 [build-utils] Add support for writing routes-manifest.json (#7062)
* [build-utils] Add support for writing routes-manifest.json

* Add support for dynamicRoutes

* Add another test with multiple named params
2021-11-24 18:00:12 -05:00
30 changed files with 499 additions and 242 deletions

View File

@@ -1,8 +1,8 @@
node_modules
/.cache
/.vercel
/.output
.cache
.vercel
.output
/public/build
/api/build
public/build
api/build

View File

@@ -2,56 +2,33 @@
- [Remix Docs](https://remix.run/docs)
## Vercel Setup
## Deployment
First you'll need the [Vercel CLI](https://vercel.com/docs/cli):
After having run the `create-remix` command and selected "Vercel" as a deployment target, you only need to [import your Git repository](https://vercel.com/new) into Vercel, and it will be deployed.
If you'd like to avoid using a Git repository, you can also deploy the directory by running [Vercel CLI](https://vercel.com/cli):
```sh
npm i -g vercel
vercel
```
Before you can run the app in development, you need to link this project to a new Vercel project on your account.
**It is important that you use a new project. If you try to link this project to an existing project (like a Next.js site) you will have problems.**
```sh
$ vercel link
```
Follow the prompts, and when it's done you should be able to get started.
It is generally recommended to use a Git repository, because future commits will then automatically be deployed by Vercel, through its [Git Integration](https://vercel.com/docs/concepts/git).
## Development
You will be running two processes during development when using Vercel as your server.
- Your Vercel server in one
- The Remix development server in another
To run your Remix app locally, make sure your project's local dependencies are installed:
```sh
# in one tab
$ vercel dev
npm install
```
# in another
$ npm run dev
Afterwards, start the Remix development server like so:
```sh
npm run dev
```
Open up [http://localhost:3000](http://localhost:3000) and you should be ready to go!
If you'd rather run everything in a single tab, you can look at [concurrently](https://npm.im/concurrently) or similar tools to run both processes in one tab.
## Deploying
```sh
$ npm run build
# preview deployment
$ vercel
# production deployment
$ vercel --prod
```
### GitHub Automatic Deployments
For some reason the GitHub integration doesn't deploy the public folder. We're working with Vercel to figure this out.
For now, [you can set up a GitHub action with this config](https://gist.github.com/mcansh/91f8effda798b41bb373351fad217070) from our friend [@mcansh](https://github.com/mcansh).
If you're used to using the `vercel dev` command provided by [Vercel CLI](https://vercel.com/cli) instead, you can also use that, but it's not needed.

View File

@@ -1,4 +1,3 @@
import * as React from "react";
import {
Link,
Links,
@@ -7,23 +6,14 @@ import {
Outlet,
Scripts,
ScrollRestoration,
useCatch,
useLocation
useCatch
} from "remix";
import type { LinksFunction } from "remix";
import deleteMeRemixStyles from "~/styles/demos/remix.css";
import globalStylesUrl from "~/styles/global.css";
import darkStylesUrl from "~/styles/dark.css";
/**
* The `links` export is a function that returns an array of objects that map to
* the attributes for an HTML `<link>` element. These will load `<link>` tags on
* every route in the app, but individual routes can include their own links
* that are automatically unloaded when a user navigates away from the route.
*
* https://remix.run/api/app#links
*/
// https://remix.run/api/app#links
export let links: LinksFunction = () => {
return [
{ rel: "stylesheet", href: globalStylesUrl },
@@ -31,16 +21,12 @@ export let links: LinksFunction = () => {
rel: "stylesheet",
href: darkStylesUrl,
media: "(prefers-color-scheme: dark)"
},
{ rel: "stylesheet", href: deleteMeRemixStyles }
}
];
};
/**
* The root module's default export is a component that renders the current
* route via the `<Outlet />` component. Think of this as the global layout
* component for your app.
*/
// https://remix.run/api/conventions#default-export
// https://remix.run/api/conventions#route-filenames
export default function App() {
return (
<Document>
@@ -51,68 +37,27 @@ export default function App() {
);
}
function Document({
children,
title
}: {
children: React.ReactNode;
title?: string;
}) {
// https://remix.run/docs/en/v1/api/conventions#errorboundary
export function ErrorBoundary({ error }: { error: Error }) {
console.error(error);
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
{title ? <title>{title}</title> : null}
<Meta />
<Links />
</head>
<body>
{children}
<RouteChangeAnnouncement />
<ScrollRestoration />
<Scripts />
{process.env.NODE_ENV === "development" && <LiveReload />}
</body>
</html>
);
}
function Layout({ children }: React.PropsWithChildren<{}>) {
return (
<div className="remix-app">
<header className="remix-app__header">
<div className="container remix-app__header-content">
<Link to="/" title="Remix" className="remix-app__header-home-link">
<RemixLogo />
</Link>
<nav aria-label="Main navigation" className="remix-app__header-nav">
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<a href="https://remix.run/docs">Remix Docs</a>
</li>
<li>
<a href="https://github.com/remix-run/remix">GitHub</a>
</li>
</ul>
</nav>
</div>
</header>
<div className="remix-app__main">
<div className="container remix-app__main-content">{children}</div>
</div>
<footer className="remix-app__footer">
<div className="container remix-app__footer-content">
<p>&copy; You!</p>
</div>
</footer>
</div>
<Document title="Error!">
<Layout>
<div>
<h1>There was an error</h1>
<p>{error.message}</p>
<hr />
<p>
Hey, developer, you should replace this with what you want your
users to see.
</p>
</div>
</Layout>
</Document>
);
}
// https://remix.run/docs/en/v1/api/conventions#catchboundary
export function CatchBoundary() {
let caught = useCatch();
@@ -148,26 +93,68 @@ export function CatchBoundary() {
);
}
export function ErrorBoundary({ error }: { error: Error }) {
console.error(error);
function Document({
children,
title
}: {
children: React.ReactNode;
title?: string;
}) {
return (
<Document title="Error!">
<Layout>
<div>
<h1>There was an error</h1>
<p>{error.message}</p>
<hr />
<p>
Hey, developer, you should replace this with what you want your
users to see.
</p>
</div>
</Layout>
</Document>
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
{title ? <title>{title}</title> : null}
<Meta />
<Links />
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
{process.env.NODE_ENV === "development" && <LiveReload />}
</body>
</html>
);
}
function RemixLogo(props: React.ComponentPropsWithoutRef<"svg">) {
function Layout({ children }: { children: React.ReactNode }) {
return (
<div className="remix-app">
<header className="remix-app__header">
<div className="container remix-app__header-content">
<Link to="/" title="Remix" className="remix-app__header-home-link">
<RemixLogo />
</Link>
<nav aria-label="Main navigation" className="remix-app__header-nav">
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<a href="https://remix.run/docs">Remix Docs</a>
</li>
<li>
<a href="https://github.com/remix-run/remix">GitHub</a>
</li>
</ul>
</nav>
</div>
</header>
<div className="remix-app__main">
<div className="container remix-app__main-content">{children}</div>
</div>
<footer className="remix-app__footer">
<div className="container remix-app__footer-content">
<p>&copy; You!</p>
</div>
</footer>
</div>
);
}
function RemixLogo() {
return (
<svg
viewBox="0 0 659 165"
@@ -179,7 +166,6 @@ function RemixLogo(props: React.ComponentPropsWithoutRef<"svg">) {
width="106"
height="30"
fill="currentColor"
{...props}
>
<title id="remix-run-logo-title">Remix Logo</title>
<path d="M0 161V136H45.5416C53.1486 136 54.8003 141.638 54.8003 145V161H0Z M133.85 124.16C135.3 142.762 135.3 151.482 135.3 161H92.2283C92.2283 158.927 92.2653 157.03 92.3028 155.107C92.4195 149.128 92.5411 142.894 91.5717 130.304C90.2905 111.872 82.3473 107.776 67.7419 107.776H54.8021H0V74.24H69.7918C88.2407 74.24 97.4651 68.632 97.4651 53.784C97.4651 40.728 88.2407 32.816 69.7918 32.816H0V0H77.4788C119.245 0 140 19.712 140 51.2C140 74.752 125.395 90.112 105.665 92.672C122.32 96 132.057 105.472 133.85 124.16Z" />
@@ -190,58 +176,3 @@ function RemixLogo(props: React.ComponentPropsWithoutRef<"svg">) {
</svg>
);
}
/**
* Provides an alert for screen reader users when the route changes.
*/
const RouteChangeAnnouncement = React.memo(() => {
let [hydrated, setHydrated] = React.useState(false);
let [innerHtml, setInnerHtml] = React.useState("");
let location = useLocation();
React.useEffect(() => {
setHydrated(true);
}, []);
let firstRenderRef = React.useRef(true);
React.useEffect(() => {
// Skip the first render because we don't want an announcement on the
// initial page load.
if (firstRenderRef.current) {
firstRenderRef.current = false;
return;
}
let pageTitle = location.pathname === "/" ? "Home page" : document.title;
setInnerHtml(`Navigated to ${pageTitle}`);
}, [location.pathname]);
// Render nothing on the server. The live region provides no value unless
// scripts are loaded and the browser takes over normal routing.
if (!hydrated) {
return null;
}
return (
<div
aria-live="assertive"
aria-atomic
id="route-change-region"
style={{
border: "0",
clipPath: "inset(100%)",
clip: "rect(0 0 0 0)",
height: "1px",
margin: "-1px",
overflow: "hidden",
padding: "0",
position: "absolute",
width: "1px",
whiteSpace: "nowrap",
wordWrap: "normal"
}}
>
{innerHtml}
</div>
);
});

View File

@@ -96,3 +96,121 @@ input:where([type="search"]) {
margin-right: auto;
margin-left: auto;
}
.remix-app {
display: flex;
flex-direction: column;
min-height: 100vh;
min-height: calc(100vh - env(safe-area-inset-bottom));
}
.remix-app > * {
width: 100%;
}
.remix-app__header {
padding-top: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--color-border);
}
.remix-app__header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.remix-app__header-home-link {
width: 106px;
height: 30px;
color: var(--color-foreground);
}
.remix-app__header-nav ul {
list-style: none;
margin: 0;
display: flex;
align-items: center;
gap: 1.5em;
}
.remix-app__header-nav li {
font-weight: bold;
}
.remix-app__main {
flex: 1 1 100%;
}
.remix-app__footer {
padding-top: 1rem;
padding-bottom: 1rem;
border-top: 1px solid var(--color-border);
}
.remix-app__footer-content {
display: flex;
justify-content: center;
align-items: center;
}
.remix__page {
--gap: 1rem;
--space: 2rem;
display: grid;
grid-auto-rows: min-content;
gap: var(--gap);
padding-top: var(--space);
padding-bottom: var(--space);
}
@media print, screen and (min-width: 640px) {
.remix__page {
--gap: 2rem;
grid-auto-rows: unset;
grid-template-columns: repeat(2, 1fr);
}
}
@media screen and (min-width: 1024px) {
.remix__page {
--gap: 4rem;
}
}
.remix__page > main > :first-child {
margin-top: 0;
}
.remix__page > main > :last-child {
margin-bottom: 0;
}
.remix__page > aside {
margin: 0;
padding: 1.5ch 2ch;
border: solid 1px var(--color-border);
border-radius: 0.5rem;
}
.remix__page > aside > :first-child {
margin-top: 0;
}
.remix__page > aside > :last-child {
margin-bottom: 0;
}
.remix__form {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1rem;
border: 1px solid var(--color-border);
border-radius: 0.5rem;
}
.remix__form > * {
margin-top: 0;
margin-bottom: 0;
}

View File

@@ -9,14 +9,15 @@
"postinstall": "remix setup node"
},
"dependencies": {
"@remix-run/react": "^1.0.4",
"@remix-run/react": "^1.0.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"remix": "^1.0.4",
"@remix-run/vercel": "^1.0.4"
"remix": "^1.0.5",
"@remix-run/serve": "^1.0.5",
"@remix-run/vercel": "^1.0.5"
},
"devDependencies": {
"@remix-run/dev": "^1.0.4",
"@remix-run/dev": "^1.0.5",
"@types/react": "^17.0.24",
"@types/react-dom": "^17.0.9",
"typescript": "^4.1.2"

View File

@@ -1,7 +1,7 @@
{
"build": {
"env": {
"ENABLE_FILE_SYSTEM_API": "1"
}
"build": {
"env": {
"ENABLE_FILE_SYSTEM_API": "1"
}
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "2.12.3-canary.21",
"version": "2.12.3-canary.24",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -30,7 +30,7 @@
"@types/node-fetch": "^2.1.6",
"@types/semver": "6.0.0",
"@types/yazl": "^2.4.1",
"@vercel/frameworks": "0.5.1-canary.13",
"@vercel/frameworks": "0.5.1-canary.14",
"@vercel/ncc": "0.24.0",
"aggregate-error": "3.0.1",
"async-retry": "1.2.3",

View File

@@ -21,7 +21,10 @@ export function convertRuntimeToPlugin(
vercelConfig,
workPath,
}: {
vercelConfig: { functions?: BuilderFunctions; regions?: string[] };
vercelConfig: {
functions?: BuilderFunctions;
regions?: string[];
};
workPath: string;
}) {
const opts = { cwd: workPath };
@@ -58,7 +61,7 @@ export function convertRuntimeToPlugin(
maxDuration: output.maxDuration,
environment: output.environment,
allowQuery: output.allowQuery,
regions: output.regions,
//regions: output.regions,
};
// @ts-ignore This symbol is a private API
@@ -172,32 +175,81 @@ export async function updateFunctionsManifest({
}
/**
* Will append routes to the `routes-manifest.json` file.
* If the file does not exist, it'll be created.
* Append routes to the `routes-manifest.json` file.
* If the file does not exist, it will be created.
*/
export async function updateRoutesManifest({
workPath,
redirects,
rewrites,
headers,
dynamicRoutes,
staticRoutes,
}: {
workPath: string;
redirects?: {
source: string;
destination: string;
statusCode: number;
regex: string;
}[];
rewrites?: {
source: string;
destination: string;
regex: string;
}[];
headers?: {
source: string;
headers: {
key: string;
value: string;
}[];
regex: string;
}[];
dynamicRoutes?: {
page: string;
regex: string;
namedRegex?: string;
routeKeys?: { [named: string]: string };
}[];
staticRoutes?: {
page: string;
regex: string;
namedRegex?: string;
routeKeys?: { [named: string]: string };
}[];
}) {
const routesManifestPath = join(workPath, '.output', 'routes-manifest.json');
const routesManifest = await readJson(routesManifestPath);
if (!routesManifest.version) routesManifest.version = 1;
if (!routesManifest.version) routesManifest.version = 3;
if (routesManifest.pages404 === undefined) routesManifest.pages404 = true;
if (redirects) {
if (!routesManifest.redirects) routesManifest.redirects = [];
routesManifest.redirects.push(...redirects);
}
if (rewrites) {
if (!routesManifest.rewrites) routesManifest.rewrites = [];
routesManifest.rewrites.push(...rewrites);
}
if (headers) {
if (!routesManifest.headers) routesManifest.headers = [];
routesManifest.headers.push(...headers);
}
if (dynamicRoutes) {
if (!routesManifest.dynamicRoutes) routesManifest.dynamicRoutes = [];
routesManifest.dynamicRoutes.push(...dynamicRoutes);
}
if (staticRoutes) {
if (!routesManifest.staticRoutes) routesManifest.staticRoutes = [];
routesManifest.staticRoutes.push(...staticRoutes);
}
await fs.writeFile(routesManifestPath, JSON.stringify(routesManifest));
}

View File

@@ -96,6 +96,7 @@ export async function detectBuilders(
redirectRoutes: Route[] | null;
rewriteRoutes: Route[] | null;
errorRoutes: Route[] | null;
limitedRoutes: LimitedRoutes | null;
}> {
const errors: ErrorResponse[] = [];
const warnings: ErrorResponse[] = [];
@@ -114,6 +115,7 @@ export async function detectBuilders(
redirectRoutes: null,
rewriteRoutes: null,
errorRoutes: null,
limitedRoutes: null,
};
}
@@ -179,6 +181,7 @@ export async function detectBuilders(
redirectRoutes: null,
rewriteRoutes: null,
errorRoutes: null,
limitedRoutes: null,
};
}
@@ -257,6 +260,7 @@ export async function detectBuilders(
defaultRoutes: null,
rewriteRoutes: null,
errorRoutes: null,
limitedRoutes: null,
};
}
@@ -299,6 +303,7 @@ export async function detectBuilders(
defaultRoutes: null,
rewriteRoutes: null,
errorRoutes: null,
limitedRoutes: null,
};
}
@@ -326,6 +331,7 @@ export async function detectBuilders(
}
const routesResult = getRouteResult(
pkg,
apiRoutes,
dynamicRoutes,
usedOutputDirectory,
@@ -342,6 +348,7 @@ export async function detectBuilders(
defaultRoutes: routesResult.defaultRoutes,
rewriteRoutes: routesResult.rewriteRoutes,
errorRoutes: routesResult.errorRoutes,
limitedRoutes: routesResult.limitedRoutes,
};
}
@@ -932,7 +939,14 @@ function createRouteFromPath(
return { route, isDynamic };
}
interface LimitedRoutes {
defaultRoutes: Route[];
redirectRoutes: Route[];
rewriteRoutes: Route[];
}
function getRouteResult(
pkg: PackageJson | undefined | null,
apiRoutes: Source[],
dynamicRoutes: Source[],
outputDirectory: string,
@@ -944,11 +958,18 @@ function getRouteResult(
redirectRoutes: Route[];
rewriteRoutes: Route[];
errorRoutes: Route[];
limitedRoutes: LimitedRoutes;
} {
const deps = Object.assign({}, pkg?.dependencies, pkg?.devDependencies);
const defaultRoutes: Route[] = [];
const redirectRoutes: Route[] = [];
const rewriteRoutes: Route[] = [];
const errorRoutes: Route[] = [];
const limitedRoutes: LimitedRoutes = {
defaultRoutes: [],
redirectRoutes: [],
rewriteRoutes: [],
};
const framework = frontendBuilder?.config?.framework || '';
const isNextjs =
framework === 'nextjs' || isOfficialRuntime('next', frontendBuilder?.use);
@@ -956,14 +977,43 @@ function getRouteResult(
if (apiRoutes && apiRoutes.length > 0) {
if (options.featHandleMiss) {
// Exclude extension names if the corresponding plugin is not found in package.json
// detectBuilders({ignoreRoutesForBuilders: ['@vercel/python']})
// return a copy of routes.
// We should exclud errorRoutes and
const extSet = detectApiExtensions(apiBuilders);
const withTag = options.tag ? `@${options.tag}` : '';
const extSetLimited = detectApiExtensions(
apiBuilders.filter(b => {
if (
b.use === `@vercel/python${withTag}` &&
!('vercel-plugin-python' in deps)
) {
return false;
}
if (
b.use === `@vercel/go${withTag}` &&
!('vercel-plugin-go' in deps)
) {
return false;
}
if (
b.use === `@vercel/ruby${withTag}` &&
!('vercel-plugin-ruby' in deps)
) {
return false;
}
return true;
})
);
if (extSet.size > 0) {
const exts = Array.from(extSet)
const extGroup = `(?:\\.(?:${Array.from(extSet)
.map(ext => ext.slice(1))
.join('|');
const extGroup = `(?:\\.(?:${exts}))`;
.join('|')}))`;
const extGroupLimited = `(?:\\.(?:${Array.from(extSetLimited)
.map(ext => ext.slice(1))
.join('|')}))`;
if (options.cleanUrls) {
redirectRoutes.push({
@@ -979,6 +1029,20 @@ function getRouteResult(
},
status: 308,
});
limitedRoutes.redirectRoutes.push({
src: `^/(api(?:.+)?)/index${extGroupLimited}?/?$`,
headers: { Location: options.trailingSlash ? '/$1/' : '/$1' },
status: 308,
});
limitedRoutes.redirectRoutes.push({
src: `^/api/(.+)${extGroupLimited}/?$`,
headers: {
Location: options.trailingSlash ? '/api/$1/' : '/api/$1',
},
status: 308,
});
} else {
defaultRoutes.push({ handle: 'miss' });
defaultRoutes.push({
@@ -986,10 +1050,18 @@ function getRouteResult(
dest: '/api/$1',
check: true,
});
limitedRoutes.defaultRoutes.push({ handle: 'miss' });
limitedRoutes.defaultRoutes.push({
src: `^/api/(.+)${extGroupLimited}$`,
dest: '/api/$1',
check: true,
});
}
}
rewriteRoutes.push(...dynamicRoutes);
limitedRoutes.rewriteRoutes.push(...dynamicRoutes);
if (typeof ignoreRuntimes === 'undefined') {
// This route is only necessary to hide the directory listing
@@ -1040,6 +1112,7 @@ function getRouteResult(
redirectRoutes,
rewriteRoutes,
errorRoutes,
limitedRoutes,
};
}

View File

@@ -0,0 +1 @@
# users.rb

View File

@@ -1,9 +1,9 @@
{
"functions": {
"api/users/post.py": {
"api/users.rb": {
"memory": 3008
},
"api/not-matching-anything.py": {
"api/doesnt-exist.rb": {
"memory": 768
}
}

View File

@@ -0,0 +1 @@
# [id].py

View File

@@ -0,0 +1 @@
# project/[aid]/[bid]/index.py

View File

@@ -0,0 +1,7 @@
{
"functions": {
"api/users/post.py": {
"memory": 3008
}
}
}

View File

@@ -2385,13 +2385,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
{
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
const { defaultRoutes, rewriteRoutes, errorRoutes } = await detectBuilders(
files,
null,
{
const { defaultRoutes, rewriteRoutes, errorRoutes, limitedRoutes } =
await detectBuilders(files, null, {
featHandleMiss,
}
);
});
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
@@ -2414,6 +2411,22 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
},
]);
// Limited routes should have js but not go since the go plugin is not installed
expect(limitedRoutes).toStrictEqual({
redirectRoutes: [],
rewriteRoutes: [],
defaultRoutes: [
{
handle: 'miss',
},
{
src: '^/api/(.+)(?:\\.(?:js))$',
dest: '/api/$1',
check: true,
},
],
});
const pattern = new RegExp(errorRoutes![0].src!);
[
@@ -2816,8 +2829,13 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
{
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
const { defaultRoutes, redirectRoutes, rewriteRoutes, errorRoutes } =
await detectBuilders(files, null, options);
const {
defaultRoutes,
redirectRoutes,
rewriteRoutes,
errorRoutes,
limitedRoutes,
} = await detectBuilders(files, null, options);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([]);
expect(rewriteRoutes).toStrictEqual([
@@ -2834,6 +2852,28 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
},
]);
// Limited routes should have js but not go since the go plugin is not installed
expect(limitedRoutes).toStrictEqual({
redirectRoutes: [
{
src: '^/(api(?:.+)?)/index(?:\\.(?:js))?/?$',
headers: {
Location: '/$1',
},
status: 308,
},
{
src: '^/api/(.+)(?:\\.(?:js))/?$',
headers: {
Location: '/api/$1',
},
status: 308,
},
],
rewriteRoutes: [],
defaultRoutes: [],
});
// expected redirect should match inputs
const getLocation = createReplaceLocation(redirectRoutes);
@@ -3077,7 +3117,7 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
{
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
const { defaultRoutes, redirectRoutes, rewriteRoutes } =
const { defaultRoutes, redirectRoutes, rewriteRoutes, limitedRoutes } =
await detectBuilders(files, null, options);
testHeaders(redirectRoutes);
expect(defaultRoutes).toStrictEqual([]);
@@ -3088,6 +3128,28 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
},
]);
// Limited routes should have js but not go since the go plugin is not installed
expect(limitedRoutes).toStrictEqual({
redirectRoutes: [
{
src: '^/(api(?:.+)?)/index(?:\\.(?:js))?/?$',
headers: {
Location: '/$1/',
},
status: 308,
},
{
src: '^/api/(.+)(?:\\.(?:js))/?$',
headers: {
Location: '/api/$1/',
},
status: 308,
},
],
rewriteRoutes: [],
defaultRoutes: [],
});
// expected redirect should match inputs
const getLocation = createReplaceLocation(redirectRoutes);

View File

@@ -18,21 +18,27 @@ async function fsToJson(dir: string, output: Record<string, any> = {}) {
return output;
}
const workPath = join(__dirname, 'walk', 'python-api');
const invalidFuncWorkpath = join(
__dirname,
'convert-runtime',
'invalid-functions'
);
const pythonApiWorkpath = join(__dirname, 'convert-runtime', 'python-api');
describe('convert-runtime-to-plugin', () => {
afterEach(async () => {
await fs.remove(join(workPath, '.output'));
await fs.remove(join(invalidFuncWorkpath, '.output'));
await fs.remove(join(pythonApiWorkpath, '.output'));
});
it('should create correct fileystem for python', async () => {
const workPath = pythonApiWorkpath;
const lambdaOptions = {
handler: 'index.handler',
runtime: 'python3.9',
memory: 512,
maxDuration: 5,
environment: {},
regions: ['sfo1'],
};
const buildRuntime = async (opts: BuildOptions) => {
@@ -84,10 +90,19 @@ describe('convert-runtime-to-plugin', () => {
expect(indexJson).toMatchObject({
version: 1,
files: [
{
input: '../../../../runtime-traced-files/api/db/[id].py',
output: 'api/db/[id].py',
},
{
input: '../../../../runtime-traced-files/api/index.py',
output: 'api/index.py',
},
{
input:
'../../../../runtime-traced-files/api/project/[aid]/[bid]/index.py',
output: 'api/project/[aid]/[bid]/index.py',
},
{
input: '../../../../runtime-traced-files/api/users/get.py',
output: 'api/users/get.py',
@@ -117,10 +132,19 @@ describe('convert-runtime-to-plugin', () => {
expect(getJson).toMatchObject({
version: 1,
files: [
{
input: '../../../../../runtime-traced-files/api/db/[id].py',
output: 'api/db/[id].py',
},
{
input: '../../../../../runtime-traced-files/api/index.py',
output: 'api/index.py',
},
{
input:
'../../../../../runtime-traced-files/api/project/[aid]/[bid]/index.py',
output: 'api/project/[aid]/[bid]/index.py',
},
{
input: '../../../../../runtime-traced-files/api/users/get.py',
output: 'api/users/get.py',
@@ -150,10 +174,19 @@ describe('convert-runtime-to-plugin', () => {
expect(postJson).toMatchObject({
version: 1,
files: [
{
input: '../../../../../runtime-traced-files/api/db/[id].py',
output: 'api/db/[id].py',
},
{
input: '../../../../../runtime-traced-files/api/index.py',
output: 'api/index.py',
},
{
input:
'../../../../../runtime-traced-files/api/project/[aid]/[bid]/index.py',
output: 'api/project/[aid]/[bid]/index.py',
},
{
input: '../../../../../runtime-traced-files/api/users/get.py',
output: 'api/users/get.py',

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "23.1.3-canary.39",
"version": "23.1.3-canary.42",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -43,14 +43,14 @@
"node": ">= 12"
},
"dependencies": {
"@vercel/build-utils": "2.12.3-canary.21",
"@vercel/build-utils": "2.12.3-canary.24",
"@vercel/go": "1.2.4-canary.4",
"@vercel/node": "1.12.2-canary.7",
"@vercel/python": "2.1.2-canary.0",
"@vercel/ruby": "1.2.8-canary.4",
"update-notifier": "4.1.0",
"vercel-plugin-middleware": "0.0.0-canary.7",
"vercel-plugin-node": "1.12.2-canary.12"
"vercel-plugin-node": "1.12.2-canary.15"
},
"devDependencies": {
"@next/env": "11.1.2",
@@ -90,7 +90,7 @@
"@types/update-notifier": "5.1.0",
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@vercel/frameworks": "0.5.1-canary.13",
"@vercel/frameworks": "0.5.1-canary.14",
"@vercel/ncc": "0.24.0",
"@vercel/nft": "0.17.0",
"@zeit/fun": "0.11.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "10.2.3-canary.22",
"version": "10.2.3-canary.25",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -40,7 +40,7 @@
]
},
"dependencies": {
"@vercel/build-utils": "2.12.3-canary.21",
"@vercel/build-utils": "2.12.3-canary.24",
"@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.5.1-canary.13",
"version": "0.5.1-canary.14",
"main": "./dist/frameworks.js",
"types": "./dist/frameworks.d.ts",
"files": [

View File

@@ -251,8 +251,8 @@ export const frameworks = [
],
defaultHeaders: [
{
source: '^/build/(.*)$',
regex: '^/build/(.*)$',
source: '/build/(.*)',
regex: '/build/(.*)',
headers: [
{ key: 'cache-control', value: 'public, max-age=31536000, immutable' },
],

View File

@@ -1,7 +1,7 @@
{
"private": false,
"name": "vercel-plugin-go",
"version": "1.0.0-canary.6",
"version": "1.0.0-canary.9",
"main": "dist/index.js",
"license": "MIT",
"files": [
@@ -17,7 +17,7 @@
"prepublishOnly": "tsc"
},
"dependencies": {
"@vercel/build-utils": "2.12.3-canary.21",
"@vercel/build-utils": "2.12.3-canary.24",
"@vercel/go": "1.2.4-canary.4"
},
"devDependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "vercel-plugin-node",
"version": "1.12.2-canary.12",
"version": "1.12.2-canary.15",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -34,7 +34,7 @@
"@types/node-fetch": "2",
"@types/test-listen": "1.1.0",
"@types/yazl": "2.4.2",
"@vercel/build-utils": "2.12.3-canary.21",
"@vercel/build-utils": "2.12.3-canary.24",
"@vercel/fun": "1.0.3",
"@vercel/ncc": "0.24.0",
"@vercel/nft": "0.14.0",

View File

@@ -1,7 +1,7 @@
{
"private": false,
"name": "vercel-plugin-python",
"version": "1.0.0-canary.7",
"version": "1.0.0-canary.10",
"main": "dist/index.js",
"license": "MIT",
"files": [
@@ -17,7 +17,7 @@
"prepublishOnly": "tsc"
},
"dependencies": {
"@vercel/build-utils": "2.12.3-canary.21",
"@vercel/build-utils": "2.12.3-canary.24",
"@vercel/python": "2.1.2-canary.0"
},
"devDependencies": {

View File

@@ -1,7 +1,7 @@
{
"private": false,
"name": "vercel-plugin-ruby",
"version": "1.0.0-canary.5",
"version": "1.0.0-canary.8",
"main": "dist/index.js",
"license": "MIT",
"files": [
@@ -17,7 +17,7 @@
"prepublishOnly": "tsc"
},
"dependencies": {
"@vercel/build-utils": "2.12.3-canary.21",
"@vercel/build-utils": "2.12.3-canary.24",
"@vercel/ruby": "1.2.8-canary.4"
},
"devDependencies": {