Compare commits

...

10 Commits

Author SHA1 Message Date
Trek Glowacki
30ada9855e Add dynamic port assignment and FD 3 communication 2023-09-18 12:56:30 -05:00
Trek Glowacki
bfe60ec497 . 2023-09-15 12:26:59 -05:00
Trek Glowacki
3247bee5f4 WIP 2023-09-15 10:51:34 -05:00
Trek Glowacki
b0743a166a . 2023-09-14 12:48:38 -05:00
Trek Glowacki
a27b41e841 . 2023-09-14 12:48:17 -05:00
Zack Tanner
7a0fed970c [next] provide experimentalBypassFor to prerender from manifest (#10497) 2023-09-12 17:40:49 -05:00
Jimmy Lai
2f461a8b0b next.js: add option to use bundled runtime (#10485)
This PR adds an environment variable that should allow us to test the bundled version for Next.js on Vercel, see https://github.com/vercel/next.js/pull/52997 for reference.

The changes include:
- a new environment variable `VERCEL_NEXT_BUNDLED_SERVER`
- some logic changes that will put app route handlers into their own lambda groups
- extra logic to require early the rendering runtimes (see PR above for details)
2023-09-12 19:45:23 +00:00
Steven
ec894bdf7f [frameworks] Add bun install placeholder (#10492)
<img width="899" alt="image"
src="https://github.com/vercel/vercel/assets/229881/f37a3cfd-bbb9-4c33-88dc-cd19b9855a47">
2023-09-12 11:29:24 -04:00
Andy
009cea6d30 [examples] Use placeholder for API Key (#10490)
The Ionic example has an actual Google Maps API key by default and we'd
like to not have it displayed, so we'll replace it with a placeholder
instead. Considering it's commented out anyways, this will be a no-op.

---------

Co-authored-by: Steven <steven@ceriously.com>
2023-09-12 11:25:02 -04:00
Nathan Rajlich
1bab21026e [remix] Fix usage with bun install (#10489) 2023-09-12 09:55:16 -05:00
27 changed files with 512 additions and 77 deletions

View File

@@ -0,0 +1,2 @@
---
---

View File

@@ -0,0 +1,5 @@
---
"@vercel/next": patch
---
provide `experimentalBypassFor` to Prerender from manifest

View File

@@ -0,0 +1,5 @@
---
"@vercel/next": patch
---
next.js: move app route handlers in their own lambda grouping, add flag to use bundled runtime

View File

@@ -0,0 +1,5 @@
---
'@vercel/remix-builder': patch
---
Fix usage with `bun install`

View File

@@ -0,0 +1,5 @@
---
"@vercel/frameworks": patch
---
Add `bun install` placeholder

View File

@@ -29,7 +29,7 @@
<!-- Replace the API key with your own, see:
https://developers.google.com/maps/documentation/javascript/get-api-key -->
<!-- <script async="" defer="" src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB8pf6ZdFQj5qw7rc_HSGrhUwQKfIe9ICw"></script> -->
<!-- <script async="" defer="" src="https://maps.googleapis.com/maps/api/js?key=<YOUR_GOOGLE_MAPS_API_KEY>"></script> -->
<noscript>Please enable JavaScript to continue using this application.</noscript>
</body>

View File

@@ -978,6 +978,7 @@ export default class DevServer {
// log address without trailing slash to maintain backwards compatibility
addressFormatted = addressFormatted.replace(/\/$/, '');
}
this.output.ready(`Available at ${link(addressFormatted)}`);
}
@@ -1450,7 +1451,7 @@ export default class DevServer {
);
const middlewareBody = await middlewareRes.buffer();
console.error('Here?');
if (middlewareRes.status === 500 && middlewareBody.byteLength === 0) {
await this.sendError(
req,

View File

@@ -42,7 +42,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `blitz build`',
@@ -82,7 +83,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `next build`',
@@ -125,7 +127,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `gatsby build`',
@@ -214,7 +217,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
value: 'remix build',
@@ -252,7 +256,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install` or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
value: 'astro build',
@@ -299,7 +304,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `hexo generate`',
@@ -334,7 +340,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `npx @11ty/eleventy`',
@@ -371,7 +378,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `docusaurus build`',
@@ -457,7 +465,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `docusaurus-build`',
@@ -508,7 +517,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `preact build`',
@@ -555,7 +565,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `solid-start build`',
@@ -591,7 +602,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `dojo build`',
@@ -651,7 +663,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `ember build`',
@@ -696,7 +709,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `vue-cli-service build`',
@@ -749,7 +763,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `ng build && scully`',
@@ -784,7 +799,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `ng build`',
@@ -827,7 +843,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `ng build`',
@@ -885,7 +902,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `polymer build`',
@@ -944,7 +962,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `rollup -c`',
@@ -994,7 +1013,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `svelte-kit build`',
@@ -1032,7 +1052,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: 'vite build',
@@ -1066,7 +1087,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `react-scripts build`',
@@ -1129,7 +1151,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `react-scripts build`',
@@ -1188,7 +1211,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `gridsome build`',
@@ -1223,7 +1247,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `umi build`',
@@ -1267,7 +1292,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `sapper export`',
@@ -1302,7 +1328,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `saber build`',
@@ -1351,7 +1378,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `stencil build`',
@@ -1420,7 +1448,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `nuxt generate`',
@@ -1476,7 +1505,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
value: 'yarn rw deploy vercel',
@@ -1606,7 +1636,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `brunch build --production`',
@@ -1717,7 +1748,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
value: 'shopify hydrogen build',
@@ -1753,7 +1785,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `vite build`',
@@ -1787,7 +1820,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `vitepress build docs`',
@@ -1819,7 +1853,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `vuepress build src`',
@@ -1852,7 +1887,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `parcel build`',
@@ -1909,7 +1945,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run build` or `sanity build`',
@@ -1953,7 +1990,8 @@ export const frameworks = [
},
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
value: 'storybook build',
@@ -1975,7 +2013,8 @@ export const frameworks = [
description: 'No framework or an unoptimized framework.',
settings: {
installCommand: {
placeholder: '`yarn install`, `pnpm install`, or `npm install`',
placeholder:
'`yarn install`, `pnpm install`, `npm install`, or `bun install`',
},
buildCommand: {
placeholder: '`npm run vercel-build` or `npm run build`',

View File

@@ -65,6 +65,12 @@ const NEXT_DATA_MIDDLEWARE_RESOLVING_VERSION = 'v12.1.7-canary.33';
const EMPTY_ALLOW_QUERY_FOR_PRERENDERED_VERSION = 'v12.2.0';
const CORRECTED_MANIFESTS_VERSION = 'v12.2.0';
// related PR: https://github.com/vercel/next.js/pull/52997
const BUNDLED_SERVER_NEXT_VERSION = '13.4.20-canary.26';
const BUNDLED_SERVER_NEXT_PATH =
'next/dist/compiled/next-server/server.runtime.prod.js';
export async function serverBuild({
dynamicPages,
pagesDir,
@@ -305,16 +311,32 @@ export async function serverBuild({
let nextServerBuildTrace;
let instrumentationHookBuildTrace;
const useBundledServer =
semver.gte(nextVersion, BUNDLED_SERVER_NEXT_VERSION) &&
process.env.VERCEL_NEXT_BUNDLED_SERVER;
if (useBundledServer) {
debug('Using bundled Next.js server');
}
const nextServerFile = resolveFrom(
projectDir,
`${getNextServerPath(nextVersion)}/next-server.js`
useBundledServer
? BUNDLED_SERVER_NEXT_PATH
: `${getNextServerPath(nextVersion)}/next-server.js`
);
try {
// leverage next-server trace from build if available
nextServerBuildTrace = JSON.parse(
await fs.readFile(
path.join(entryPath, outputDirectory, 'next-server.js.nft.json'),
path.join(
entryPath,
outputDirectory,
useBundledServer
? 'next-minimal-server.js.nft.json'
: 'next-server.js.nft.json'
),
'utf8'
)
);
@@ -407,6 +429,7 @@ export async function serverBuild({
const apiPages: string[] = [];
const nonApiPages: string[] = [];
const appRouterPages: string[] = [];
const appRouteHandlers: string[] = [];
lambdaPageKeys.forEach(page => {
if (
@@ -428,7 +451,11 @@ export async function serverBuild({
}
if (lambdaAppPaths[page]) {
appRouterPages.push(page);
if (lambdaAppPaths[page].fsPath.endsWith('route.js')) {
appRouteHandlers.push(page);
} else {
appRouterPages.push(page);
}
} else if (pageMatchesApi(page)) {
apiPages.push(page);
} else {
@@ -596,7 +623,9 @@ export async function serverBuild({
)
.replace(
'__NEXT_SERVER_PATH__',
`${getNextServerPath(nextVersion)}/next-server.js`
useBundledServer
? BUNDLED_SERVER_NEXT_PATH
: `${getNextServerPath(nextVersion)}/next-server.js`
);
const appLauncher = launcher.replace(
@@ -632,6 +661,7 @@ export async function serverBuild({
const mergedPageKeys = [
...nonApiPages,
...appRouterPages,
...appRouteHandlers,
...apiPages,
...internalPages,
];
@@ -793,6 +823,10 @@ export async function serverBuild({
pageExtensions,
});
for (const group of pageLambdaGroups) {
group.isPages = true;
}
const appRouterLambdaGroups = await getPageLambdaGroups({
entryPath: projectDir,
config,
@@ -809,6 +843,22 @@ export async function serverBuild({
pageExtensions,
});
const appRouteHandlersLambdaGroups = await getPageLambdaGroups({
entryPath: projectDir,
config,
functionsConfigManifest,
pages: appRouteHandlers,
prerenderRoutes,
pageTraces,
compressedPages,
tracedPseudoLayer: tracedPseudoLayer.pseudoLayer,
initialPseudoLayer,
lambdaCompressedByteLimit,
initialPseudoLayerUncompressed: uncompressedInitialSize,
internalPages,
pageExtensions,
});
for (const group of appRouterLambdaGroups) {
if (!group.isPrerenders) {
group.isStreaming = true;
@@ -816,6 +866,14 @@ export async function serverBuild({
group.isAppRouter = true;
}
for (const group of appRouteHandlersLambdaGroups) {
if (!group.isPrerenders) {
group.isStreaming = true;
}
group.isAppRouter = true;
group.isAppRouteHandler = true;
}
const apiLambdaGroups = await getPageLambdaGroups({
entryPath: projectDir,
config,
@@ -857,6 +915,14 @@ export async function serverBuild({
pseudoLayerBytes: group.pseudoLayerBytes,
uncompressedLayerBytes: group.pseudoLayerUncompressedBytes,
})),
appRouteHandlersLambdaGroups: appRouteHandlersLambdaGroups.map(
group => ({
pages: group.pages,
isPrerender: group.isPrerenders,
pseudoLayerBytes: group.pseudoLayerBytes,
uncompressedLayerBytes: group.pseudoLayerUncompressedBytes,
})
),
nextServerLayerSize: initialPseudoLayer.pseudoLayerBytes,
},
null,
@@ -867,6 +933,7 @@ export async function serverBuild({
...pageLambdaGroups,
...appRouterLambdaGroups,
...apiLambdaGroups,
...appRouteHandlersLambdaGroups,
];
await detectLambdaLimitExceeding(

View File

@@ -29,6 +29,7 @@ const nextServer = new NextServer({
minimalMode: true,
customServer: false,
});
const requestHandler = nextServer.getRequestHandler();
module.exports = async (req: IncomingMessage, res: ServerResponse) => {

View File

@@ -856,6 +856,7 @@ export type NextPrerenderedRoutes = {
srcRoute: string | null;
initialStatus?: number;
initialHeaders?: Record<string, string>;
experimentalBypassFor?: HasField;
};
};
@@ -1084,6 +1085,7 @@ export async function getPrerenderManifest(
dataRoute: string | null;
initialStatus?: number;
initialHeaders?: Record<string, string>;
experimentalBypassFor?: HasField;
};
};
dynamicRoutes: {
@@ -1177,10 +1179,12 @@ export async function getPrerenderManifest(
let initialStatus: undefined | number;
let initialHeaders: undefined | Record<string, string>;
let experimentalBypassFor: undefined | HasField;
if (manifest.version === 4) {
initialStatus = manifest.routes[route].initialStatus;
initialHeaders = manifest.routes[route].initialHeaders;
experimentalBypassFor = manifest.routes[route].experimentalBypassFor;
}
ret.staticRoutes[route] = {
@@ -1192,6 +1196,7 @@ export async function getPrerenderManifest(
srcRoute,
initialStatus,
initialHeaders,
experimentalBypassFor,
};
});
@@ -1391,8 +1396,10 @@ export type LambdaGroup = {
memory?: number;
maxDuration?: number;
isAppRouter?: boolean;
isAppRouteHandler?: boolean;
isStreaming?: boolean;
isPrerenders?: boolean;
isPages?: boolean;
isApiLambda: boolean;
pseudoLayer: PseudoLayer;
pseudoLayerBytes: number;
@@ -1907,6 +1914,7 @@ export const onPrerenderRoute =
let dataRoute: string | null;
let initialStatus: number | undefined;
let initialHeaders: Record<string, string> | undefined;
let experimentalBypassFor: HasField | undefined;
if (isFallback || isBlocking) {
const pr = isFallback
@@ -1935,6 +1943,7 @@ export const onPrerenderRoute =
dataRoute,
initialHeaders,
initialStatus,
experimentalBypassFor,
} = pr);
}
@@ -2175,6 +2184,7 @@ export const onPrerenderRoute =
fallback: htmlFsRef,
group: prerenderGroup,
bypassToken: prerenderManifest.bypassToken,
experimentalBypassFor,
initialStatus,
initialHeaders,
sourcePath,

View File

@@ -0,0 +1 @@
.vercel

View File

@@ -0,0 +1,5 @@
export async function GET() {
return new Response('hello world from app route')
}
export const dynamic = 'force-dynamic'

View File

@@ -0,0 +1,5 @@
export default function Page() {
return <p>hello world from app router</p>
}
export const dynamic = 'force-dynamic'

View File

@@ -0,0 +1,10 @@
export default function Layout({children}) {
return <html>
<head>
<title>My page</title>
</head>
<body>
{children}
</body>
</html>
}

View File

@@ -0,0 +1,8 @@
const path = require('path');
const { deployAndTest } = require('../../utils');
describe(`${__dirname.split(path.sep).pop()}`, () => {
it('should deploy and pass probe checks', async () => {
await deployAndTest(__dirname);
});
});

View File

@@ -0,0 +1,15 @@
{
"name": "bundled-server",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
}
}

View File

@@ -0,0 +1,5 @@
export default async (req, res) => {
res.status(200).json({
payload: `hello world from api`
});
};

View File

@@ -0,0 +1,7 @@
export default function Home(props) {
return `hello world from pages`;
}
export async function getServerSideProps() {
return {};
}

View File

@@ -0,0 +1,24 @@
{
"probes": [
{
"path": "/app",
"status": 200,
"mustContain": "hello world from app router"
},
{
"path": "/app-route",
"status": 200,
"mustContain": "hello world from app route"
},
{
"path": "/api/hello",
"status": 200,
"mustContain": "hello world from api"
},
{
"path": "/",
"status": 200,
"mustContain": "hello world from pages"
}
]
}

View File

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

View File

@@ -72,7 +72,7 @@ if (parseInt(process.versions.node.split('.')[0], 10) >= 16) {
)
).toBeFalsy();
expect(lambdas.size).toBe(4);
expect(lambdas.size).toBe(5);
// RSC, root-level page.js
expect(buildResult.output['index']).toBeDefined();

View File

@@ -0,0 +1,36 @@
from http.server import HTTPServer
import os
import sys
__HANDLER_CLASS_TEMPLATE
if __name__ == "__main__":
hostName = "localhost"
errorMessage = 'Neither `app` nor `handler` defined in serverless function {}. See: https://vercel.com/docs/functions/serverless-functions/runtimes/python'.format(__file__)
if 'handler' in dir():
appOrHandler = handler
if 'app' in dir():
appOrHandler = app
if not 'appOrHandler' in dir():
raise Exception(errorMessage)
# Port 0 is unix-speak for 'first available port'
httpd = HTTPServer((hostName, 0), appOrHandler)
serverPort = httpd.socket.getsockname()[1]
print("Server started http://%s:%s" % (hostName, serverPort))
fd = os.open("pipe", os.O_RDWR|os.O_CREAT)
with os.fdopen(fd, 'w') as fdfile:
fdfile.write(str(serverPort))
fdfile.close()
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
print("Server stopped.")

View File

@@ -20,6 +20,7 @@
"test-e2e": "pnpm test test/integration-*"
},
"devDependencies": {
"@tootallnate/once": "1.1.2",
"@types/execa": "^0.9.0",
"@types/jest": "27.4.1",
"@types/node": "14.18.33",

View File

@@ -1,23 +1,37 @@
import { join, dirname, basename } from 'path';
import { spawn } from 'child_process';
import execa from 'execa';
import fs from 'fs';
import { promisify } from 'util';
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
import { tmpdir } from 'os';
import retry from 'async-retry';
import { Readable } from 'stream';
import once from '@tootallnate/once';
import {
GlobOptions,
BuildOptions,
getWriteableDirectory,
download,
StartDevServerOptions,
StartDevServerResult,
glob,
createLambda,
download,
getWriteableDirectory,
shouldServe,
debug,
NowBuildError,
cloneEnv,
} from '@vercel/build-utils';
import { readFile, writeFile, mkdirp, remove } from 'fs-extra';
import { GlobOptions, createLambda, NowBuildError } from '@vercel/build-utils';
import { installRequirement, installRequirementsFile } from './install';
import { getLatestPythonVersion, getSupportedPythonVersion } from './version';
const TMP = tmpdir();
function isReadable(v: any): v is Readable {
return v && v.readable === true;
}
async function pipenvConvert(cmd: string, srcDir: string) {
debug('Running pipfile2req...');
try {
@@ -204,8 +218,10 @@ export const build = async ({
: 'node_modules/**',
};
const files = await glob('**', globOptions);
console.log(files);
const lambda = await createLambda({
files: await glob('**', globOptions),
files,
handler: `${handlerPyFilename}.vc_handler`,
runtime: pythonVersion.runtime,
environment: {},
@@ -214,6 +230,146 @@ export const build = async ({
return { output: lambda };
};
interface PortInfo {
port: number;
}
function isPortInfo(v: any): v is PortInfo {
return v && typeof v.port === 'number';
}
export interface CancelablePromise<T> extends Promise<T> {
cancel: () => void;
}
function waitForPortFile(portFile: string) {
const opts = { portFile, canceled: false };
const promise = waitForPortFile_(opts) as CancelablePromise<PortInfo | void>;
promise.cancel = () => {
opts.canceled = true;
};
return promise;
}
async function waitForPortFile_(opts: {
portFile: string;
canceled: boolean;
}): Promise<PortInfo | void> {
while (!opts.canceled) {
await new Promise(resolve => setTimeout(resolve, 100));
try {
const port = Number(await readFile(opts.portFile, 'ascii'));
retry(() => remove(opts.portFile)).catch((err: Error) => {
console.error(`Could not delete port file: ${opts.portFile}: ${err}`);
});
return { port };
} catch (err: any) {
if (err.code !== 'ENOENT') {
throw err;
}
}
}
}
async function copyDevServer(
serverlessFunctionString: string,
dest: string
): Promise<void> {
const serverTemplate = await readFile(
join(__dirname, '../dev-server.py'),
'utf8'
);
const patched = serverTemplate.replace(
'__HANDLER_CLASS_TEMPLATE',
serverlessFunctionString
);
console.log('Writing vercel-dev-server-main.py to ', dest);
await writeFile(join(dest, 'vercel-dev-server-main.py'), patched);
}
export async function startDevServer(
opts: StartDevServerOptions
): Promise<StartDevServerResult> {
opts.config;
const { entrypoint, workPath, meta = {} } = opts;
const { devCacheDir = join(workPath, '.vercel', 'cache') } = meta;
const entrypointDir = dirname(entrypoint);
entrypointDir;
const tmp = join(
devCacheDir,
'python',
Math.random().toString(32).substring(2)
);
const tmpPackage = join(tmp, entrypointDir);
await mkdirp(tmpPackage);
const serverlessFunctionBody = await readFile(join(workPath, entrypoint));
await copyDevServer(serverlessFunctionBody, tmpPackage);
const portFile = join(
TMP,
`vercel-dev-port-${Math.random().toString(32).substring(2)}`
);
const env = cloneEnv(process.env, meta.env, {
VERCEL_DEV_PORT_FILE: portFile,
});
const executable = 'python3';
// run the dev server
debug(`SPAWNING ${executable} CWD=${tmp}`);
const child = spawn(executable, ['api/vercel-dev-server-main.py'], {
cwd: tmp,
env,
stdio: ['ignore', 'inherit', 'inherit', 'pipe'],
});
child.on('close', async () => {
try {
await retry(() => remove(tmp));
} catch (err: any) {
console.error(`Could not delete tmp directory: ${tmp}: ${err}`);
}
});
const portPipe = child.stdio[3];
if (!isReadable(portPipe)) {
throw new Error('File descriptor 3 is not readable');
}
// // `dev-server.python` writes the ephemeral port number to FD 3 to be consumed here
const onPort = new Promise<PortInfo>(resolve => {
portPipe.setEncoding('utf8');
portPipe.once('data', d => {
resolve({ port: Number(d) });
});
});
const onPortFile = waitForPortFile(portFile);
const onExit = once.spread<[number, string | null]>(child, 'exit');
const result = await Promise.race([onPort, onPortFile, onExit]);
onExit.cancel();
onPortFile.cancel();
console.log(`Hosting on http://127.0.0.1:${result.port}`);
if (isPortInfo(result)) {
return {
port: result.port,
pid: child.pid,
};
} else if (Array.isArray(result)) {
// Got "exit" event from child process
const [exitCode, signal] = result;
const reason = signal ? `"${signal}" signal` : `exit code ${exitCode}`;
throw new Error(`\`python3 ${entrypoint}\` failed with ${reason}`);
} else {
throw new Error(`Unexpected result type: ${typeof result}`);
}
}
export { shouldServe };
// internal only - expect breaking changes if other packages depend on these exports

View File

@@ -278,29 +278,41 @@ export function addDependencies(
}
const args: string[] = [];
if (cliType === 'npm' || cliType === 'pnpm') {
args.push('install');
if (opts.saveDev) {
args.push('--save-dev');
}
} else {
// 'yarn'
args.push('add');
if (opts.saveDev) {
args.push('--dev');
}
const yarnVersion = execSync('yarn -v', { encoding: 'utf8' }).trim();
const isYarnV1 = semver.satisfies(yarnVersion, '1');
if (isYarnV1) {
// Ignoring workspace check is only needed on Yarn v1
args.push('--ignore-workspace-root-check');
}
}
switch (cliType) {
case 'npm':
case 'pnpm':
args.push('install');
if (opts.saveDev) {
args.push('--save-dev');
}
// Don't fail if pnpm is being run at the workspace root
if (cliType === 'pnpm' && opts.cwd) {
if (existsSync(join(opts.cwd, 'pnpm-workspace.yaml'))) {
args.push('--workspace-root');
// Don't fail if pnpm is being run at the workspace root
if (cliType === 'pnpm' && opts.cwd) {
if (existsSync(join(opts.cwd, 'pnpm-workspace.yaml'))) {
args.push('--workspace-root');
}
}
break;
case 'bun':
case 'yarn':
args.push('add');
if (opts.saveDev) {
args.push('--dev');
}
if (cliType === 'yarn') {
const yarnVersion = execSync('yarn -v', { encoding: 'utf8' }).trim();
const isYarnV1 = semver.satisfies(yarnVersion, '1');
if (isYarnV1) {
// Ignoring workspace check is only needed on Yarn v1
args.push('--ignore-workspace-root-check');
}
}
break;
default: {
const n: never = cliType;
throw new Error(`Unexpected package manager: ${n}`);
}
}

3
pnpm-lock.yaml generated
View File

@@ -1337,6 +1337,9 @@ importers:
packages/python:
devDependencies:
'@tootallnate/once':
specifier: 1.1.2
version: 1.1.2
'@types/execa':
specifier: ^0.9.0
version: 0.9.0