mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-11 04:22:13 +00:00
Compare commits
31 Commits
vercel-plu
...
@vercel/cl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba7bf2e4a6 | ||
|
|
00641037fc | ||
|
|
6f4a1b527b | ||
|
|
1b95576dd2 | ||
|
|
9227471aca | ||
|
|
bf060296eb | ||
|
|
9b3aa41f2e | ||
|
|
ae36585cdb | ||
|
|
e4c636ddd2 | ||
|
|
ae3b25be4b | ||
|
|
a64ed13a40 | ||
|
|
6c1c0e6676 | ||
|
|
82fdd5d121 | ||
|
|
8b40f4435e | ||
|
|
38c87602bb | ||
|
|
7aef3013e7 | ||
|
|
c18676ab4d | ||
|
|
df450c815d | ||
|
|
792ab38760 | ||
|
|
0bba3e76c1 | ||
|
|
3d961ffbb9 | ||
|
|
a3039f57bb | ||
|
|
5499fa9a04 | ||
|
|
b9fd64faff | ||
|
|
1202ff7b2b | ||
|
|
abd9f019f1 | ||
|
|
edb5eead81 | ||
|
|
6b865ff753 | ||
|
|
4fd0734c48 | ||
|
|
f815421acb | ||
|
|
5da926fee1 |
@@ -5,7 +5,7 @@
|
||||
"description": "API for the vercel/vercel repo",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"vercel-build": "yarn --cwd .. && node ../utils/run.js build all"
|
||||
"vercel-build": "node ../utils/run.js build all"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/node": "5.11.1",
|
||||
|
||||
15787
examples/nextjs/package-lock.json
generated
Normal file
15787
examples/nextjs/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2
examples/remix/.gitignore
vendored
2
examples/remix/.gitignore
vendored
@@ -5,4 +5,4 @@ node_modules
|
||||
.output
|
||||
|
||||
public/build
|
||||
api/build
|
||||
api/_build
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const { createRequestHandler } = require("@remix-run/vercel");
|
||||
|
||||
module.exports = createRequestHandler({
|
||||
build: require("./build")
|
||||
build: require("./_build")
|
||||
});
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* You probably want to just delete this file; it's just for the demo pages.
|
||||
*/
|
||||
.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;
|
||||
}
|
||||
8345
examples/remix/package-lock.json
generated
Normal file
8345
examples/remix/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,15 +9,15 @@
|
||||
"postinstall": "remix setup node"
|
||||
},
|
||||
"dependencies": {
|
||||
"@remix-run/react": "^1.0.5",
|
||||
"@remix-run/react": "^1.0.6",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"remix": "^1.0.5",
|
||||
"@remix-run/serve": "^1.0.5",
|
||||
"@remix-run/vercel": "^1.0.5"
|
||||
"remix": "^1.0.6",
|
||||
"@remix-run/serve": "^1.0.6",
|
||||
"@remix-run/vercel": "^1.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@remix-run/dev": "^1.0.5",
|
||||
"@remix-run/dev": "^1.0.6",
|
||||
"@types/react": "^17.0.24",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"typescript": "^4.1.2"
|
||||
|
||||
@@ -5,5 +5,5 @@ module.exports = {
|
||||
appDirectory: "app",
|
||||
browserBuildDirectory: "public/build",
|
||||
publicPath: "/build/",
|
||||
serverBuildDirectory: "api/build"
|
||||
serverBuildDirectory: "api/_build"
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "2.12.3-canary.24",
|
||||
"version": "2.12.3-canary.32",
|
||||
"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.14",
|
||||
"@vercel/frameworks": "0.5.1-canary.16",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"aggregate-error": "3.0.1",
|
||||
"async-retry": "1.2.3",
|
||||
|
||||
@@ -1,75 +1,190 @@
|
||||
import fs from 'fs-extra';
|
||||
import { join, dirname, relative } from 'path';
|
||||
import { join, dirname, parse, relative } from 'path';
|
||||
import glob from './fs/glob';
|
||||
import { normalizePath } from './fs/normalize-path';
|
||||
import { FILES_SYMBOL, getLambdaOptionsFromFunction, Lambda } from './lambda';
|
||||
import { FILES_SYMBOL, Lambda } from './lambda';
|
||||
import type FileBlob from './file-blob';
|
||||
import type { BuilderFunctions, BuildOptions, Files } from './types';
|
||||
import minimatch from 'minimatch';
|
||||
import type { BuildOptions, Files } from './types';
|
||||
import { getIgnoreFilter } from '.';
|
||||
|
||||
// `.output` was already created by the Build Command, so we have
|
||||
// to ensure its contents don't get bundled into the Lambda. Similarily,
|
||||
// we don't want to bundle anything from `.vercel` either. Lastly,
|
||||
// Builders/Runtimes didn't have `vercel.json` or `now.json`.
|
||||
const ignoredPaths = ['.output', '.vercel', 'vercel.json', 'now.json'];
|
||||
|
||||
const shouldIgnorePath = (
|
||||
file: string,
|
||||
ignoreFilter: any,
|
||||
ignoreFile: boolean
|
||||
) => {
|
||||
const isNative = ignoredPaths.some(item => {
|
||||
return file.startsWith(item);
|
||||
});
|
||||
|
||||
if (!ignoreFile) {
|
||||
return isNative;
|
||||
}
|
||||
|
||||
return isNative || ignoreFilter(file);
|
||||
};
|
||||
|
||||
const getSourceFiles = async (workPath: string, ignoreFilter: any) => {
|
||||
const list = await glob('**', {
|
||||
cwd: workPath,
|
||||
});
|
||||
|
||||
// We're not passing this as an `ignore` filter to the `glob` function above,
|
||||
// so that we can re-use exactly the same `getIgnoreFilter` method that the
|
||||
// Build Step uses (literally the same code). Note that this exclusion only applies
|
||||
// when deploying. Locally, another exclusion is needed, which is handled
|
||||
// further below in the `convertRuntimeToPlugin` function.
|
||||
for (const file in list) {
|
||||
if (shouldIgnorePath(file, ignoreFilter, true)) {
|
||||
delete list[file];
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert legacy Runtime to a Plugin.
|
||||
* @param buildRuntime - a legacy build() function from a Runtime
|
||||
* @param packageName - the name of the package, for example `vercel-plugin-python`
|
||||
* @param ext - the file extension, for example `.py`
|
||||
*/
|
||||
export function convertRuntimeToPlugin(
|
||||
buildRuntime: (options: BuildOptions) => Promise<{ output: Lambda }>,
|
||||
packageName: string,
|
||||
ext: string
|
||||
) {
|
||||
// This `build()` signature should match `plugin.build()` signature in `vercel build`.
|
||||
return async function build({
|
||||
vercelConfig,
|
||||
workPath,
|
||||
}: {
|
||||
vercelConfig: {
|
||||
functions?: BuilderFunctions;
|
||||
regions?: string[];
|
||||
};
|
||||
workPath: string;
|
||||
}) {
|
||||
const opts = { cwd: workPath };
|
||||
const files = await glob('**', opts);
|
||||
delete files['vercel.json']; // Builders/Runtimes didn't have vercel.json
|
||||
const entrypoints = await glob(`api/**/*${ext}`, opts);
|
||||
return async function build({ workPath }: { workPath: string }) {
|
||||
// We also don't want to provide any files to Runtimes that were ignored
|
||||
// through `.vercelignore` or `.nowignore`, because the Build Step does the same.
|
||||
const ignoreFilter = await getIgnoreFilter(workPath);
|
||||
|
||||
// Retrieve the files that are currently available on the File System,
|
||||
// before the Legacy Runtime has even started to build.
|
||||
const sourceFilesPreBuild = await getSourceFiles(workPath, ignoreFilter);
|
||||
|
||||
// Instead of doing another `glob` to get all the matching source files,
|
||||
// we'll filter the list of existing files down to only the ones
|
||||
// that are matching the entrypoint pattern, so we're first creating
|
||||
// a clean new list to begin.
|
||||
const entrypoints = Object.assign({}, sourceFilesPreBuild);
|
||||
|
||||
const entrypointMatch = new RegExp(`^api/.*${ext}$`);
|
||||
|
||||
// Up next, we'll strip out the files from the list of entrypoints
|
||||
// that aren't actually considered entrypoints.
|
||||
for (const file in entrypoints) {
|
||||
if (!entrypointMatch.test(file)) {
|
||||
delete entrypoints[file];
|
||||
}
|
||||
}
|
||||
|
||||
const pages: { [key: string]: any } = {};
|
||||
const { functions = {} } = vercelConfig;
|
||||
const traceDir = join(workPath, '.output', 'runtime-traced-files');
|
||||
const pluginName = packageName.replace('vercel-plugin-', '');
|
||||
|
||||
const traceDir = join(
|
||||
workPath,
|
||||
`.output`,
|
||||
`inputs`,
|
||||
// Legacy Runtimes can only provide API Routes, so that's
|
||||
// why we can use this prefix for all of them. Here, we have to
|
||||
// make sure to not use a cryptic hash name, because people
|
||||
// need to be able to easily inspect the output.
|
||||
`api-routes-${pluginName}`
|
||||
);
|
||||
|
||||
await fs.ensureDir(traceDir);
|
||||
|
||||
for (const entrypoint of Object.keys(entrypoints)) {
|
||||
const key =
|
||||
Object.keys(functions).find(
|
||||
src => src === entrypoint || minimatch(entrypoint, src)
|
||||
) || '';
|
||||
const config = functions[key] || {};
|
||||
|
||||
const { output } = await buildRuntime({
|
||||
files,
|
||||
files: sourceFilesPreBuild,
|
||||
entrypoint,
|
||||
workPath,
|
||||
config: {
|
||||
zeroConfig: true,
|
||||
includeFiles: config.includeFiles,
|
||||
excludeFiles: config.excludeFiles,
|
||||
},
|
||||
meta: {
|
||||
avoidTopLevelInstall: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Legacy Runtimes tend to pollute the `workPath` with compiled results,
|
||||
// because the `workPath` used to be a place that was a place where they could
|
||||
// just put anything, but nowadays it's the working directory of the `vercel build`
|
||||
// command, which is the place where the developer keeps their source files,
|
||||
// so we don't want to pollute this space unnecessarily. That means we have to clean
|
||||
// up files that were created by the build, which is done further below.
|
||||
const sourceFilesAfterBuild = await getSourceFiles(
|
||||
workPath,
|
||||
ignoreFilter
|
||||
);
|
||||
|
||||
// Further down, we will need the filename of the Lambda handler
|
||||
// for placing it inside `server/pages/api`, but because Legacy Runtimes
|
||||
// don't expose the filename directly, we have to construct it
|
||||
// from the handler name, and then find the matching file further below,
|
||||
// because we don't yet know its extension here.
|
||||
const handler = output.handler;
|
||||
const handlerMethod = handler.split('.').reverse()[0];
|
||||
const handlerFileName = handler.replace(`.${handlerMethod}`, '');
|
||||
|
||||
pages[entrypoint] = {
|
||||
handler: output.handler,
|
||||
handler: handler,
|
||||
runtime: output.runtime,
|
||||
memory: output.memory,
|
||||
maxDuration: output.maxDuration,
|
||||
environment: output.environment,
|
||||
allowQuery: output.allowQuery,
|
||||
//regions: output.regions,
|
||||
};
|
||||
|
||||
// @ts-ignore This symbol is a private API
|
||||
const lambdaFiles: Files = output[FILES_SYMBOL];
|
||||
|
||||
// When deploying, the `files` that are passed to the Legacy Runtimes already
|
||||
// have certain files that are ignored stripped, but locally, that list of
|
||||
// files isn't used by the Legacy Runtimes, so we need to apply the filters
|
||||
// to the outputs that they are returning instead.
|
||||
for (const file in lambdaFiles) {
|
||||
if (shouldIgnorePath(file, ignoreFilter, false)) {
|
||||
delete lambdaFiles[file];
|
||||
}
|
||||
}
|
||||
|
||||
const handlerFilePath = Object.keys(lambdaFiles).find(item => {
|
||||
return parse(item).name === handlerFileName;
|
||||
});
|
||||
|
||||
const handlerFileOrigin = lambdaFiles[handlerFilePath || ''].fsPath;
|
||||
|
||||
if (!handlerFileOrigin) {
|
||||
throw new Error(
|
||||
`Could not find a handler file. Please ensure that the list of \`files\` defined for the returned \`Lambda\` contains a file with the name ${handlerFileName} (+ any extension).`
|
||||
);
|
||||
}
|
||||
|
||||
const entry = join(workPath, '.output', 'server', 'pages', entrypoint);
|
||||
|
||||
await fs.ensureDir(dirname(entry));
|
||||
await linkOrCopy(files[entrypoint].fsPath, entry);
|
||||
await linkOrCopy(handlerFileOrigin, entry);
|
||||
|
||||
const toRemove = [];
|
||||
|
||||
// You can find more details about this at the point where the
|
||||
// `sourceFilesAfterBuild` is created originally.
|
||||
for (const file in sourceFilesAfterBuild) {
|
||||
if (!sourceFilesPreBuild[file]) {
|
||||
const path = sourceFilesAfterBuild[file].fsPath;
|
||||
toRemove.push(fs.remove(path));
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(toRemove);
|
||||
|
||||
const tracedFiles: {
|
||||
absolutePath: string;
|
||||
@@ -78,7 +193,14 @@ export function convertRuntimeToPlugin(
|
||||
|
||||
Object.entries(lambdaFiles).forEach(async ([relPath, file]) => {
|
||||
const newPath = join(traceDir, relPath);
|
||||
|
||||
// The handler was already moved into position above.
|
||||
if (relPath === handlerFilePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
tracedFiles.push({ absolutePath: newPath, relativePath: relPath });
|
||||
|
||||
if (file.fsPath) {
|
||||
await linkOrCopy(file.fsPath, newPath);
|
||||
} else if (file.type === 'FileBlob') {
|
||||
@@ -96,11 +218,12 @@ export function convertRuntimeToPlugin(
|
||||
'pages',
|
||||
`${entrypoint}.nft.json`
|
||||
);
|
||||
|
||||
const json = JSON.stringify({
|
||||
version: 1,
|
||||
files: tracedFiles.map(f => ({
|
||||
input: normalizePath(relative(nft, f.absolutePath)),
|
||||
output: normalizePath(f.relativePath),
|
||||
files: tracedFiles.map(file => ({
|
||||
input: normalizePath(relative(dirname(nft), file.absolutePath)),
|
||||
output: normalizePath(file.relativePath),
|
||||
})),
|
||||
});
|
||||
|
||||
@@ -108,7 +231,7 @@ export function convertRuntimeToPlugin(
|
||||
await fs.writeFile(nft, json);
|
||||
}
|
||||
|
||||
await updateFunctionsManifest({ vercelConfig, workPath, pages });
|
||||
await updateFunctionsManifest({ workPath, pages });
|
||||
};
|
||||
}
|
||||
|
||||
@@ -136,15 +259,12 @@ async function readJson(filePath: string): Promise<{ [key: string]: any }> {
|
||||
|
||||
/**
|
||||
* If `.output/functions-manifest.json` exists, append to the pages
|
||||
* property. Otherwise write a new file. This will also read `vercel.json`
|
||||
* and apply relevant `functions` property config.
|
||||
* property. Otherwise write a new file.
|
||||
*/
|
||||
export async function updateFunctionsManifest({
|
||||
vercelConfig,
|
||||
workPath,
|
||||
pages,
|
||||
}: {
|
||||
vercelConfig: { functions?: BuilderFunctions; regions?: string[] };
|
||||
workPath: string;
|
||||
pages: { [key: string]: any };
|
||||
}) {
|
||||
@@ -159,16 +279,7 @@ export async function updateFunctionsManifest({
|
||||
if (!functionsManifest.pages) functionsManifest.pages = {};
|
||||
|
||||
for (const [pageKey, pageConfig] of Object.entries(pages)) {
|
||||
const fnConfig = await getLambdaOptionsFromFunction({
|
||||
sourceFile: pageKey,
|
||||
config: vercelConfig,
|
||||
});
|
||||
functionsManifest.pages[pageKey] = {
|
||||
...pageConfig,
|
||||
memory: fnConfig.memory || pageConfig.memory,
|
||||
maxDuration: fnConfig.maxDuration || pageConfig.maxDuration,
|
||||
regions: vercelConfig.regions || pageConfig.regions,
|
||||
};
|
||||
functionsManifest.pages[pageKey] = { ...pageConfig };
|
||||
}
|
||||
|
||||
await fs.writeFile(functionsManifestPath, JSON.stringify(functionsManifest));
|
||||
|
||||
84
packages/build-utils/src/get-ignore-filter.ts
Normal file
84
packages/build-utils/src/get-ignore-filter.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import ignore from 'ignore';
|
||||
|
||||
interface CodedError extends Error {
|
||||
code: string;
|
||||
}
|
||||
|
||||
function isCodedError(error: unknown): error is CodedError {
|
||||
return (
|
||||
error !== null &&
|
||||
error !== undefined &&
|
||||
(error as CodedError).code !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
function clearRelative(s: string) {
|
||||
return s.replace(/(\n|^)\.\//g, '$1');
|
||||
}
|
||||
|
||||
export default async function (
|
||||
downloadPath: string,
|
||||
rootDirectory?: string | undefined
|
||||
) {
|
||||
const readFile = async (p: string) => {
|
||||
try {
|
||||
return await fs.readFile(p, 'utf8');
|
||||
} catch (error: any) {
|
||||
if (
|
||||
error.code === 'ENOENT' ||
|
||||
(error instanceof Error && error.message.includes('ENOENT'))
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const vercelIgnorePath = path.join(
|
||||
downloadPath,
|
||||
rootDirectory || '',
|
||||
'.vercelignore'
|
||||
);
|
||||
const nowIgnorePath = path.join(
|
||||
downloadPath,
|
||||
rootDirectory || '',
|
||||
'.nowignore'
|
||||
);
|
||||
const ignoreContents = [];
|
||||
|
||||
try {
|
||||
ignoreContents.push(
|
||||
...(
|
||||
await Promise.all([readFile(vercelIgnorePath), readFile(nowIgnorePath)])
|
||||
).filter(Boolean)
|
||||
);
|
||||
} catch (error) {
|
||||
if (isCodedError(error) && error.code === 'ENOTDIR') {
|
||||
console.log(`Warning: Cannot read ignore file from ${vercelIgnorePath}`);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (ignoreContents.length === 2) {
|
||||
throw new Error(
|
||||
'Cannot use both a `.vercelignore` and `.nowignore` file. Please delete the `.nowignore` file.'
|
||||
);
|
||||
}
|
||||
|
||||
if (ignoreContents.length === 0) {
|
||||
return () => false;
|
||||
}
|
||||
|
||||
const ignoreFilter: any = ignore().add(clearRelative(ignoreContents[0]!));
|
||||
|
||||
return function (p: string) {
|
||||
// we should not ignore now.json and vercel.json if it asked to.
|
||||
// we depend on these files for building the app with sourceless
|
||||
if (p === 'now.json' || p === 'vercel.json') return false;
|
||||
return ignoreFilter.test(p).ignored;
|
||||
};
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { createHash } from 'crypto';
|
||||
import FileBlob from './file-blob';
|
||||
import FileFsRef from './file-fs-ref';
|
||||
import FileRef from './file-ref';
|
||||
@@ -33,6 +34,7 @@ import { NowBuildError } from './errors';
|
||||
import streamToBuffer from './fs/stream-to-buffer';
|
||||
import shouldServe from './should-serve';
|
||||
import debug from './debug';
|
||||
import getIgnoreFilter from './get-ignore-filter';
|
||||
|
||||
export {
|
||||
FileBlob,
|
||||
@@ -70,6 +72,7 @@ export {
|
||||
isSymbolicLink,
|
||||
getLambdaOptionsFromFunction,
|
||||
scanParentDirs,
|
||||
getIgnoreFilter,
|
||||
};
|
||||
|
||||
export {
|
||||
@@ -132,3 +135,11 @@ export const getPlatformEnv = (name: string): string | undefined => {
|
||||
}
|
||||
return n;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function for generating file or directories names in `.output/inputs`
|
||||
* for dependencies of files provided to the File System API.
|
||||
*/
|
||||
export const getInputHash = (source: Buffer | string): string => {
|
||||
return createHash('sha1').update(source).digest('hex');
|
||||
};
|
||||
|
||||
@@ -58,6 +58,7 @@ export interface Meta {
|
||||
filesRemoved?: string[];
|
||||
env?: Env;
|
||||
buildEnv?: Env;
|
||||
avoidTopLevelInstall?: boolean;
|
||||
}
|
||||
|
||||
export interface AnalyzeOptions {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { join } from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import { BuildOptions, createLambda } from '../src';
|
||||
import { BuildOptions, createLambda, FileFsRef } from '../src';
|
||||
import { convertRuntimeToPlugin } from '../src/convert-runtime-to-plugin';
|
||||
|
||||
async function fsToJson(dir: string, output: Record<string, any> = {}) {
|
||||
@@ -32,9 +32,13 @@ describe('convert-runtime-to-plugin', () => {
|
||||
});
|
||||
|
||||
it('should create correct fileystem for python', async () => {
|
||||
const ext = '.py';
|
||||
const workPath = pythonApiWorkpath;
|
||||
const handlerName = 'vc__handler__python';
|
||||
const handlerFileName = handlerName + ext;
|
||||
|
||||
const lambdaOptions = {
|
||||
handler: 'index.handler',
|
||||
handler: `${handlerName}.vc_handler`,
|
||||
runtime: 'python3.9',
|
||||
memory: 512,
|
||||
maxDuration: 5,
|
||||
@@ -42,6 +46,15 @@ describe('convert-runtime-to-plugin', () => {
|
||||
};
|
||||
|
||||
const buildRuntime = async (opts: BuildOptions) => {
|
||||
const handlerPath = join(workPath, handlerFileName);
|
||||
|
||||
// This is the usual time at which a Legacy Runtime writes its Lambda launcher.
|
||||
await fs.writeFile(handlerPath, '# handler');
|
||||
|
||||
opts.files[handlerFileName] = new FileFsRef({
|
||||
fsPath: handlerPath,
|
||||
});
|
||||
|
||||
const lambda = await createLambda({
|
||||
files: opts.files,
|
||||
...lambdaOptions,
|
||||
@@ -50,25 +63,30 @@ describe('convert-runtime-to-plugin', () => {
|
||||
};
|
||||
|
||||
const lambdaFiles = await fsToJson(workPath);
|
||||
const vercelConfig = JSON.parse(lambdaFiles['vercel.json']);
|
||||
delete lambdaFiles['vercel.json'];
|
||||
const build = await convertRuntimeToPlugin(buildRuntime, '.py');
|
||||
const packageName = 'vercel-plugin-python';
|
||||
const build = await convertRuntimeToPlugin(buildRuntime, packageName, ext);
|
||||
|
||||
await build({ vercelConfig, workPath });
|
||||
await build({ workPath });
|
||||
|
||||
const output = await fsToJson(join(workPath, '.output'));
|
||||
|
||||
delete lambdaFiles['vercel.json'];
|
||||
delete lambdaFiles['vc__handler__python.py'];
|
||||
|
||||
expect(output).toMatchObject({
|
||||
'functions-manifest.json': expect.stringContaining('{'),
|
||||
'runtime-traced-files': lambdaFiles,
|
||||
inputs: {
|
||||
'api-routes-python': lambdaFiles,
|
||||
},
|
||||
server: {
|
||||
pages: {
|
||||
api: {
|
||||
'index.py': expect.stringContaining('index'),
|
||||
'index.py': expect.stringContaining('handler'),
|
||||
'index.py.nft.json': expect.stringContaining('{'),
|
||||
users: {
|
||||
'get.py': expect.stringContaining('get'),
|
||||
'get.py': expect.stringContaining('handler'),
|
||||
'get.py.nft.json': expect.stringContaining('{'),
|
||||
'post.py': expect.stringContaining('post'),
|
||||
'post.py': expect.stringContaining('handler'),
|
||||
'post.py.nft.json': expect.stringContaining('{'),
|
||||
},
|
||||
},
|
||||
@@ -82,7 +100,7 @@ describe('convert-runtime-to-plugin', () => {
|
||||
pages: {
|
||||
'api/index.py': lambdaOptions,
|
||||
'api/users/get.py': lambdaOptions,
|
||||
'api/users/post.py': { ...lambdaOptions, memory: 3008 },
|
||||
'api/users/post.py': { ...lambdaOptions, memory: 512 },
|
||||
},
|
||||
});
|
||||
|
||||
@@ -91,36 +109,35 @@ describe('convert-runtime-to-plugin', () => {
|
||||
version: 1,
|
||||
files: [
|
||||
{
|
||||
input: '../../../../runtime-traced-files/api/db/[id].py',
|
||||
input: `../../../inputs/api-routes-python/api/db/[id].py`,
|
||||
output: 'api/db/[id].py',
|
||||
},
|
||||
{
|
||||
input: '../../../../runtime-traced-files/api/index.py',
|
||||
input: `../../../inputs/api-routes-python/api/index.py`,
|
||||
output: 'api/index.py',
|
||||
},
|
||||
{
|
||||
input:
|
||||
'../../../../runtime-traced-files/api/project/[aid]/[bid]/index.py',
|
||||
input: `../../../inputs/api-routes-python/api/project/[aid]/[bid]/index.py`,
|
||||
output: 'api/project/[aid]/[bid]/index.py',
|
||||
},
|
||||
{
|
||||
input: '../../../../runtime-traced-files/api/users/get.py',
|
||||
input: `../../../inputs/api-routes-python/api/users/get.py`,
|
||||
output: 'api/users/get.py',
|
||||
},
|
||||
{
|
||||
input: '../../../../runtime-traced-files/api/users/post.py',
|
||||
input: `../../../inputs/api-routes-python/api/users/post.py`,
|
||||
output: 'api/users/post.py',
|
||||
},
|
||||
{
|
||||
input: '../../../../runtime-traced-files/file.txt',
|
||||
input: `../../../inputs/api-routes-python/file.txt`,
|
||||
output: 'file.txt',
|
||||
},
|
||||
{
|
||||
input: '../../../../runtime-traced-files/util/date.py',
|
||||
input: `../../../inputs/api-routes-python/util/date.py`,
|
||||
output: 'util/date.py',
|
||||
},
|
||||
{
|
||||
input: '../../../../runtime-traced-files/util/math.py',
|
||||
input: `../../../inputs/api-routes-python/util/math.py`,
|
||||
output: 'util/math.py',
|
||||
},
|
||||
],
|
||||
@@ -133,36 +150,35 @@ describe('convert-runtime-to-plugin', () => {
|
||||
version: 1,
|
||||
files: [
|
||||
{
|
||||
input: '../../../../../runtime-traced-files/api/db/[id].py',
|
||||
input: `../../../../inputs/api-routes-python/api/db/[id].py`,
|
||||
output: 'api/db/[id].py',
|
||||
},
|
||||
{
|
||||
input: '../../../../../runtime-traced-files/api/index.py',
|
||||
input: `../../../../inputs/api-routes-python/api/index.py`,
|
||||
output: 'api/index.py',
|
||||
},
|
||||
{
|
||||
input:
|
||||
'../../../../../runtime-traced-files/api/project/[aid]/[bid]/index.py',
|
||||
input: `../../../../inputs/api-routes-python/api/project/[aid]/[bid]/index.py`,
|
||||
output: 'api/project/[aid]/[bid]/index.py',
|
||||
},
|
||||
{
|
||||
input: '../../../../../runtime-traced-files/api/users/get.py',
|
||||
input: `../../../../inputs/api-routes-python/api/users/get.py`,
|
||||
output: 'api/users/get.py',
|
||||
},
|
||||
{
|
||||
input: '../../../../../runtime-traced-files/api/users/post.py',
|
||||
input: `../../../../inputs/api-routes-python/api/users/post.py`,
|
||||
output: 'api/users/post.py',
|
||||
},
|
||||
{
|
||||
input: '../../../../../runtime-traced-files/file.txt',
|
||||
input: `../../../../inputs/api-routes-python/file.txt`,
|
||||
output: 'file.txt',
|
||||
},
|
||||
{
|
||||
input: '../../../../../runtime-traced-files/util/date.py',
|
||||
input: `../../../../inputs/api-routes-python/util/date.py`,
|
||||
output: 'util/date.py',
|
||||
},
|
||||
{
|
||||
input: '../../../../../runtime-traced-files/util/math.py',
|
||||
input: `../../../../inputs/api-routes-python/util/math.py`,
|
||||
output: 'util/math.py',
|
||||
},
|
||||
],
|
||||
@@ -175,36 +191,35 @@ describe('convert-runtime-to-plugin', () => {
|
||||
version: 1,
|
||||
files: [
|
||||
{
|
||||
input: '../../../../../runtime-traced-files/api/db/[id].py',
|
||||
input: `../../../../inputs/api-routes-python/api/db/[id].py`,
|
||||
output: 'api/db/[id].py',
|
||||
},
|
||||
{
|
||||
input: '../../../../../runtime-traced-files/api/index.py',
|
||||
input: `../../../../inputs/api-routes-python/api/index.py`,
|
||||
output: 'api/index.py',
|
||||
},
|
||||
{
|
||||
input:
|
||||
'../../../../../runtime-traced-files/api/project/[aid]/[bid]/index.py',
|
||||
input: `../../../../inputs/api-routes-python/api/project/[aid]/[bid]/index.py`,
|
||||
output: 'api/project/[aid]/[bid]/index.py',
|
||||
},
|
||||
{
|
||||
input: '../../../../../runtime-traced-files/api/users/get.py',
|
||||
input: `../../../../inputs/api-routes-python/api/users/get.py`,
|
||||
output: 'api/users/get.py',
|
||||
},
|
||||
{
|
||||
input: '../../../../../runtime-traced-files/api/users/post.py',
|
||||
input: `../../../../inputs/api-routes-python/api/users/post.py`,
|
||||
output: 'api/users/post.py',
|
||||
},
|
||||
{
|
||||
input: '../../../../../runtime-traced-files/file.txt',
|
||||
input: `../../../../inputs/api-routes-python/file.txt`,
|
||||
output: 'file.txt',
|
||||
},
|
||||
{
|
||||
input: '../../../../../runtime-traced-files/util/date.py',
|
||||
input: `../../../../inputs/api-routes-python/util/date.py`,
|
||||
output: 'util/date.py',
|
||||
},
|
||||
{
|
||||
input: '../../../../../runtime-traced-files/util/math.py',
|
||||
input: `../../../../inputs/api-routes-python/util/math.py`,
|
||||
output: 'util/math.py',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "23.1.3-canary.42",
|
||||
"version": "23.1.3-canary.53",
|
||||
"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.24",
|
||||
"@vercel/build-utils": "2.12.3-canary.32",
|
||||
"@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",
|
||||
"@vercel/python": "2.1.2-canary.1",
|
||||
"@vercel/ruby": "1.2.8-canary.6",
|
||||
"update-notifier": "4.1.0",
|
||||
"vercel-plugin-middleware": "0.0.0-canary.7",
|
||||
"vercel-plugin-node": "1.12.2-canary.15"
|
||||
"vercel-plugin-node": "1.12.2-canary.24"
|
||||
},
|
||||
"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.14",
|
||||
"@vercel/frameworks": "0.5.1-canary.16",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/nft": "0.17.0",
|
||||
"@zeit/fun": "0.11.2",
|
||||
|
||||
@@ -14,7 +14,7 @@ import { assert } from 'console';
|
||||
import { createHash } from 'crypto';
|
||||
import fs from 'fs-extra';
|
||||
import ogGlob from 'glob';
|
||||
import { isAbsolute, join, parse, relative, resolve } from 'path';
|
||||
import { dirname, isAbsolute, join, parse, relative, resolve } from 'path';
|
||||
import pluralize from 'pluralize';
|
||||
import Client from '../util/client';
|
||||
import { VercelConfig } from '../util/dev/types';
|
||||
@@ -136,9 +136,11 @@ export default async function main(client: Client) {
|
||||
});
|
||||
|
||||
// Set process.env with loaded environment variables
|
||||
await processEnv(loadedEnvFiles);
|
||||
processEnv(loadedEnvFiles);
|
||||
|
||||
const spawnOpts = {
|
||||
const spawnOpts: {
|
||||
env: Record<string, string | undefined>;
|
||||
} = {
|
||||
env: { ...combinedEnv, VERCEL: '1' },
|
||||
};
|
||||
|
||||
@@ -284,6 +286,21 @@ export default async function main(client: Client) {
|
||||
// Clean the output directory
|
||||
fs.removeSync(join(cwd, OUTPUT_DIR));
|
||||
|
||||
if (framework && process.env.VERCEL_URL && 'envPrefix' in framework) {
|
||||
for (const key of Object.keys(process.env)) {
|
||||
if (key.startsWith('VERCEL_')) {
|
||||
const newKey = `${framework.envPrefix}${key}`;
|
||||
// Set `process.env` and `spawnOpts.env` to make sure the variables are
|
||||
// available to the `build` step and the CLI Plugins.
|
||||
process.env[newKey] = process.env[newKey] || process.env[key];
|
||||
spawnOpts.env[newKey] = process.env[newKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Required for Next.js to produce the correct `.nft.json` files.
|
||||
spawnOpts.env.NEXT_PRIVATE_OUTPUT_TRACE_ROOT = baseDir;
|
||||
|
||||
// Yarn v2 PnP mode may be activated, so force
|
||||
// "node-modules" linker style
|
||||
const env = {
|
||||
@@ -315,22 +332,42 @@ export default async function main(client: Client) {
|
||||
cwd,
|
||||
});
|
||||
}
|
||||
// don't trust framework detection here because they might be switching to next on a branch
|
||||
const isNextJs = fs.existsSync(join(cwd, '.next'));
|
||||
|
||||
if (!fs.existsSync(join(cwd, OUTPUT_DIR))) {
|
||||
let outputDir = join(OUTPUT_DIR, 'static');
|
||||
let distDir = await framework.getFsOutputDir(cwd);
|
||||
if (isNextJs) {
|
||||
outputDir = OUTPUT_DIR;
|
||||
let dotNextDir: string | null = null;
|
||||
|
||||
// If a custom `outputDirectory` was set, we'll need to verify
|
||||
// if it's `.next` output, or just static output.
|
||||
const userOutputDirectory = project.settings.outputDirectory;
|
||||
|
||||
if (typeof userOutputDirectory === 'string') {
|
||||
if (fs.existsSync(join(cwd, userOutputDirectory, 'BUILD_ID'))) {
|
||||
dotNextDir = join(cwd, userOutputDirectory);
|
||||
client.output.debug(
|
||||
`Consider ${param(userOutputDirectory)} as ${param('.next')} output.`
|
||||
);
|
||||
}
|
||||
} else if (fs.existsSync(join(cwd, '.next'))) {
|
||||
dotNextDir = join(cwd, '.next');
|
||||
client.output.debug(`Found ${param('.next')} directory.`);
|
||||
}
|
||||
const copyStamp = stamp();
|
||||
|
||||
// We cannot rely on the `framework` alone, as it might be a static export,
|
||||
// and the current build might use a differnt project that's not in the settings.
|
||||
const isNextOutput = Boolean(dotNextDir);
|
||||
const outputDir = isNextOutput ? OUTPUT_DIR : join(OUTPUT_DIR, 'static');
|
||||
const distDir =
|
||||
dotNextDir ||
|
||||
userOutputDirectory ||
|
||||
(await framework.getFsOutputDir(cwd));
|
||||
|
||||
await fs.ensureDir(join(cwd, outputDir));
|
||||
const relativeDistDir = relative(cwd, distDir);
|
||||
|
||||
const copyStamp = stamp();
|
||||
client.output.spinner(
|
||||
`Copying files from ${param(distDir)} to ${param(outputDir)}`
|
||||
);
|
||||
const files = await glob(join(relativeDistDir, '**'), {
|
||||
const files = await glob(join(relative(cwd, distDir), '**'), {
|
||||
ignore: [
|
||||
'node_modules/**',
|
||||
'.vercel/**',
|
||||
@@ -378,6 +415,7 @@ export default async function main(client: Client) {
|
||||
`Generating build manifest: ${param(buildManifestPath)}`
|
||||
);
|
||||
const buildManifest = {
|
||||
version: 1,
|
||||
cache: framework.cachePattern ? [framework.cachePattern] : [],
|
||||
};
|
||||
await fs.writeJSON(buildManifestPath, buildManifest, { spaces: 2 });
|
||||
@@ -405,7 +443,7 @@ export default async function main(client: Client) {
|
||||
}
|
||||
|
||||
// Special Next.js processing.
|
||||
if (isNextJs) {
|
||||
if (isNextOutput) {
|
||||
// The contents of `.output/static` should be placed inside of `.output/static/_next/static`
|
||||
const tempStatic = '___static';
|
||||
await fs.rename(
|
||||
@@ -456,10 +494,12 @@ export default async function main(client: Client) {
|
||||
// `public`, then`static`). We can't read both at the same time because that would mean we'd
|
||||
// read public for old Next.js versions that don't support it, which might be breaking (and
|
||||
// we don't want to make vercel build specific framework versions).
|
||||
const nextSrcDirectory = dirname(distDir);
|
||||
|
||||
const publicFiles = await glob('public/**', {
|
||||
nodir: true,
|
||||
dot: true,
|
||||
cwd,
|
||||
cwd: nextSrcDirectory,
|
||||
absolute: true,
|
||||
});
|
||||
if (publicFiles.length > 0) {
|
||||
@@ -468,7 +508,11 @@ export default async function main(client: Client) {
|
||||
smartCopy(
|
||||
client,
|
||||
f,
|
||||
f.replace('public', join(OUTPUT_DIR, 'static'))
|
||||
join(
|
||||
OUTPUT_DIR,
|
||||
'static',
|
||||
relative(join(dirname(distDir), 'public'), f)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -476,7 +520,7 @@ export default async function main(client: Client) {
|
||||
const staticFiles = await glob('static/**', {
|
||||
nodir: true,
|
||||
dot: true,
|
||||
cwd,
|
||||
cwd: nextSrcDirectory,
|
||||
absolute: true,
|
||||
});
|
||||
await Promise.all(
|
||||
@@ -484,7 +528,12 @@ export default async function main(client: Client) {
|
||||
smartCopy(
|
||||
client,
|
||||
f,
|
||||
f.replace('static', join(OUTPUT_DIR, 'static', 'static'))
|
||||
join(
|
||||
OUTPUT_DIR,
|
||||
'static',
|
||||
'static',
|
||||
relative(join(dirname(distDir), 'static'), f)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -503,6 +552,7 @@ export default async function main(client: Client) {
|
||||
const nftFiles = await glob(join(OUTPUT_DIR, '**', '*.nft.json'), {
|
||||
nodir: true,
|
||||
dot: true,
|
||||
ignore: ['cache/**'],
|
||||
cwd,
|
||||
absolute: true,
|
||||
});
|
||||
@@ -539,6 +589,7 @@ export default async function main(client: Client) {
|
||||
baseDir,
|
||||
outputDir: OUTPUT_DIR,
|
||||
nftFileName: f.replace(ext, '.js.nft.json'),
|
||||
distDir,
|
||||
nft: {
|
||||
version: 1,
|
||||
files: Array.from(fileList).map(fileListEntry =>
|
||||
@@ -556,6 +607,7 @@ export default async function main(client: Client) {
|
||||
outputDir: OUTPUT_DIR,
|
||||
nftFileName: f,
|
||||
nft: json,
|
||||
distDir,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -564,22 +616,33 @@ export default async function main(client: Client) {
|
||||
OUTPUT_DIR,
|
||||
'required-server-files.json'
|
||||
);
|
||||
const requiredServerFilesJson = await fs.readJSON(
|
||||
requiredServerFilesPath
|
||||
);
|
||||
await fs.writeJSON(requiredServerFilesPath, {
|
||||
...requiredServerFilesJson,
|
||||
appDir: '.',
|
||||
files: requiredServerFilesJson.files.map((i: string) => {
|
||||
const absolutePath = join(cwd, i.replace('.next', '.output'));
|
||||
const output = relative(baseDir, absolutePath);
|
||||
|
||||
return {
|
||||
input: i.replace('.next', '.output'),
|
||||
output,
|
||||
};
|
||||
}),
|
||||
});
|
||||
if (fs.existsSync(requiredServerFilesPath)) {
|
||||
client.output.debug(`Resolve ${param('required-server-files.json')}.`);
|
||||
|
||||
const requiredServerFilesJson = await fs.readJSON(
|
||||
requiredServerFilesPath
|
||||
);
|
||||
|
||||
await fs.writeJSON(requiredServerFilesPath, {
|
||||
...requiredServerFilesJson,
|
||||
appDir: '.',
|
||||
files: requiredServerFilesJson.files.map((i: string) => {
|
||||
const originalPath = join(requiredServerFilesJson.appDir, i);
|
||||
const relPath = join(OUTPUT_DIR, relative(distDir, originalPath));
|
||||
|
||||
const absolutePath = join(cwd, relPath);
|
||||
const output = relative(baseDir, absolutePath);
|
||||
|
||||
return relPath === output
|
||||
? relPath
|
||||
: {
|
||||
input: relPath,
|
||||
output,
|
||||
};
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -758,24 +821,37 @@ async function resolveNftToOutput({
|
||||
baseDir,
|
||||
outputDir,
|
||||
nftFileName,
|
||||
distDir,
|
||||
nft,
|
||||
}: {
|
||||
client: Client;
|
||||
baseDir: string;
|
||||
outputDir: string;
|
||||
nftFileName: string;
|
||||
distDir: string;
|
||||
nft: NftFile;
|
||||
}) {
|
||||
client.output.debug(`Processing and resolving ${nftFileName}`);
|
||||
await fs.ensureDir(join(outputDir, 'inputs'));
|
||||
const newFilesList: NftFile['files'] = [];
|
||||
|
||||
// If `distDir` is a subdirectory, then the input has to be resolved to where the `.output` directory will be.
|
||||
const relNftFileName = relative(outputDir, nftFileName);
|
||||
const origNftFilename = join(distDir, relNftFileName);
|
||||
|
||||
if (relNftFileName.startsWith('cache/')) {
|
||||
// No need to process the `cache/` directory.
|
||||
// Paths in it might also not be relative to `cache` itself.
|
||||
return;
|
||||
}
|
||||
|
||||
for (let fileEntity of nft.files) {
|
||||
const relativeInput: string =
|
||||
const relativeInput =
|
||||
typeof fileEntity === 'string' ? fileEntity : fileEntity.input;
|
||||
const fullInput = resolve(join(parse(nftFileName).dir, relativeInput));
|
||||
const fullInput = resolve(join(parse(origNftFilename).dir, relativeInput));
|
||||
|
||||
// if the resolved path is NOT in the .output directory we move in it there
|
||||
if (!fullInput.includes(outputDir)) {
|
||||
if (!fullInput.includes(distDir)) {
|
||||
const { ext } = parse(fullInput);
|
||||
const raw = await fs.readFile(fullInput);
|
||||
const newFilePath = join(outputDir, 'inputs', hash(raw) + ext);
|
||||
|
||||
@@ -968,7 +968,7 @@ export default class DevServer {
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
const target = `http://localhost:${this.devProcessPort}`;
|
||||
const target = `http://127.0.0.1:${this.devProcessPort}`;
|
||||
this.output.debug(`Detected "upgrade" event, proxying to ${target}`);
|
||||
this.proxy.ws(req, socket, head, { target });
|
||||
});
|
||||
@@ -1663,7 +1663,7 @@ export default class DevServer {
|
||||
if (!match) {
|
||||
// If the dev command is started, then proxy to it
|
||||
if (this.devProcessPort) {
|
||||
const upstream = `http://localhost:${this.devProcessPort}`;
|
||||
const upstream = `http://127.0.0.1:${this.devProcessPort}`;
|
||||
debug(`Proxying to frontend dev server: ${upstream}`);
|
||||
|
||||
// Add the Vercel platform proxy request headers
|
||||
@@ -1810,7 +1810,7 @@ export default class DevServer {
|
||||
return proxyPass(
|
||||
req,
|
||||
res,
|
||||
`http://localhost:${port}`,
|
||||
`http://127.0.0.1:${port}`,
|
||||
this,
|
||||
requestId,
|
||||
false
|
||||
@@ -1847,7 +1847,7 @@ export default class DevServer {
|
||||
return proxyPass(
|
||||
req,
|
||||
res,
|
||||
`http://localhost:${this.devProcessPort}`,
|
||||
`http://127.0.0.1:${this.devProcessPort}`,
|
||||
this,
|
||||
requestId,
|
||||
false
|
||||
|
||||
@@ -8,6 +8,7 @@ export type ProjectLinkAndSettings = ProjectLink & {
|
||||
buildCommand: Project['buildCommand'];
|
||||
devCommand: Project['devCommand'];
|
||||
outputDirectory: Project['outputDirectory'];
|
||||
directoryListing: Project['directoryListing'];
|
||||
rootDirectory: Project['rootDirectory'];
|
||||
framework: Project['framework'];
|
||||
};
|
||||
@@ -29,6 +30,7 @@ export async function writeProjectSettings(
|
||||
settings: {
|
||||
buildCommand: project.buildCommand,
|
||||
devCommand: project.devCommand,
|
||||
outputDirectory: project.outputDirectory,
|
||||
directoryListing: project.directoryListing,
|
||||
rootDirectory: project.rootDirectory,
|
||||
framework: project.framework,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "10.2.3-canary.25",
|
||||
"version": "10.2.3-canary.33",
|
||||
"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.24",
|
||||
"@vercel/build-utils": "2.12.3-canary.32",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "3.0.0",
|
||||
|
||||
6
packages/frameworks/logos/remix-no-shadow.svg
Normal file
6
packages/frameworks/logos/remix-no-shadow.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M700 0H100C44.772 0 0 44.772 0 100v600c0 55.228 44.772 100 100 100h600c55.228 0 100-44.772 100-100V100C800 44.772 755.228 0 700 0Z" fill="#212121"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M587.947 527.768c4.254 54.65 4.254 80.268 4.254 108.232H465.756c0-6.091.109-11.663.219-17.313.342-17.564.699-35.88-2.147-72.868-3.761-54.152-27.08-66.185-69.957-66.185H195v-98.525h204.889c54.16 0 81.241-16.476 81.241-60.098 0-38.357-27.081-61.601-81.241-61.601H195V163h227.456C545.069 163 606 220.912 606 313.42c0 69.193-42.877 114.319-100.799 121.84 48.895 9.777 77.48 37.605 82.746 92.508Z" fill="#fff"/>
|
||||
<path d="M195 636v-73.447h133.697c22.332 0 27.181 16.563 27.181 26.441V636H195Z" fill="#fff"/>
|
||||
<path d="M194.5 636v.5h161.878v-47.506c0-5.006-1.226-11.734-5.315-17.224-4.108-5.515-11.059-9.717-22.366-9.717H194.5V636Z" stroke="#fff" stroke-opacity=".8"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 958 B |
@@ -1,25 +1,6 @@
|
||||
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="800" height="800" fill="#212121"/>
|
||||
<g filter="url(#filter0_dd_126_53)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M587.947 527.768C592.201 582.418 592.201 608.036 592.201 636H465.756C465.756 629.909 465.865 624.337 465.975 618.687C466.317 601.123 466.674 582.807 463.828 545.819C460.067 491.667 436.748 479.634 393.871 479.634H355.883H195V381.109H399.889C454.049 381.109 481.13 364.633 481.13 321.011C481.13 282.654 454.049 259.41 399.889 259.41H195V163H422.456C545.069 163 606 220.912 606 313.42C606 382.613 563.123 427.739 505.201 435.26C554.096 445.037 582.681 472.865 587.947 527.768Z" fill="#E8F2FF"/>
|
||||
<path d="M195 636V562.553H328.697C351.029 562.553 355.878 579.116 355.878 588.994V636H195Z" fill="#E8F2FF"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_dd_126_53" x="131" y="99" width="539" height="601" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="28"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.223529 0 0 0 0 0.572549 0 0 0 0 1 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_126_53"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="32"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.223529 0 0 0 0 0.572549 0 0 0 0 1 0 0 0 0.9 0"/>
|
||||
<feBlend mode="normal" in2="effect1_dropShadow_126_53" result="effect2_dropShadow_126_53"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_126_53" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<svg viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M700 0H100C44.772 0 0 44.772 0 100v600c0 55.228 44.772 100 100 100h600c55.228 0 100-44.772 100-100V100C800 44.772 755.228 0 700 0Z" fill="#212121"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M587.947 527.768c4.254 54.65 4.254 80.268 4.254 108.232H465.756c0-6.091.109-11.663.219-17.313.342-17.564.699-35.88-2.147-72.868-3.761-54.152-27.08-66.185-69.957-66.185H195v-98.525h204.889c54.16 0 81.241-16.476 81.241-60.098 0-38.357-27.081-61.601-81.241-61.601H195V163h227.456C545.069 163 606 220.912 606 313.42c0 69.193-42.877 114.319-100.799 121.84 48.895 9.777 77.48 37.605 82.746 92.508Z" fill="#fff"/>
|
||||
<path d="M195 636v-73.447h133.697c22.332 0 27.181 16.563 27.181 26.441V636H195Z" fill="#fff"/>
|
||||
<path d="M194.5 636v.5h161.878v-47.506c0-5.006-1.226-11.734-5.315-17.224-4.108-5.515-11.059-9.717-22.366-9.717H194.5V636Z" stroke="#fff" stroke-opacity=".8"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 958 B |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/frameworks",
|
||||
"version": "0.5.1-canary.14",
|
||||
"version": "0.5.1-canary.16",
|
||||
"main": "./dist/frameworks.js",
|
||||
"types": "./dist/frameworks.d.ts",
|
||||
"files": [
|
||||
|
||||
@@ -195,7 +195,7 @@ export const frameworks = [
|
||||
name: 'Remix',
|
||||
slug: 'remix',
|
||||
demo: 'https://remix.examples.vercel.com',
|
||||
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/remix.svg',
|
||||
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/remix-no-shadow.svg',
|
||||
tagline: 'Build Better Websites',
|
||||
description: 'A new Remix app — the result of running `npx create-remix`.',
|
||||
website: 'https://remix.run',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "vercel-plugin-go",
|
||||
"version": "1.0.0-canary.9",
|
||||
"version": "1.0.0-canary.20",
|
||||
"main": "dist/index.js",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
@@ -17,7 +17,7 @@
|
||||
"prepublishOnly": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.12.3-canary.24",
|
||||
"@vercel/build-utils": "2.12.3-canary.32",
|
||||
"@vercel/go": "1.2.4-canary.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { convertRuntimeToPlugin } from '@vercel/build-utils';
|
||||
import * as go from '@vercel/go';
|
||||
|
||||
export const build = convertRuntimeToPlugin(go.build, '.go');
|
||||
export const build = convertRuntimeToPlugin(go.build, 'vercel-plugin-go', '.go');
|
||||
|
||||
export const startDevServer = go.startDevServer;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel-plugin-node",
|
||||
"version": "1.12.2-canary.15",
|
||||
"version": "1.12.2-canary.24",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
||||
@@ -34,12 +34,12 @@
|
||||
"@types/node-fetch": "2",
|
||||
"@types/test-listen": "1.1.0",
|
||||
"@types/yazl": "2.4.2",
|
||||
"@vercel/build-utils": "2.12.3-canary.24",
|
||||
"@vercel/build-utils": "2.12.3-canary.32",
|
||||
"@vercel/fun": "1.0.3",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/nft": "0.14.0",
|
||||
"@vercel/node-bridge": "2.1.1-canary.2",
|
||||
"@vercel/static-config": "0.0.1-canary.0",
|
||||
"@vercel/static-config": "0.0.1-canary.1",
|
||||
"abort-controller": "3.0.0",
|
||||
"content-type": "1.0.4",
|
||||
"cookie": "0.4.0",
|
||||
|
||||
@@ -40,6 +40,7 @@ import {
|
||||
walkParentDirs,
|
||||
normalizePath,
|
||||
runPackageJsonScript,
|
||||
getInputHash,
|
||||
} from '@vercel/build-utils';
|
||||
import { FromSchema } from 'json-schema-to-ts';
|
||||
import { getConfig, BaseFunctionConfigSchema } from '@vercel/static-config';
|
||||
@@ -47,8 +48,6 @@ import { AbortController } from 'abort-controller';
|
||||
import { Register, register } from './typescript';
|
||||
import { pageToRoute } from './router/page-to-route';
|
||||
import { isDynamicRoute } from './router/is-dynamic';
|
||||
import crypto from 'crypto';
|
||||
import type { VercelConfig } from '@vercel/client';
|
||||
|
||||
export { shouldServe };
|
||||
export {
|
||||
@@ -380,13 +379,7 @@ function getAWSLambdaHandler(entrypoint: string, config: FunctionConfig) {
|
||||
}
|
||||
|
||||
// TODO NATE: turn this into a `@vercel/plugin-utils` helper function?
|
||||
export async function build({
|
||||
vercelConfig,
|
||||
workPath,
|
||||
}: {
|
||||
vercelConfig: VercelConfig;
|
||||
workPath: string;
|
||||
}) {
|
||||
export async function build({ workPath }: { workPath: string }) {
|
||||
const project = new Project();
|
||||
const entrypoints = await glob('api/**/*.[jt]s', workPath);
|
||||
const installedPaths = new Set<string>();
|
||||
@@ -408,14 +401,13 @@ export async function build({
|
||||
getConfig(project, absEntrypoint, FunctionConfigSchema) || {};
|
||||
|
||||
// No config exported means "node", but if there is a config
|
||||
// and "runtime" is defined, but it is not "node" then don't
|
||||
// and "use" is defined, but it is not "node" then don't
|
||||
// compile this file.
|
||||
if (config.runtime && config.runtime !== 'node') {
|
||||
if (config.use && config.use !== 'node') {
|
||||
continue;
|
||||
}
|
||||
|
||||
await buildEntrypoint({
|
||||
vercelConfig,
|
||||
workPath,
|
||||
entrypoint,
|
||||
config,
|
||||
@@ -425,23 +417,18 @@ export async function build({
|
||||
}
|
||||
|
||||
export async function buildEntrypoint({
|
||||
vercelConfig,
|
||||
workPath,
|
||||
entrypoint,
|
||||
config,
|
||||
installedPaths,
|
||||
}: {
|
||||
vercelConfig: VercelConfig;
|
||||
workPath: string;
|
||||
entrypoint: string;
|
||||
config: FunctionConfig;
|
||||
installedPaths?: Set<string>;
|
||||
}) {
|
||||
// Unique hash that will be used as directory name for `.output`.
|
||||
const entrypointHash = crypto
|
||||
.createHash('sha256')
|
||||
.update(entrypoint)
|
||||
.digest('hex');
|
||||
const entrypointHash = 'api-routes-node-' + getInputHash(entrypoint);
|
||||
const outputDirPath = join(workPath, '.output');
|
||||
|
||||
const { dir, name } = parsePath(entrypoint);
|
||||
@@ -561,7 +548,7 @@ export async function buildEntrypoint({
|
||||
runtime: nodeVersion.runtime,
|
||||
},
|
||||
};
|
||||
await updateFunctionsManifest({ vercelConfig, workPath, pages });
|
||||
await updateFunctionsManifest({ workPath, pages });
|
||||
|
||||
// Update the `routes-mainifest.json` file with the wildcard route
|
||||
// when the entrypoint is dynamic (i.e. `/api/[id].ts`).
|
||||
|
||||
11
packages/plugin-node/test/build.test.ts
vendored
11
packages/plugin-node/test/build.test.ts
vendored
@@ -143,16 +143,7 @@ function withFixture<T>(
|
||||
await runNpmInstall(fixture);
|
||||
}
|
||||
|
||||
let vercelConfig = {};
|
||||
try {
|
||||
vercelConfig = JSON.parse(
|
||||
await fsp.readFile(path.join(fixture, 'vercel.json'), 'utf8')
|
||||
);
|
||||
} catch (e) {
|
||||
// Consume error
|
||||
}
|
||||
|
||||
await build({ vercelConfig, workPath: fixture });
|
||||
await build({ workPath: fixture });
|
||||
|
||||
try {
|
||||
return await t({ fixture, fetch });
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "vercel-plugin-python",
|
||||
"version": "1.0.0-canary.10",
|
||||
"version": "1.0.0-canary.21",
|
||||
"main": "dist/index.js",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
@@ -17,8 +17,8 @@
|
||||
"prepublishOnly": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.12.3-canary.24",
|
||||
"@vercel/python": "2.1.2-canary.0"
|
||||
"@vercel/build-utils": "2.12.3-canary.32",
|
||||
"@vercel/python": "2.1.2-canary.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "*",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { convertRuntimeToPlugin } from '@vercel/build-utils';
|
||||
import * as python from '@vercel/python';
|
||||
|
||||
export const build = convertRuntimeToPlugin(python.build, '.py');
|
||||
export const build = convertRuntimeToPlugin(python.build, 'vercel-plugin-python', '.py');
|
||||
|
||||
//export const startDevServer = python.startDevServer;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": false,
|
||||
"name": "vercel-plugin-ruby",
|
||||
"version": "1.0.0-canary.8",
|
||||
"version": "1.0.0-canary.19",
|
||||
"main": "dist/index.js",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
@@ -17,8 +17,8 @@
|
||||
"prepublishOnly": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "2.12.3-canary.24",
|
||||
"@vercel/ruby": "1.2.8-canary.4"
|
||||
"@vercel/build-utils": "2.12.3-canary.32",
|
||||
"@vercel/ruby": "1.2.8-canary.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "*",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { convertRuntimeToPlugin } from '@vercel/build-utils';
|
||||
import * as ruby from '@vercel/ruby';
|
||||
|
||||
export const build = convertRuntimeToPlugin(ruby.build, '.rb');
|
||||
export const build = convertRuntimeToPlugin(ruby.build, 'vercel-plugin-ruby', '.rb');
|
||||
|
||||
//export const startDevServer = ruby.startDevServer;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/python",
|
||||
"version": "2.1.2-canary.0",
|
||||
"version": "2.1.2-canary.1",
|
||||
"main": "./dist/index.js",
|
||||
"license": "MIT",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { relative, basename } from 'path';
|
||||
import execa from 'execa';
|
||||
import { Meta, debug } from '@vercel/build-utils';
|
||||
|
||||
@@ -135,6 +136,18 @@ export async function installRequirementsFile({
|
||||
meta,
|
||||
args = [],
|
||||
}: InstallRequirementsFileArg) {
|
||||
const fileAtRoot = relative(workPath, filePath) === basename(filePath);
|
||||
|
||||
// If the `requirements.txt` file is located in the Root Directory of the project and
|
||||
// the new File System API is used (`avoidTopLevelInstall`), the Install Command
|
||||
// will have already installed its dependencies, so we don't need to do it again.
|
||||
if (meta.avoidTopLevelInstall && fileAtRoot) {
|
||||
debug(
|
||||
`Skipping requirements file installation, already installed by Install Command`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
meta.isDev &&
|
||||
(await areRequirementsInstalled(pythonPath, filePath, workPath))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { join, dirname } from 'path';
|
||||
import { join, dirname, relative } from 'path';
|
||||
import execa from 'execa';
|
||||
import {
|
||||
ensureDir,
|
||||
@@ -85,10 +85,12 @@ export async function build({
|
||||
}: BuildOptions) {
|
||||
await download(files, workPath, meta);
|
||||
const entrypointFsDirname = join(workPath, dirname(entrypoint));
|
||||
const gemfileName = 'Gemfile';
|
||||
|
||||
const gemfilePath = await walkParentDirs({
|
||||
base: workPath,
|
||||
start: entrypointFsDirname,
|
||||
filename: 'Gemfile',
|
||||
filename: gemfileName,
|
||||
});
|
||||
const gemfileContents = gemfilePath
|
||||
? await readFile(gemfilePath, 'utf8')
|
||||
@@ -130,15 +132,24 @@ export async function build({
|
||||
'did not find a vendor directory but found a Gemfile, bundling gems...'
|
||||
);
|
||||
|
||||
// try installing. this won't work if native extesions are required.
|
||||
// if that's the case, gems should be vendored locally before deploying.
|
||||
try {
|
||||
await bundleInstall(bundlerPath, bundleDir, gemfilePath);
|
||||
} catch (err) {
|
||||
debug(
|
||||
'unable to build gems from Gemfile. vendor the gems locally with "bundle install --deployment" and retry.'
|
||||
);
|
||||
throw err;
|
||||
const fileAtRoot = relative(workPath, gemfilePath) === gemfileName;
|
||||
|
||||
// If the `Gemfile` is located in the Root Directory of the project and
|
||||
// the new File System API is used (`avoidTopLevelInstall`), the Install Command
|
||||
// will have already installed its dependencies, so we don't need to do it again.
|
||||
if (meta.avoidTopLevelInstall && fileAtRoot) {
|
||||
debug('Skipping `bundle install` — already handled by Install Command');
|
||||
} else {
|
||||
// try installing. this won't work if native extesions are required.
|
||||
// if that's the case, gems should be vendored locally before deploying.
|
||||
try {
|
||||
await bundleInstall(bundlerPath, bundleDir, gemfilePath);
|
||||
} catch (err) {
|
||||
debug(
|
||||
'unable to build gems from Gemfile. vendor the gems locally with "bundle install --deployment" and retry.'
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -66,6 +66,23 @@ export async function installBundler(meta: Meta, gemfileContents: string) {
|
||||
gemfileContents
|
||||
);
|
||||
|
||||
// If the new File System API is used (`avoidTopLevelInstall`), the Install Command
|
||||
// will have already installed the dependencies, so we don't need to do it again.
|
||||
if (meta.avoidTopLevelInstall) {
|
||||
debug(
|
||||
`Skipping bundler installation, already installed by Install Command`
|
||||
);
|
||||
|
||||
return {
|
||||
gemHome,
|
||||
rubyPath,
|
||||
gemPath,
|
||||
vendorPath,
|
||||
runtime,
|
||||
bundlerPath: join(gemHome, 'bin', 'bundler'),
|
||||
};
|
||||
}
|
||||
|
||||
debug('installing bundler...');
|
||||
await execa(gemPath, ['install', 'bundler', '--no-document'], {
|
||||
stdio: 'pipe',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@vercel/ruby",
|
||||
"author": "Nathan Cahill <nathan@nathancahill.com>",
|
||||
"version": "1.2.8-canary.4",
|
||||
"version": "1.2.8-canary.6",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
|
||||
|
||||
2
packages/ruby/test/fixtures/02-cowsay-vendored/.bundle/config
vendored
Normal file
2
packages/ruby/test/fixtures/02-cowsay-vendored/.bundle/config
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
---
|
||||
BUNDLE_PATH: "vendor/bundle"
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
ruby "~> 2.5.0"
|
||||
ruby "~> 2.7.0"
|
||||
|
||||
gem "cowsay", "~> 0.3.0"
|
||||
|
||||
16
packages/ruby/test/fixtures/02-cowsay-vendored/Gemfile.lock
vendored
Normal file
16
packages/ruby/test/fixtures/02-cowsay-vendored/Gemfile.lock
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
cowsay (0.3.0)
|
||||
|
||||
PLATFORMS
|
||||
x86_64-darwin-21
|
||||
|
||||
DEPENDENCIES
|
||||
cowsay (~> 0.3.0)
|
||||
|
||||
RUBY VERSION
|
||||
ruby 2.7.5p203
|
||||
|
||||
BUNDLED WITH
|
||||
2.2.22
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "index.rb", "use": "@vercel/ruby" }],
|
||||
"build": { "env": { "RUBY_VERSION": "2.5.x" } },
|
||||
"build": { "env": { "RUBY_VERSION": "2.7.x" } },
|
||||
"probes": [{ "path": "/", "mustContain": "gem:RANDOMNESS_PLACEHOLDER" }]
|
||||
}
|
||||
|
||||
@@ -14,19 +14,17 @@ Gem::Specification.new do |s|
|
||||
s.executables = ["cowsay".freeze]
|
||||
s.files = ["bin/cowsay".freeze]
|
||||
s.homepage = "https://github.com/moneydesktop/cowsay".freeze
|
||||
s.rubygems_version = "3.0.3".freeze
|
||||
s.rubygems_version = "3.2.22".freeze
|
||||
s.summary = "ASCII art avatars emote your messages".freeze
|
||||
|
||||
s.installed_by_version = "3.0.3" if s.respond_to? :installed_by_version
|
||||
s.installed_by_version = "3.2.22" if s.respond_to? :installed_by_version
|
||||
|
||||
if s.respond_to? :specification_version then
|
||||
s.specification_version = 4
|
||||
end
|
||||
|
||||
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
||||
s.add_development_dependency(%q<rake>.freeze, [">= 0"])
|
||||
else
|
||||
s.add_dependency(%q<rake>.freeze, [">= 0"])
|
||||
end
|
||||
if s.respond_to? :add_runtime_dependency then
|
||||
s.add_development_dependency(%q<rake>.freeze, [">= 0"])
|
||||
else
|
||||
s.add_dependency(%q<rake>.freeze, [">= 0"])
|
||||
end
|
||||
2
packages/ruby/test/fixtures/04-cowsay-vendored-nested/.bundle/config
vendored
Normal file
2
packages/ruby/test/fixtures/04-cowsay-vendored-nested/.bundle/config
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
---
|
||||
BUNDLE_PATH: "vendor/bundle"
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "project/index.rb", "use": "@vercel/ruby" }],
|
||||
"build": { "env": { "RUBY_VERSION": "2.5.x" } },
|
||||
"build": { "env": { "RUBY_VERSION": "2.7.x" } },
|
||||
"probes": [
|
||||
{ "path": "/project/", "mustContain": "gem:RANDOMNESS_PLACEHOLDER" }
|
||||
]
|
||||
|
||||
2
packages/ruby/test/fixtures/04-cowsay-vendored-nested/project/.bundle/config
vendored
Normal file
2
packages/ruby/test/fixtures/04-cowsay-vendored-nested/project/.bundle/config
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
---
|
||||
BUNDLE_PATH: "vendor/bundle"
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
ruby "~> 2.5.0"
|
||||
ruby "~> 2.7.0"
|
||||
|
||||
gem "cowsay", "~> 0.3.0"
|
||||
|
||||
16
packages/ruby/test/fixtures/04-cowsay-vendored-nested/project/Gemfile.lock
vendored
Normal file
16
packages/ruby/test/fixtures/04-cowsay-vendored-nested/project/Gemfile.lock
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
cowsay (0.3.0)
|
||||
|
||||
PLATFORMS
|
||||
x86_64-darwin-21
|
||||
|
||||
DEPENDENCIES
|
||||
cowsay (~> 0.3.0)
|
||||
|
||||
RUBY VERSION
|
||||
ruby 2.7.5p203
|
||||
|
||||
BUNDLED WITH
|
||||
2.2.22
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user