mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-11 12:57:46 +00:00
Compare commits
21 Commits
@vercel/py
...
@vercel/ed
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5eb8b16cbd | ||
|
|
226bf02be2 | ||
|
|
8505872f55 | ||
|
|
7db6436797 | ||
|
|
e2d76e9c92 | ||
|
|
337cb21d67 | ||
|
|
6bfff3e9eb | ||
|
|
ac5b259c11 | ||
|
|
bfc553db11 | ||
|
|
2b101d4692 | ||
|
|
3316f38cb4 | ||
|
|
7837387127 | ||
|
|
f478200dd3 | ||
|
|
c29de8206a | ||
|
|
a2df3b5463 | ||
|
|
73446e544a | ||
|
|
21ff4a58c3 | ||
|
|
2b9eb02b8c | ||
|
|
4ef4722460 | ||
|
|
be5308b137 | ||
|
|
08a83a94f8 |
@@ -1,7 +1,9 @@
|
||||
# Runtime Developer Reference
|
||||
|
||||
The following page is a reference for how to create a Runtime by implementing
|
||||
the Runtime API interface.
|
||||
the Runtime API interface. It's a way to add support for a new programming language to Vercel.
|
||||
|
||||
> Note: If you're the author of a web framework, please use the [Build Output API](https://vercel.com/docs/build-output-api/v3) instead to make your framework compatible with Vercel.
|
||||
|
||||
A Runtime is an npm module that implements the following interface:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "5.0.8",
|
||||
"version": "5.1.1",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
@@ -14,8 +14,7 @@
|
||||
"build": "node build",
|
||||
"test": "jest --env node --verbose --runInBand --bail",
|
||||
"test-unit": "yarn test test/unit.*test.*",
|
||||
"test-integration-once": "yarn test test/integration.test.ts",
|
||||
"prepublishOnly": "node build"
|
||||
"test-integration-once": "yarn test test/integration.test.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iarna/toml": "2.2.3",
|
||||
|
||||
@@ -27,9 +27,7 @@ async function prepareSymlinkTarget(
|
||||
}
|
||||
|
||||
if (file.type === 'FileRef' || file.type === 'FileBlob') {
|
||||
const targetPathBufferPromise = await streamToBuffer(
|
||||
await file.toStreamAsync()
|
||||
);
|
||||
const targetPathBufferPromise = streamToBuffer(await file.toStreamAsync());
|
||||
const [targetPathBuffer] = await Promise.all([
|
||||
targetPathBufferPromise,
|
||||
mkdirPromise,
|
||||
@@ -42,9 +40,15 @@ async function prepareSymlinkTarget(
|
||||
);
|
||||
}
|
||||
|
||||
async function downloadFile(file: File, fsPath: string): Promise<FileFsRef> {
|
||||
export async function downloadFile(
|
||||
file: File,
|
||||
fsPath: string
|
||||
): Promise<FileFsRef> {
|
||||
const { mode } = file;
|
||||
|
||||
// If the source is a symlink, try to create it instead of copying the file.
|
||||
// Note: creating symlinks on Windows requires admin priviliges or symlinks
|
||||
// enabled in the group policy. We may want to improve the error message.
|
||||
if (isSymbolicLink(mode)) {
|
||||
const target = await prepareSymlinkTarget(file, fsPath);
|
||||
|
||||
|
||||
@@ -4,7 +4,11 @@ import FileRef from './file-ref';
|
||||
import { Lambda, createLambda, getLambdaOptionsFromFunction } from './lambda';
|
||||
import { NodejsLambda } from './nodejs-lambda';
|
||||
import { Prerender } from './prerender';
|
||||
import download, { DownloadedFiles, isSymbolicLink } from './fs/download';
|
||||
import download, {
|
||||
downloadFile,
|
||||
DownloadedFiles,
|
||||
isSymbolicLink,
|
||||
} from './fs/download';
|
||||
import getWriteableDirectory from './fs/get-writable-directory';
|
||||
import glob, { GlobOptions } from './fs/glob';
|
||||
import rename from './fs/rename';
|
||||
@@ -46,6 +50,7 @@ export {
|
||||
createLambda,
|
||||
Prerender,
|
||||
download,
|
||||
downloadFile,
|
||||
DownloadedFiles,
|
||||
getWriteableDirectory,
|
||||
glob,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "27.3.4",
|
||||
"version": "27.3.6",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -16,7 +16,6 @@
|
||||
"test-unit": "yarn test test/unit/",
|
||||
"test-integration-cli": "rimraf test/fixtures/integration && ava test/integration.js --serial --fail-fast --verbose",
|
||||
"test-integration-dev": "yarn test test/dev/",
|
||||
"prepublishOnly": "yarn build",
|
||||
"coverage": "codecov",
|
||||
"build": "ts-node ./scripts/build.ts",
|
||||
"dev": "ts-node ./src/index.ts"
|
||||
@@ -42,16 +41,16 @@
|
||||
"node": ">= 14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "5.0.8",
|
||||
"@vercel/go": "2.0.12",
|
||||
"@vercel/hydrogen": "0.0.9",
|
||||
"@vercel/next": "3.1.12",
|
||||
"@vercel/node": "2.5.3",
|
||||
"@vercel/python": "3.1.4",
|
||||
"@vercel/redwood": "1.0.13",
|
||||
"@vercel/remix": "1.0.14",
|
||||
"@vercel/ruby": "1.3.20",
|
||||
"@vercel/static-build": "1.0.13",
|
||||
"@vercel/build-utils": "5.1.1",
|
||||
"@vercel/go": "2.0.14",
|
||||
"@vercel/hydrogen": "0.0.11",
|
||||
"@vercel/next": "3.1.14",
|
||||
"@vercel/node": "2.5.5",
|
||||
"@vercel/python": "3.1.6",
|
||||
"@vercel/redwood": "1.0.15",
|
||||
"@vercel/remix": "1.0.16",
|
||||
"@vercel/ruby": "1.3.22",
|
||||
"@vercel/static-build": "1.0.15",
|
||||
"update-notifier": "5.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -97,9 +96,9 @@
|
||||
"@types/which": "1.3.2",
|
||||
"@types/write-json-file": "2.2.1",
|
||||
"@types/yauzl-promise": "2.1.0",
|
||||
"@vercel/client": "12.1.7",
|
||||
"@vercel/frameworks": "1.1.1",
|
||||
"@vercel/fs-detectors": "2.0.3",
|
||||
"@vercel/client": "12.1.9",
|
||||
"@vercel/frameworks": "1.1.2",
|
||||
"@vercel/fs-detectors": "2.0.4",
|
||||
"@vercel/fun": "1.0.4",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@zeit/source-map-support": "0.6.2",
|
||||
|
||||
@@ -469,6 +469,8 @@ async function doBuild(
|
||||
)
|
||||
);
|
||||
} catch (err: any) {
|
||||
output.prettyError(err);
|
||||
|
||||
const writeConfigJsonPromise = fs.writeJSON(
|
||||
join(outputDir, 'config.json'),
|
||||
{ version: 3 },
|
||||
|
||||
13
packages/cli/src/commands/env/pull.ts
vendored
13
packages/cli/src/commands/env/pull.ts
vendored
@@ -130,6 +130,12 @@ export default async function pull(
|
||||
|
||||
await outputFile(fullPath, contents, 'utf8');
|
||||
|
||||
if (deltaString) {
|
||||
output.print('\n' + deltaString);
|
||||
} else if (oldEnv && exists) {
|
||||
output.log('No changes found.');
|
||||
}
|
||||
|
||||
output.print(
|
||||
`${prependEmoji(
|
||||
`${exists ? 'Updated' : 'Created'} ${chalk.bold(
|
||||
@@ -139,13 +145,6 @@ export default async function pull(
|
||||
)}\n`
|
||||
);
|
||||
|
||||
output.print('\n');
|
||||
if (deltaString) {
|
||||
output.print(deltaString);
|
||||
} else if (oldEnv && exists) {
|
||||
output.log('No changes found.');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import { Build } from '../types';
|
||||
import title from 'title';
|
||||
import { isErrnoException } from '../util/is-error';
|
||||
import { isAPIError } from '../util/errors-ts';
|
||||
import { URL } from 'url';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
@@ -66,7 +67,7 @@ export default async function main(client: Client) {
|
||||
const { print, log, error } = client.output;
|
||||
|
||||
// extract the first parameter
|
||||
const [, deploymentIdOrHost] = argv._;
|
||||
let [, deploymentIdOrHost] = argv._;
|
||||
|
||||
if (argv._.length !== 2) {
|
||||
error(`${getCommandName('inspect <url>')} expects exactly one argument`);
|
||||
@@ -90,12 +91,16 @@ export default async function main(client: Client) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// resolve the deployment, since we might have been given an alias
|
||||
const depFetchStart = Date.now();
|
||||
|
||||
try {
|
||||
deploymentIdOrHost = new URL(deploymentIdOrHost).hostname;
|
||||
} catch {}
|
||||
client.output.spinner(
|
||||
`Fetching deployment "${deploymentIdOrHost}" in ${chalk.bold(contextName)}`
|
||||
);
|
||||
|
||||
// resolve the deployment, since we might have been given an alias
|
||||
try {
|
||||
deployment = await getDeployment(client, deploymentIdOrHost);
|
||||
} catch (err: unknown) {
|
||||
|
||||
@@ -5,7 +5,7 @@ try {
|
||||
// Test to see if cwd has been deleted before
|
||||
// importing 3rd party packages that might need cwd.
|
||||
process.cwd();
|
||||
} catch (err) {
|
||||
} catch (err: unknown) {
|
||||
if (isError(err) && err.message.includes('uv_cwd')) {
|
||||
console.error('Error! The current working directory does not exist.');
|
||||
process.exit(1);
|
||||
@@ -40,8 +40,8 @@ import getConfig from './util/get-config';
|
||||
import * as configFiles from './util/config/files';
|
||||
import getGlobalPathConfig from './util/config/global-path';
|
||||
import {
|
||||
getDefaultConfig,
|
||||
getDefaultAuthConfig,
|
||||
defaultAuthConfig,
|
||||
defaultGlobalConfig,
|
||||
} from './util/config/get-default';
|
||||
import * as ERRORS from './util/errors-ts';
|
||||
import { APIError } from './util/errors-ts';
|
||||
@@ -50,7 +50,7 @@ import getUpdateCommand from './util/get-update-command';
|
||||
import { metrics, shouldCollectMetrics } from './util/metrics';
|
||||
import { getCommandName, getTitleName } from './util/pkg-name';
|
||||
import doLoginPrompt from './util/login/prompt';
|
||||
import { GlobalConfig } from './types';
|
||||
import { AuthConfig, GlobalConfig } from './types';
|
||||
import { VercelConfig } from '@vercel/client';
|
||||
|
||||
const isCanary = pkg.version.includes('canary');
|
||||
@@ -208,160 +208,59 @@ const main = async () => {
|
||||
VERCEL_DIR
|
||||
)}" ${errorToString(err)}`
|
||||
);
|
||||
}
|
||||
|
||||
let migrated = false;
|
||||
let configExists;
|
||||
|
||||
try {
|
||||
configExists = existsSync(VERCEL_CONFIG_PATH);
|
||||
} catch (err: unknown) {
|
||||
console.error(
|
||||
error(
|
||||
`${
|
||||
'An unexpected error occurred while trying to find the ' +
|
||||
`config file "${hp(VERCEL_CONFIG_PATH)}" `
|
||||
}${errorToString(err)}`
|
||||
)
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
let config: GlobalConfig | null = null;
|
||||
|
||||
if (configExists) {
|
||||
try {
|
||||
config = configFiles.readConfigFile();
|
||||
} catch (err) {
|
||||
console.error(
|
||||
error(
|
||||
`${
|
||||
'An unexpected error occurred while trying to read the ' +
|
||||
`config in "${hp(VERCEL_CONFIG_PATH)}" `
|
||||
}${errorToString(err)}`
|
||||
)
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// This is from when Vercel CLI supported
|
||||
// multiple providers. In that case, we really
|
||||
// need to migrate.
|
||||
if (
|
||||
// @ts-ignore
|
||||
config.sh ||
|
||||
// @ts-ignore
|
||||
config.user ||
|
||||
// @ts-ignore
|
||||
typeof config.user === 'object' ||
|
||||
typeof config.currentTeam === 'object'
|
||||
) {
|
||||
configExists = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!configExists) {
|
||||
const results = await getDefaultConfig(config);
|
||||
|
||||
config = results.config;
|
||||
migrated = results.migrated;
|
||||
|
||||
try {
|
||||
configFiles.writeToConfigFile(config);
|
||||
} catch (err: unknown) {
|
||||
console.error(
|
||||
error(
|
||||
`${
|
||||
'An unexpected error occurred while trying to write the ' +
|
||||
`default config to "${hp(VERCEL_CONFIG_PATH)}" `
|
||||
}${errorToString(err)}`
|
||||
)
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
let authConfigExists;
|
||||
|
||||
try {
|
||||
authConfigExists = existsSync(VERCEL_AUTH_CONFIG_PATH);
|
||||
} catch (err: unknown) {
|
||||
console.error(
|
||||
error(
|
||||
`${
|
||||
'An unexpected error occurred while trying to find the ' +
|
||||
`auth file "${hp(VERCEL_AUTH_CONFIG_PATH)}" `
|
||||
}${errorToString(err)}`
|
||||
)
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
let authConfig = null;
|
||||
|
||||
const subcommandsWithoutToken = [
|
||||
'login',
|
||||
'logout',
|
||||
'help',
|
||||
'init',
|
||||
'update',
|
||||
'build',
|
||||
];
|
||||
|
||||
if (authConfigExists) {
|
||||
try {
|
||||
authConfig = configFiles.readAuthConfigFile();
|
||||
} catch (err: unknown) {
|
||||
console.error(
|
||||
error(
|
||||
`${
|
||||
'An unexpected error occurred while trying to read the ' +
|
||||
`auth config in "${hp(VERCEL_AUTH_CONFIG_PATH)}" `
|
||||
}${errorToString(err)}`
|
||||
)
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// This is from when Vercel CLI supported
|
||||
// multiple providers. In that case, we really
|
||||
// need to migrate.
|
||||
// @ts-ignore
|
||||
if (authConfig.credentials) {
|
||||
authConfigExists = false;
|
||||
}
|
||||
} else {
|
||||
const results = await getDefaultAuthConfig(authConfig);
|
||||
|
||||
authConfig = results.config;
|
||||
migrated = results.migrated;
|
||||
|
||||
try {
|
||||
configFiles.writeToAuthConfigFile(authConfig);
|
||||
} catch (err: unknown) {
|
||||
console.error(
|
||||
error(
|
||||
`${
|
||||
'An unexpected error occurred while trying to write the ' +
|
||||
`default config to "${hp(VERCEL_AUTH_CONFIG_PATH)}" `
|
||||
}${errorToString(err)}`
|
||||
)
|
||||
let config: GlobalConfig;
|
||||
try {
|
||||
config = configFiles.readConfigFile();
|
||||
} catch (err: unknown) {
|
||||
if (isErrnoException(err) && err.code === 'ENOENT') {
|
||||
config = defaultGlobalConfig;
|
||||
try {
|
||||
configFiles.writeToConfigFile(config);
|
||||
} catch (err: unknown) {
|
||||
output.error(
|
||||
`An unexpected error occurred while trying to save the config to "${hp(
|
||||
VERCEL_CONFIG_PATH
|
||||
)}" ${errorToString(err)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
output.error(
|
||||
`An unexpected error occurred while trying to read the config in "${hp(
|
||||
VERCEL_CONFIG_PATH
|
||||
)}" ${errorToString(err)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Let the user know we migrated the config
|
||||
if (migrated) {
|
||||
const directory = param(hp(VERCEL_DIR));
|
||||
debug(
|
||||
`The credentials and configuration within the ${directory} directory were upgraded`
|
||||
);
|
||||
let authConfig: AuthConfig;
|
||||
try {
|
||||
authConfig = configFiles.readAuthConfigFile();
|
||||
} catch (err: unknown) {
|
||||
if (isErrnoException(err) && err.code === 'ENOENT') {
|
||||
authConfig = defaultAuthConfig;
|
||||
try {
|
||||
configFiles.writeToAuthConfigFile(authConfig);
|
||||
} catch (err: unknown) {
|
||||
output.error(
|
||||
`An unexpected error occurred while trying to write the auth config to "${hp(
|
||||
VERCEL_AUTH_CONFIG_PATH
|
||||
)}" ${errorToString(err)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
output.error(
|
||||
`An unexpected error occurred while trying to read the auth config in "${hp(
|
||||
VERCEL_AUTH_CONFIG_PATH
|
||||
)}" ${errorToString(err)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof argv['--api'] === 'string') {
|
||||
@@ -371,18 +270,12 @@ const main = async () => {
|
||||
}
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line no-new
|
||||
new URL(apiUrl);
|
||||
} catch (err) {
|
||||
} catch (err: unknown) {
|
||||
output.error(`Please provide a valid URL instead of ${highlight(apiUrl)}.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!config) {
|
||||
output.error(`Vercel global config was not loaded.`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Shared API `Client` instance for all sub-commands to utilize
|
||||
client = new Client({
|
||||
apiUrl,
|
||||
@@ -430,6 +323,15 @@ const main = async () => {
|
||||
client.argv.push('-h');
|
||||
}
|
||||
|
||||
const subcommandsWithoutToken = [
|
||||
'login',
|
||||
'logout',
|
||||
'help',
|
||||
'init',
|
||||
'update',
|
||||
'build',
|
||||
];
|
||||
|
||||
// Prompt for login if there is no current token
|
||||
if (
|
||||
(!authConfig || !authConfig.token) &&
|
||||
|
||||
@@ -20,13 +20,15 @@ export interface JSONObject {
|
||||
}
|
||||
|
||||
export interface AuthConfig {
|
||||
_?: string;
|
||||
'// Note'?: string;
|
||||
'// Docs'?: string;
|
||||
token?: string;
|
||||
skipWrite?: boolean;
|
||||
}
|
||||
|
||||
export interface GlobalConfig {
|
||||
_?: string;
|
||||
'// Note'?: string;
|
||||
'// Docs'?: string;
|
||||
currentTeam?: string;
|
||||
includeScheme?: string;
|
||||
collectMetrics?: boolean;
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
PackageJson,
|
||||
Prerender,
|
||||
download,
|
||||
downloadFile,
|
||||
EdgeFunction,
|
||||
BuildResultBuildOutput,
|
||||
getLambdaOptionsFromFunction,
|
||||
@@ -266,9 +267,7 @@ async function writeStaticFile(
|
||||
const dest = join(outputDir, 'static', fsPath);
|
||||
await fs.mkdirp(dirname(dest));
|
||||
|
||||
// TODO: handle (or skip) symlinks?
|
||||
const stream = file.toStream();
|
||||
await pipe(stream, fs.createWriteStream(dest, { mode: file.mode }));
|
||||
await downloadFile(file, dest);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,75 +1,15 @@
|
||||
import { AuthConfig, GlobalConfig } from '../../types';
|
||||
|
||||
export const getDefaultConfig = async (existingCopy?: GlobalConfig | null) => {
|
||||
let migrated = false;
|
||||
|
||||
const config: GlobalConfig = {
|
||||
_: 'This is your Vercel config file. For more information see the global configuration documentation: https://vercel.com/docs/configuration#global',
|
||||
collectMetrics: true,
|
||||
};
|
||||
|
||||
if (existingCopy) {
|
||||
const keep = [
|
||||
'_',
|
||||
'currentTeam',
|
||||
'desktop',
|
||||
'updateChannel',
|
||||
'collectMetrics',
|
||||
'api',
|
||||
// This is deleted later in the code
|
||||
];
|
||||
|
||||
try {
|
||||
const existing = Object.assign({}, existingCopy);
|
||||
// @ts-ignore
|
||||
const sh = Object.assign({}, existing.sh || {});
|
||||
|
||||
Object.assign(config, existing, sh);
|
||||
|
||||
for (const key of Object.keys(config)) {
|
||||
if (!keep.includes(key)) {
|
||||
// @ts-ignore
|
||||
delete config[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof config.currentTeam === 'object') {
|
||||
// @ts-ignore
|
||||
config.currentTeam = config.currentTeam.id;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
if (typeof config.user === 'object') {
|
||||
// @ts-ignore
|
||||
config.user = config.user.uid || config.user.id;
|
||||
}
|
||||
|
||||
migrated = true;
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
return { config, migrated };
|
||||
export const defaultGlobalConfig: GlobalConfig = {
|
||||
'// Note':
|
||||
'This is your Vercel config file. For more information see the global configuration documentation.',
|
||||
'// Docs':
|
||||
'https://vercel.com/docs/project-configuration#global-configuration/config-json',
|
||||
collectMetrics: true,
|
||||
};
|
||||
|
||||
export const getDefaultAuthConfig = async (existing?: AuthConfig | null) => {
|
||||
let migrated = false;
|
||||
|
||||
const config: AuthConfig = {
|
||||
_: 'This is your Vercel credentials file. DO NOT SHARE! More: https://vercel.com/docs/configuration#global',
|
||||
};
|
||||
|
||||
if (existing) {
|
||||
try {
|
||||
// @ts-ignore
|
||||
const sh = existing.credentials.find(item => item.provider === 'sh');
|
||||
|
||||
if (sh) {
|
||||
config.token = sh.token;
|
||||
}
|
||||
|
||||
migrated = true;
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
return { config, migrated };
|
||||
export const defaultAuthConfig: AuthConfig = {
|
||||
'// Note': 'This is your Vercel credentials file. DO NOT SHARE!',
|
||||
'// Docs':
|
||||
'https://vercel.com/docs/project-configuration#global-configuration/auth-json',
|
||||
};
|
||||
|
||||
@@ -1556,6 +1556,8 @@ export default class DevServer {
|
||||
(err as any).link = 'https://vercel.link/command-not-found';
|
||||
}
|
||||
|
||||
this.output.prettyError(err);
|
||||
|
||||
await this.sendError(
|
||||
req,
|
||||
res,
|
||||
|
||||
14
packages/cli/src/util/env/diff-env-files.ts
vendored
14
packages/cli/src/util/env/diff-env-files.ts
vendored
@@ -63,19 +63,25 @@ export function buildDeltaString(
|
||||
const { added, changed, removed } = findChanges(oldEnv, newEnv);
|
||||
|
||||
let deltaString = '';
|
||||
deltaString += chalk.green(addDeltaSection('+', changed, true));
|
||||
deltaString += chalk.green(addDeltaSection('+', added));
|
||||
deltaString += chalk.yellow(addDeltaSection('~', changed));
|
||||
deltaString += chalk.red(addDeltaSection('-', removed));
|
||||
|
||||
return deltaString ? chalk.gray('Changes:\n') + deltaString : deltaString;
|
||||
return deltaString
|
||||
? chalk.gray('Changes:\n') + deltaString + '\n'
|
||||
: deltaString;
|
||||
}
|
||||
|
||||
function addDeltaSection(prefix: string, arr: string[]): string {
|
||||
function addDeltaSection(
|
||||
prefix: string,
|
||||
arr: string[],
|
||||
changed: boolean = false
|
||||
): string {
|
||||
if (arr.length === 0) return '';
|
||||
return (
|
||||
arr
|
||||
.sort()
|
||||
.map(item => `${prefix} ${item}`)
|
||||
.map(item => `${prefix} ${item}${changed ? ' (Updated)' : ''}`)
|
||||
.join('\n') + '\n'
|
||||
);
|
||||
}
|
||||
|
||||
10
packages/cli/test/dev/fixtures/go/api/[segement].go
Normal file
10
packages/cli/test/dev/fixtures/go/api/[segement].go
Normal file
@@ -0,0 +1,10 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Handler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Req Path: %s", r.URL.Path)
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package handler
|
||||
package another
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Another(w http.ResponseWriter, r *http.Request) {
|
||||
func HandlerAnother(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "This is another page")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export const config = {
|
||||
matcher: 'not-a-valid-matcher',
|
||||
};
|
||||
|
||||
export default function middleware(request, _event) {
|
||||
return new Response(null);
|
||||
}
|
||||
@@ -227,7 +227,7 @@ test('[vercel dev] should handle syntax errors thrown in edge functions', async
|
||||
expect(await res.text()).toMatch(
|
||||
/<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g
|
||||
);
|
||||
expect(stderr).toMatch(/Failed to instantiate edge runtime./g);
|
||||
expect(stderr).toMatch(/Failed to compile user code for edge runtime./g);
|
||||
expect(stderr).toMatch(/Unexpected end of file/g);
|
||||
expect(stderr).toMatch(
|
||||
/Failed to complete request to \/api\/edge-error-syntax: Error: socket hang up/g
|
||||
@@ -307,6 +307,35 @@ test('[vercel dev] should handle missing handler errors thrown in edge functions
|
||||
}
|
||||
});
|
||||
|
||||
test('[vercel dev] should handle invalid middleware config', async () => {
|
||||
const dir = fixture('middleware-matchers-invalid');
|
||||
const { dev, port, readyResolver } = await testFixture(dir);
|
||||
|
||||
try {
|
||||
await readyResolver;
|
||||
|
||||
let res = await fetch(`http://localhost:${port}/api/whatever`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept:
|
||||
'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
},
|
||||
});
|
||||
validateResponseHeaders(res);
|
||||
|
||||
const { stderr } = await dev.kill('SIGTERM');
|
||||
|
||||
expect(await res.text()).toMatch(
|
||||
/<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g
|
||||
);
|
||||
expect(stderr).toMatch(
|
||||
/Middleware's `config.matcher` .+ Received: not-a-valid-matcher/g
|
||||
);
|
||||
} finally {
|
||||
await dev.kill('SIGTERM');
|
||||
}
|
||||
});
|
||||
|
||||
test('[vercel dev] should support request body', async () => {
|
||||
const dir = fixture('node-request-body');
|
||||
const { dev, port, readyResolver } = await testFixture(dir);
|
||||
|
||||
@@ -388,9 +388,8 @@ test(
|
||||
await testPath(200, `/api/index.go`, 'This is the index page');
|
||||
await testPath(200, `/api/another`, 'This is another page');
|
||||
await testPath(200, '/api/another.go', 'This is another page');
|
||||
// DISABLED: These assertions rely on different bracket names working.
|
||||
// await testPath(200, `/api/foo`, 'Req Path: /api/foo');
|
||||
// await testPath(200, `/api/bar`, 'Req Path: /api/bar');
|
||||
await testPath(200, `/api/foo`, 'Req Path: /api/foo');
|
||||
await testPath(200, `/api/bar`, 'Req Path: /api/bar');
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -348,7 +348,10 @@ function testFixtureStdio(
|
||||
: []),
|
||||
'deploy',
|
||||
...(process.env.VERCEL_CLI_VERSION
|
||||
? ['--build-env', `VERCEL_CLI_VERSION=${process.env.VERCEL_CLI_VERSION}`]
|
||||
? [
|
||||
'--build-env',
|
||||
`VERCEL_CLI_VERSION=${process.env.VERCEL_CLI_VERSION}`,
|
||||
]
|
||||
: []),
|
||||
'--public',
|
||||
'--debug',
|
||||
@@ -430,7 +433,7 @@ function testFixtureStdio(
|
||||
);
|
||||
}
|
||||
|
||||
if (stderr.includes('Command failed') || stderr.includes('Error!')) {
|
||||
if (stderr.includes('Command failed')) {
|
||||
dev.kill('SIGTERM');
|
||||
throw new Error(`Failed for "${directory}" with stderr "${stderr}".`);
|
||||
}
|
||||
|
||||
7
packages/cli/test/fixtures/unit/commands/build/static-symlink/.vercel/project.json
vendored
Normal file
7
packages/cli/test/fixtures/unit/commands/build/static-symlink/.vercel/project.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"orgId": ".",
|
||||
"projectId": ".",
|
||||
"settings": {
|
||||
"framework": null
|
||||
}
|
||||
}
|
||||
1
packages/cli/test/fixtures/unit/commands/build/static-symlink/index.html
vendored
Normal file
1
packages/cli/test/fixtures/unit/commands/build/static-symlink/index.html
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<h1>Vercel</h1>
|
||||
@@ -143,6 +143,54 @@ describe('build', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle symlinked static files', async () => {
|
||||
const cwd = fixture('static-symlink');
|
||||
const output = join(cwd, '.vercel/output');
|
||||
|
||||
// try to create the symlink, if it fails (e.g. Windows), skip the test
|
||||
try {
|
||||
await fs.unlink(join(cwd, 'foo.html'));
|
||||
await fs.symlink(join(cwd, 'index.html'), join(cwd, 'foo.html'));
|
||||
} catch (e) {
|
||||
console.log('Symlinks not available, skipping test');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
const exitCode = await build(client);
|
||||
expect(exitCode).toEqual(0);
|
||||
|
||||
// `builds.json` says that "@vercel/static" was run
|
||||
const builds = await fs.readJSON(join(output, 'builds.json'));
|
||||
expect(builds).toMatchObject({
|
||||
target: 'preview',
|
||||
builds: [
|
||||
{
|
||||
require: '@vercel/static',
|
||||
apiVersion: 2,
|
||||
src: '**',
|
||||
use: '@vercel/static',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// "static" directory contains static files
|
||||
const files = await fs.readdir(join(output, 'static'));
|
||||
expect(files.sort()).toEqual(['foo.html', 'index.html']);
|
||||
expect(
|
||||
(await fs.lstat(join(output, 'static', 'foo.html'))).isSymbolicLink()
|
||||
).toEqual(true);
|
||||
expect(
|
||||
(await fs.lstat(join(output, 'static', 'index.html'))).isSymbolicLink()
|
||||
).toEqual(false);
|
||||
} finally {
|
||||
await fs.unlink(join(cwd, 'foo.html'));
|
||||
process.chdir(originalCwd);
|
||||
delete process.env.__VERCEL_BUILD_RUNNING;
|
||||
}
|
||||
});
|
||||
|
||||
it('should normalize "src" path in `vercel.json`', async () => {
|
||||
const cwd = fixture('normalize-src');
|
||||
const output = join(cwd, '.vercel/output');
|
||||
@@ -661,6 +709,11 @@ describe('build', () => {
|
||||
const exitCode = await build(client);
|
||||
expect(exitCode).toEqual(1);
|
||||
|
||||
// Error gets printed to the terminal
|
||||
await expect(client.stderr).toOutput(
|
||||
'Error! Function must contain at least one property.'
|
||||
);
|
||||
|
||||
// `builds.json` contains top-level "error" property
|
||||
const builds = await fs.readJSON(join(output, 'builds.json'));
|
||||
expect(builds.builds).toBeUndefined();
|
||||
@@ -687,6 +740,9 @@ describe('build', () => {
|
||||
const exitCode = await build(client);
|
||||
expect(exitCode).toEqual(1);
|
||||
|
||||
// Error gets printed to the terminal
|
||||
await expect(client.stderr).toOutput("Duplicate identifier 'res'.");
|
||||
|
||||
// `builds.json` contains "error" build
|
||||
const builds = await fs.readJSON(join(output, 'builds.json'));
|
||||
expect(builds.builds).toHaveLength(4);
|
||||
|
||||
@@ -178,10 +178,10 @@ describe('env', () => {
|
||||
await expect(client.stderr).toOutput(
|
||||
'Downloading `development` Environment Variables for Project env-pull-delta'
|
||||
);
|
||||
await expect(client.stderr).toOutput('Updated .env file');
|
||||
await expect(client.stderr).toOutput(
|
||||
'+ NEW_VAR\n~ SPECIAL_FLAG\n- TEST\n'
|
||||
'+ SPECIAL_FLAG (Updated)\n+ NEW_VAR\n- TEST\n'
|
||||
);
|
||||
await expect(client.stderr).toOutput('Updated .env file');
|
||||
|
||||
await expect(pullPromise).resolves.toEqual(0);
|
||||
} finally {
|
||||
@@ -218,8 +218,8 @@ describe('env', () => {
|
||||
|
||||
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
|
||||
const pullPromise = env(client);
|
||||
await expect(client.stderr).toOutput('Updated .env file');
|
||||
await expect(client.stderr).toOutput('> No changes found.');
|
||||
await expect(client.stderr).toOutput('Updated .env file');
|
||||
await expect(pullPromise).resolves.toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,6 +15,17 @@ describe('inspect', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should strip the scheme of a url', async () => {
|
||||
const user = useUser();
|
||||
const deployment = useDeployment({ creator: user });
|
||||
client.setArgv('inspect', `http://${deployment.url}`);
|
||||
const exitCode = await inspect(client);
|
||||
expect(exitCode).toEqual(0);
|
||||
await expect(client.stderr).toOutput(
|
||||
`> Fetched deployment ${deployment.url} in ${user.username}`
|
||||
);
|
||||
});
|
||||
|
||||
it('should print error when deployment not found', async () => {
|
||||
const user = useUser();
|
||||
useDeployment({ creator: user });
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "12.1.7",
|
||||
"version": "12.1.9",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -42,8 +42,8 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "5.0.8",
|
||||
"@vercel/routing-utils": "2.0.0",
|
||||
"@vercel/build-utils": "5.1.1",
|
||||
"@vercel/routing-utils": "2.0.1",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "3.0.0",
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "@vercel/edge",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.mjs",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsup src/index.ts --dts --format esm,cjs",
|
||||
"test-unit": "jest",
|
||||
"prepublishOnly": "yarn build"
|
||||
"test": "jest --env node --verbose --runInBand --bail",
|
||||
"test-unit": "yarn test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@edge-runtime/jest-environment": "1.1.0-beta.7",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/frameworks",
|
||||
"version": "1.1.1",
|
||||
"version": "1.1.2",
|
||||
"main": "./dist/frameworks.js",
|
||||
"types": "./dist/frameworks.d.ts",
|
||||
"files": [
|
||||
@@ -21,7 +21,7 @@
|
||||
"@types/js-yaml": "3.12.1",
|
||||
"@types/node": "12.0.4",
|
||||
"@types/node-fetch": "2.5.8",
|
||||
"@vercel/routing-utils": "2.0.0",
|
||||
"@vercel/routing-utils": "2.0.1",
|
||||
"ajv": "6.12.2",
|
||||
"typescript": "4.3.4"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/fs-detectors",
|
||||
"version": "2.0.3",
|
||||
"version": "2.0.4",
|
||||
"description": "Vercel filesystem detectors",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -14,14 +14,13 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"prepublishOnly": "tsc",
|
||||
"build": "tsc",
|
||||
"test": "yarn jest --env node --verbose --runInBand --bail test/unit.*test.*",
|
||||
"test": "jest --env node --verbose --runInBand --bail test/unit.*test.*",
|
||||
"test-unit": "yarn test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/frameworks": "1.1.1",
|
||||
"@vercel/routing-utils": "2.0.0",
|
||||
"@vercel/frameworks": "1.1.2",
|
||||
"@vercel/routing-utils": "2.0.1",
|
||||
"glob": "8.0.3",
|
||||
"js-yaml": "4.1.0",
|
||||
"minimatch": "3.0.4",
|
||||
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
mkdirp,
|
||||
move,
|
||||
remove,
|
||||
rmdir,
|
||||
readdir,
|
||||
} from 'fs-extra';
|
||||
import {
|
||||
BuildOptions,
|
||||
@@ -71,20 +73,23 @@ async function initPrivateGit(credentials: string) {
|
||||
* which works great for this feature. We also need to add a suffix during `vercel dev`
|
||||
* since the entrypoint is already stripped of its suffix before build() is called.
|
||||
*/
|
||||
async function getRenamedEntrypoint(entrypoint: string, files: Files) {
|
||||
function getRenamedEntrypoint(entrypoint: string): string | undefined {
|
||||
const filename = basename(entrypoint);
|
||||
if (filename.startsWith('[')) {
|
||||
const newEntrypoint = entrypoint.replace('/[', '/now-bracket[');
|
||||
const file = files[entrypoint];
|
||||
delete files[entrypoint];
|
||||
files[newEntrypoint] = file;
|
||||
|
||||
debug(`Renamed entrypoint from ${entrypoint} to ${newEntrypoint}`);
|
||||
entrypoint = newEntrypoint;
|
||||
return newEntrypoint;
|
||||
}
|
||||
|
||||
return entrypoint;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
type UndoFileAction = {
|
||||
from: string;
|
||||
to: string | undefined;
|
||||
};
|
||||
|
||||
export const version = 3;
|
||||
|
||||
export async function build({
|
||||
@@ -94,332 +99,456 @@ export async function build({
|
||||
workPath,
|
||||
meta = {},
|
||||
}: BuildOptions) {
|
||||
if (process.env.GIT_CREDENTIALS) {
|
||||
debug('Initialize Git credentials...');
|
||||
await initPrivateGit(process.env.GIT_CREDENTIALS);
|
||||
}
|
||||
|
||||
if (process.env.GO111MODULE) {
|
||||
console.log(`\nManually assigning 'GO111MODULE' is not recommended.
|
||||
|
||||
By default:
|
||||
- 'GO111MODULE=on' If entrypoint package name is not 'main'
|
||||
- 'GO111MODULE=off' If entrypoint package name is 'main'
|
||||
|
||||
We highly recommend you leverage Go Modules in your project.
|
||||
Learn more: https://github.com/golang/go/wiki/Modules
|
||||
`);
|
||||
}
|
||||
entrypoint = await getRenamedEntrypoint(entrypoint, files);
|
||||
const entrypointArr = entrypoint.split(sep);
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
let [goPath, outDir] = await Promise.all([
|
||||
getWriteableDirectory(),
|
||||
getWriteableDirectory(),
|
||||
]);
|
||||
|
||||
const goPath = await getWriteableDirectory();
|
||||
const srcPath = join(goPath, 'src', 'lambda');
|
||||
const downloadPath = meta.skipDownload ? workPath : srcPath;
|
||||
const downloadedFiles = await download(files, downloadPath, meta);
|
||||
await download(files, downloadPath, meta);
|
||||
|
||||
// keep track of file system actions we need to undo
|
||||
// the keys "from" and "to" refer to what needs to be done
|
||||
// in order to undo the action, not what the original action was
|
||||
const undoFileActions: UndoFileAction[] = [];
|
||||
const undoDirectoryCreation: string[] = [];
|
||||
|
||||
debug(`Parsing AST for "${entrypoint}"`);
|
||||
let analyzed: string;
|
||||
try {
|
||||
let goModAbsPathDir = '';
|
||||
const fileName = 'go.mod';
|
||||
if (fileName in downloadedFiles) {
|
||||
goModAbsPathDir = dirname(downloadedFiles[fileName].fsPath);
|
||||
debug(`Found ${fileName} file in "${goModAbsPathDir}"`);
|
||||
} else if ('api/go.mod' in downloadedFiles) {
|
||||
goModAbsPathDir = dirname(downloadedFiles['api/go.mod'].fsPath);
|
||||
debug(`Found ${fileName} file in "${goModAbsPathDir}"`);
|
||||
if (process.env.GIT_CREDENTIALS) {
|
||||
debug('Initialize Git credentials...');
|
||||
await initPrivateGit(process.env.GIT_CREDENTIALS);
|
||||
}
|
||||
analyzed = await getAnalyzedEntrypoint(
|
||||
workPath,
|
||||
downloadedFiles[entrypoint].fsPath,
|
||||
goModAbsPathDir
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(`Failed to parse AST for "${entrypoint}"`);
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!analyzed) {
|
||||
const err = new Error(
|
||||
`Could not find an exported function in "${entrypoint}"
|
||||
Learn more: https://vercel.com/docs/runtimes#official-runtimes/go
|
||||
`
|
||||
);
|
||||
console.log(err.message);
|
||||
throw err;
|
||||
}
|
||||
if (process.env.GO111MODULE) {
|
||||
console.log(`\nManually assigning 'GO111MODULE' is not recommended.
|
||||
|
||||
const parsedAnalyzed = JSON.parse(analyzed) as Analyzed;
|
||||
By default:
|
||||
- 'GO111MODULE=on' If entrypoint package name is not 'main'
|
||||
- 'GO111MODULE=off' If entrypoint package name is 'main'
|
||||
|
||||
// find `go.mod` in downloadedFiles
|
||||
const entrypointDirname = dirname(downloadedFiles[entrypoint].fsPath);
|
||||
let isGoModExist = false;
|
||||
let goModPath = '';
|
||||
let isGoModInRootDir = false;
|
||||
for (const file of Object.keys(downloadedFiles)) {
|
||||
const { fsPath } = downloadedFiles[file];
|
||||
const fileDirname = dirname(fsPath);
|
||||
if (file === 'go.mod') {
|
||||
isGoModExist = true;
|
||||
isGoModInRootDir = true;
|
||||
goModPath = fileDirname;
|
||||
} else if (file.endsWith('go.mod')) {
|
||||
if (entrypointDirname === fileDirname) {
|
||||
isGoModExist = true;
|
||||
goModPath = fileDirname;
|
||||
debug(`Found file dirname equals entrypoint dirname: ${fileDirname}`);
|
||||
break;
|
||||
We highly recommend you leverage Go Modules in your project.
|
||||
Learn more: https://github.com/golang/go/wiki/Modules
|
||||
`);
|
||||
}
|
||||
|
||||
const renamedEntrypoint = getRenamedEntrypoint(entrypoint);
|
||||
if (renamedEntrypoint) {
|
||||
await move(join(workPath, entrypoint), join(workPath, renamedEntrypoint));
|
||||
undoFileActions.push({
|
||||
to: join(workPath, entrypoint),
|
||||
from: join(workPath, renamedEntrypoint),
|
||||
});
|
||||
entrypoint = renamedEntrypoint;
|
||||
}
|
||||
|
||||
const entrypointAbsolute = join(workPath, entrypoint);
|
||||
const entrypointArr = entrypoint.split(sep);
|
||||
|
||||
debug(`Parsing AST for "${entrypoint}"`);
|
||||
let analyzed: string;
|
||||
try {
|
||||
const goModAbsPath = await findGoModPath(workPath);
|
||||
if (goModAbsPath) {
|
||||
debug(`Found ${goModAbsPath}"`);
|
||||
}
|
||||
|
||||
if (!isGoModInRootDir && config.zeroConfig && file === 'api/go.mod') {
|
||||
// We didn't find `/go.mod` but we found `/api/go.mod` so move it to the root
|
||||
analyzed = await getAnalyzedEntrypoint(
|
||||
workPath,
|
||||
entrypointAbsolute,
|
||||
dirname(goModAbsPath)
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(`Failed to parse AST for "${entrypoint}"`);
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!analyzed) {
|
||||
const err = new Error(
|
||||
`Could not find an exported function in "${entrypoint}"
|
||||
Learn more: https://vercel.com/docs/runtimes#official-runtimes/go
|
||||
`
|
||||
);
|
||||
console.log(err.message);
|
||||
throw err;
|
||||
}
|
||||
|
||||
const parsedAnalyzed = JSON.parse(analyzed) as Analyzed;
|
||||
|
||||
// find `go.mod` in modFiles
|
||||
const entrypointDirname = dirname(entrypointAbsolute);
|
||||
let isGoModExist = false;
|
||||
let goModPath = '';
|
||||
let isGoModInRootDir = false;
|
||||
|
||||
const modFileRefs = await glob('**/*.mod', workPath);
|
||||
const modFiles = Object.keys(modFileRefs);
|
||||
|
||||
for (const file of modFiles) {
|
||||
const fileDirname = dirname(file);
|
||||
if (file === 'go.mod') {
|
||||
isGoModExist = true;
|
||||
isGoModInRootDir = true;
|
||||
goModPath = join(fileDirname, '..');
|
||||
const pathParts = fsPath.split(sep);
|
||||
pathParts.pop(); // Remove go.mod
|
||||
pathParts.pop(); // Remove api
|
||||
pathParts.push('go.mod');
|
||||
const newFsPath = pathParts.join(sep);
|
||||
debug(`Moving api/go.mod to root: ${fsPath} to ${newFsPath}`);
|
||||
await move(fsPath, newFsPath);
|
||||
const oldSumPath = join(dirname(fsPath), 'go.sum');
|
||||
const newSumPath = join(dirname(newFsPath), 'go.sum');
|
||||
if (await pathExists(oldSumPath)) {
|
||||
debug(`Moving api/go.sum to root: ${oldSumPath} to ${newSumPath}`);
|
||||
await move(oldSumPath, newSumPath);
|
||||
goModPath = join(workPath, fileDirname);
|
||||
} else if (file.endsWith('go.mod')) {
|
||||
if (entrypointDirname === fileDirname) {
|
||||
isGoModExist = true;
|
||||
goModPath = join(workPath, fileDirname);
|
||||
|
||||
debug(`Found file dirname equals entrypoint dirname: ${fileDirname}`);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isGoModInRootDir && config.zeroConfig && file === 'api/go.mod') {
|
||||
// We didn't find `/go.mod` but we found `/api/go.mod` so move it to the root
|
||||
isGoModExist = true;
|
||||
isGoModInRootDir = true;
|
||||
goModPath = join(fileDirname, '..');
|
||||
const pathParts = file.split(sep);
|
||||
pathParts.pop(); // Remove go.mod
|
||||
pathParts.pop(); // Remove api
|
||||
pathParts.push('go.mod');
|
||||
|
||||
const newRoot = pathParts.join(sep);
|
||||
const newFsPath = join(workPath, newRoot);
|
||||
|
||||
debug(`Moving api/go.mod to root: ${file} to ${newFsPath}`);
|
||||
await move(file, newFsPath);
|
||||
undoFileActions.push({
|
||||
to: file,
|
||||
from: newFsPath,
|
||||
});
|
||||
|
||||
const oldSumPath = join(dirname(file), 'go.sum');
|
||||
const newSumPath = join(dirname(newFsPath), 'go.sum');
|
||||
if (await pathExists(oldSumPath)) {
|
||||
debug(`Moving api/go.sum to root: ${oldSumPath} to ${newSumPath}`);
|
||||
await move(oldSumPath, newSumPath);
|
||||
undoFileActions.push({
|
||||
to: oldSumPath,
|
||||
from: newSumPath,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const input = entrypointDirname;
|
||||
const includedFiles: Files = {};
|
||||
const input = entrypointDirname;
|
||||
const includedFiles: Files = {};
|
||||
|
||||
if (config && config.includeFiles) {
|
||||
const patterns = Array.isArray(config.includeFiles)
|
||||
? config.includeFiles
|
||||
: [config.includeFiles];
|
||||
for (const pattern of patterns) {
|
||||
const fsFiles = await glob(pattern, input);
|
||||
for (const [assetName, asset] of Object.entries(fsFiles)) {
|
||||
includedFiles[assetName] = asset;
|
||||
if (config && config.includeFiles) {
|
||||
const patterns = Array.isArray(config.includeFiles)
|
||||
? config.includeFiles
|
||||
: [config.includeFiles];
|
||||
for (const pattern of patterns) {
|
||||
const fsFiles = await glob(pattern, input);
|
||||
for (const [assetName, asset] of Object.entries(fsFiles)) {
|
||||
includedFiles[assetName] = asset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handlerFunctionName = parsedAnalyzed.functionName;
|
||||
debug(`Found exported function "${handlerFunctionName}" in "${entrypoint}"`);
|
||||
|
||||
if (!isGoModExist && 'vendor' in downloadedFiles) {
|
||||
throw new Error('`go.mod` is required to use a `vendor` directory.');
|
||||
}
|
||||
|
||||
// check if package name other than main
|
||||
// using `go.mod` way building the handler
|
||||
const packageName = parsedAnalyzed.packageName;
|
||||
|
||||
if (isGoModExist && packageName === 'main') {
|
||||
throw new Error('Please change `package main` to `package handler`');
|
||||
}
|
||||
|
||||
if (packageName !== 'main') {
|
||||
const go = await createGo(
|
||||
workPath,
|
||||
goPath,
|
||||
process.platform,
|
||||
process.arch,
|
||||
{
|
||||
cwd: entrypointDirname,
|
||||
},
|
||||
true
|
||||
const handlerFunctionName = parsedAnalyzed.functionName;
|
||||
debug(
|
||||
`Found exported function "${handlerFunctionName}" in "${entrypoint}"`
|
||||
);
|
||||
if (!isGoModExist) {
|
||||
try {
|
||||
const defaultGoModContent = `module ${packageName}`;
|
||||
|
||||
await writeFile(join(entrypointDirname, 'go.mod'), defaultGoModContent);
|
||||
if (!isGoModExist) {
|
||||
if (await pathExists(join(workPath, 'vendor'))) {
|
||||
throw new Error('`go.mod` is required to use a `vendor` directory.');
|
||||
}
|
||||
}
|
||||
|
||||
// check if package name other than main
|
||||
// using `go.mod` way building the handler
|
||||
const packageName = parsedAnalyzed.packageName;
|
||||
|
||||
if (isGoModExist && packageName === 'main') {
|
||||
throw new Error('Please change `package main` to `package handler`');
|
||||
}
|
||||
|
||||
const outDir = await getWriteableDirectory();
|
||||
|
||||
if (packageName !== 'main') {
|
||||
const go = await createGo(
|
||||
workPath,
|
||||
goPath,
|
||||
process.platform,
|
||||
process.arch,
|
||||
{
|
||||
cwd: entrypointDirname,
|
||||
},
|
||||
true
|
||||
);
|
||||
if (!isGoModExist) {
|
||||
try {
|
||||
const defaultGoModContent = `module ${packageName}`;
|
||||
|
||||
await writeFile(
|
||||
join(entrypointDirname, 'go.mod'),
|
||||
defaultGoModContent
|
||||
);
|
||||
|
||||
undoFileActions.push({
|
||||
to: undefined, // delete
|
||||
from: join(entrypointDirname, 'go.mod'),
|
||||
});
|
||||
|
||||
// remove the `go.sum` file that will be generated as well
|
||||
undoFileActions.push({
|
||||
to: undefined, // delete
|
||||
from: join(entrypointDirname, 'go.sum'),
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(`Failed to create default go.mod for ${packageName}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
const mainModGoFileName = 'main__mod__.go';
|
||||
const modMainGoContents = await readFile(
|
||||
join(__dirname, mainModGoFileName),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
let goPackageName = `${packageName}/${packageName}`;
|
||||
const goFuncName = `${packageName}.${handlerFunctionName}`;
|
||||
|
||||
if (isGoModExist) {
|
||||
const goModContents = await readFile(join(goModPath, 'go.mod'), 'utf8');
|
||||
const usrModName = goModContents.split('\n')[0].split(' ')[1];
|
||||
if (entrypointArr.length > 1 && isGoModInRootDir) {
|
||||
const cleanPackagePath = [...entrypointArr];
|
||||
cleanPackagePath.pop();
|
||||
goPackageName = `${usrModName}/${cleanPackagePath.join('/')}`;
|
||||
} else {
|
||||
goPackageName = `${usrModName}/${packageName}`;
|
||||
}
|
||||
}
|
||||
|
||||
const mainModGoContents = modMainGoContents
|
||||
.replace('__VC_HANDLER_PACKAGE_NAME', goPackageName)
|
||||
.replace('__VC_HANDLER_FUNC_NAME', goFuncName);
|
||||
|
||||
if (isGoModExist && isGoModInRootDir) {
|
||||
debug('[mod-root] Write main file to ' + downloadPath);
|
||||
await writeFile(
|
||||
join(downloadPath, mainModGoFileName),
|
||||
mainModGoContents
|
||||
);
|
||||
undoFileActions.push({
|
||||
to: undefined, // delete
|
||||
from: join(downloadPath, mainModGoFileName),
|
||||
});
|
||||
} else if (isGoModExist && !isGoModInRootDir) {
|
||||
debug('[mod-other] Write main file to ' + goModPath);
|
||||
await writeFile(join(goModPath, mainModGoFileName), mainModGoContents);
|
||||
undoFileActions.push({
|
||||
to: undefined, // delete
|
||||
from: join(goModPath, mainModGoFileName),
|
||||
});
|
||||
} else {
|
||||
debug('[entrypoint] Write main file to ' + entrypointDirname);
|
||||
await writeFile(
|
||||
join(entrypointDirname, mainModGoFileName),
|
||||
mainModGoContents
|
||||
);
|
||||
undoFileActions.push({
|
||||
to: undefined, // delete
|
||||
from: join(entrypointDirname, mainModGoFileName),
|
||||
});
|
||||
}
|
||||
|
||||
// move user go file to folder
|
||||
try {
|
||||
// default path
|
||||
let finalDestination = join(entrypointDirname, packageName, entrypoint);
|
||||
|
||||
// if `entrypoint` include folder, only use filename
|
||||
if (entrypointArr.length > 1) {
|
||||
finalDestination = join(
|
||||
entrypointDirname,
|
||||
packageName,
|
||||
entrypointArr[entrypointArr.length - 1]
|
||||
);
|
||||
}
|
||||
|
||||
if (dirname(entrypointAbsolute) === goModPath || !isGoModExist) {
|
||||
debug(
|
||||
`moving entrypoint "${entrypointAbsolute}" to "${finalDestination}"`
|
||||
);
|
||||
|
||||
await move(entrypointAbsolute, finalDestination);
|
||||
undoFileActions.push({
|
||||
to: entrypointAbsolute,
|
||||
from: finalDestination,
|
||||
});
|
||||
undoDirectoryCreation.push(dirname(finalDestination));
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(`Failed to create default go.mod for ${packageName}`);
|
||||
console.log('Failed to move entry to package folder');
|
||||
throw err;
|
||||
}
|
||||
|
||||
let baseGoModPath = '';
|
||||
if (isGoModExist && isGoModInRootDir) {
|
||||
baseGoModPath = downloadPath;
|
||||
} else if (isGoModExist && !isGoModInRootDir) {
|
||||
baseGoModPath = goModPath;
|
||||
} else {
|
||||
baseGoModPath = entrypointDirname;
|
||||
}
|
||||
|
||||
debug('Tidy `go.mod` file...');
|
||||
try {
|
||||
// ensure go.mod up-to-date
|
||||
await go.mod();
|
||||
} catch (err) {
|
||||
console.log('failed to `go mod tidy`');
|
||||
throw err;
|
||||
}
|
||||
|
||||
debug('Running `go build`...');
|
||||
const destPath = join(outDir, handlerFileName);
|
||||
|
||||
try {
|
||||
const src = [join(baseGoModPath, mainModGoFileName)];
|
||||
|
||||
await go.build(src, destPath);
|
||||
} catch (err) {
|
||||
console.log('failed to `go build`');
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
// legacy mode
|
||||
// we need `main.go` in the same dir as the entrypoint,
|
||||
// otherwise `go build` will refuse to build
|
||||
const go = await createGo(
|
||||
workPath,
|
||||
goPath,
|
||||
process.platform,
|
||||
process.arch,
|
||||
{
|
||||
cwd: entrypointDirname,
|
||||
},
|
||||
false
|
||||
);
|
||||
const origianlMainGoContents = await readFile(
|
||||
join(__dirname, 'main.go'),
|
||||
'utf8'
|
||||
);
|
||||
const mainGoContents = origianlMainGoContents.replace(
|
||||
'__VC_HANDLER_FUNC_NAME',
|
||||
handlerFunctionName
|
||||
);
|
||||
|
||||
// in order to allow the user to have `main.go`,
|
||||
// we need our `main.go` to be called something else
|
||||
const mainGoFileName = 'main__vc__go__.go';
|
||||
|
||||
// Go doesn't like to build files in different directories,
|
||||
// so now we place `main.go` together with the user code
|
||||
await writeFile(join(entrypointDirname, mainGoFileName), mainGoContents);
|
||||
undoFileActions.push({
|
||||
to: undefined, // delete
|
||||
from: join(entrypointDirname, mainGoFileName),
|
||||
});
|
||||
|
||||
// `go get` will look at `*.go` (note we set `cwd`), parse the `import`s
|
||||
// and download any packages that aren't part of the stdlib
|
||||
debug('Running `go get`...');
|
||||
try {
|
||||
await go.get();
|
||||
} catch (err) {
|
||||
console.log('Failed to `go get`');
|
||||
throw err;
|
||||
}
|
||||
|
||||
debug('Running `go build`...');
|
||||
const destPath = join(outDir, handlerFileName);
|
||||
try {
|
||||
const src = [
|
||||
join(entrypointDirname, mainGoFileName),
|
||||
entrypointAbsolute,
|
||||
].map(file => normalize(file));
|
||||
await go.build(src, destPath);
|
||||
} catch (err) {
|
||||
console.log('failed to `go build`');
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
const mainModGoFileName = 'main__mod__.go';
|
||||
const modMainGoContents = await readFile(
|
||||
join(__dirname, mainModGoFileName),
|
||||
'utf8'
|
||||
);
|
||||
const lambda = await createLambda({
|
||||
files: { ...(await glob('**', outDir)), ...includedFiles },
|
||||
handler: handlerFileName,
|
||||
runtime: 'go1.x',
|
||||
environment: {},
|
||||
});
|
||||
|
||||
let goPackageName = `${packageName}/${packageName}`;
|
||||
const goFuncName = `${packageName}.${handlerFunctionName}`;
|
||||
|
||||
if (isGoModExist) {
|
||||
const goModContents = await readFile(join(goModPath, 'go.mod'), 'utf8');
|
||||
const usrModName = goModContents.split('\n')[0].split(' ')[1];
|
||||
if (entrypointArr.length > 1 && isGoModInRootDir) {
|
||||
const cleanPackagePath = [...entrypointArr];
|
||||
cleanPackagePath.pop();
|
||||
goPackageName = `${usrModName}/${cleanPackagePath.join('/')}`;
|
||||
} else {
|
||||
goPackageName = `${usrModName}/${packageName}`;
|
||||
}
|
||||
const watch = parsedAnalyzed.watch;
|
||||
let watchSub: string[] = [];
|
||||
// if `entrypoint` located in subdirectory
|
||||
// we will need to concat it with return watch array
|
||||
if (entrypointArr.length > 1) {
|
||||
entrypointArr.pop();
|
||||
watchSub = parsedAnalyzed.watch.map(file => join(...entrypointArr, file));
|
||||
}
|
||||
|
||||
const mainModGoContents = modMainGoContents
|
||||
.replace('__VC_HANDLER_PACKAGE_NAME', goPackageName)
|
||||
.replace('__VC_HANDLER_FUNC_NAME', goFuncName);
|
||||
return {
|
||||
output: lambda,
|
||||
watch: watch.concat(watchSub),
|
||||
};
|
||||
} catch (error) {
|
||||
debug('Go Builder Error: ' + error);
|
||||
|
||||
if (isGoModExist && isGoModInRootDir) {
|
||||
debug('[mod-root] Write main file to ' + downloadPath);
|
||||
await writeFile(join(downloadPath, mainModGoFileName), mainModGoContents);
|
||||
} else if (isGoModExist && !isGoModInRootDir) {
|
||||
debug('[mod-other] Write main file to ' + goModPath);
|
||||
await writeFile(join(goModPath, mainModGoFileName), mainModGoContents);
|
||||
throw error;
|
||||
} finally {
|
||||
try {
|
||||
await cleanupFileSystem(undoFileActions, undoDirectoryCreation);
|
||||
} catch (error) {
|
||||
console.log(`Build cleanup failed: ${error.message}`);
|
||||
debug('Cleanup Error: ' + error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function cleanupFileSystem(
|
||||
undoFileActions: UndoFileAction[],
|
||||
undoDirectoryCreation: string[]
|
||||
) {
|
||||
// we have to undo the actions in reverse order in cases
|
||||
// where one file was moved multiple times, which happens
|
||||
// using files that start with brackets
|
||||
for (const action of undoFileActions.reverse()) {
|
||||
if (action.to) {
|
||||
await move(action.from, action.to);
|
||||
} else {
|
||||
debug('[entrypoint] Write main file to ' + entrypointDirname);
|
||||
await writeFile(
|
||||
join(entrypointDirname, mainModGoFileName),
|
||||
mainModGoContents
|
||||
);
|
||||
}
|
||||
|
||||
// move user go file to folder
|
||||
try {
|
||||
// default path
|
||||
let finalDestination = join(entrypointDirname, packageName, entrypoint);
|
||||
|
||||
// if `entrypoint` include folder, only use filename
|
||||
if (entrypointArr.length > 1) {
|
||||
finalDestination = join(
|
||||
entrypointDirname,
|
||||
packageName,
|
||||
entrypointArr[entrypointArr.length - 1]
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
dirname(downloadedFiles[entrypoint].fsPath) === goModPath ||
|
||||
!isGoModExist
|
||||
) {
|
||||
await move(downloadedFiles[entrypoint].fsPath, finalDestination);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Failed to move entry to package folder');
|
||||
throw err;
|
||||
}
|
||||
|
||||
let baseGoModPath = '';
|
||||
if (isGoModExist && isGoModInRootDir) {
|
||||
baseGoModPath = downloadPath;
|
||||
} else if (isGoModExist && !isGoModInRootDir) {
|
||||
baseGoModPath = goModPath;
|
||||
} else {
|
||||
baseGoModPath = entrypointDirname;
|
||||
}
|
||||
|
||||
debug('Tidy `go.mod` file...');
|
||||
try {
|
||||
// ensure go.mod up-to-date
|
||||
await go.mod();
|
||||
} catch (err) {
|
||||
console.log('failed to `go mod tidy`');
|
||||
throw err;
|
||||
}
|
||||
|
||||
debug('Running `go build`...');
|
||||
const destPath = join(outDir, handlerFileName);
|
||||
|
||||
try {
|
||||
const src = [join(baseGoModPath, mainModGoFileName)];
|
||||
|
||||
await go.build(src, destPath);
|
||||
} catch (err) {
|
||||
console.log('failed to `go build`');
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
// legacy mode
|
||||
// we need `main.go` in the same dir as the entrypoint,
|
||||
// otherwise `go build` will refuse to build
|
||||
const go = await createGo(
|
||||
workPath,
|
||||
goPath,
|
||||
process.platform,
|
||||
process.arch,
|
||||
{
|
||||
cwd: entrypointDirname,
|
||||
},
|
||||
false
|
||||
);
|
||||
const origianlMainGoContents = await readFile(
|
||||
join(__dirname, 'main.go'),
|
||||
'utf8'
|
||||
);
|
||||
const mainGoContents = origianlMainGoContents.replace(
|
||||
'__VC_HANDLER_FUNC_NAME',
|
||||
handlerFunctionName
|
||||
);
|
||||
|
||||
// in order to allow the user to have `main.go`,
|
||||
// we need our `main.go` to be called something else
|
||||
const mainGoFileName = 'main__vc__go__.go';
|
||||
|
||||
// Go doesn't like to build files in different directories,
|
||||
// so now we place `main.go` together with the user code
|
||||
await writeFile(join(entrypointDirname, mainGoFileName), mainGoContents);
|
||||
|
||||
// `go get` will look at `*.go` (note we set `cwd`), parse the `import`s
|
||||
// and download any packages that aren't part of the stdlib
|
||||
debug('Running `go get`...');
|
||||
try {
|
||||
await go.get();
|
||||
} catch (err) {
|
||||
console.log('Failed to `go get`');
|
||||
throw err;
|
||||
}
|
||||
|
||||
debug('Running `go build`...');
|
||||
const destPath = join(outDir, handlerFileName);
|
||||
try {
|
||||
const src = [
|
||||
join(entrypointDirname, mainGoFileName),
|
||||
downloadedFiles[entrypoint].fsPath,
|
||||
].map(file => normalize(file));
|
||||
await go.build(src, destPath);
|
||||
} catch (err) {
|
||||
console.log('failed to `go build`');
|
||||
throw err;
|
||||
await remove(action.from);
|
||||
}
|
||||
}
|
||||
|
||||
const lambda = await createLambda({
|
||||
files: { ...(await glob('**', outDir)), ...includedFiles },
|
||||
handler: handlerFileName,
|
||||
runtime: 'go1.x',
|
||||
environment: {},
|
||||
const undoDirectoryPromises = undoDirectoryCreation.map(async directory => {
|
||||
const contents = await readdir(directory);
|
||||
// only delete an empty directory
|
||||
// if it has contents, either something went wrong during cleanup or this
|
||||
// directory contains project source code that should not be deleted
|
||||
if (!contents.length) {
|
||||
return rmdir(directory);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
await Promise.all(undoDirectoryPromises);
|
||||
}
|
||||
|
||||
const watch = parsedAnalyzed.watch;
|
||||
let watchSub: string[] = [];
|
||||
// if `entrypoint` located in subdirectory
|
||||
// we will need to concat it with return watch array
|
||||
if (entrypointArr.length > 1) {
|
||||
entrypointArr.pop();
|
||||
watchSub = parsedAnalyzed.watch.map(file => join(...entrypointArr, file));
|
||||
async function findGoModPath(workPath: string): Promise<string> {
|
||||
let checkPath = join(workPath, 'go.mod');
|
||||
if (await pathExists(checkPath)) {
|
||||
return checkPath;
|
||||
}
|
||||
|
||||
return {
|
||||
output: lambda,
|
||||
watch: watch.concat(watchSub),
|
||||
};
|
||||
checkPath = join(workPath, 'api/go.mod');
|
||||
if (await pathExists(checkPath)) {
|
||||
return checkPath;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function isPortInfo(v: any): v is PortInfo {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/go",
|
||||
"version": "2.0.12",
|
||||
"version": "2.0.14",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
|
||||
@@ -11,9 +11,8 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node build",
|
||||
"test": "yarn jest --env node --verbose --runInBand --bail",
|
||||
"test-integration-once": "yarn test",
|
||||
"prepublishOnly": "node build"
|
||||
"test": "jest --env node --verbose --runInBand --bail",
|
||||
"test-integration-once": "yarn test"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
@@ -25,7 +24,7 @@
|
||||
"@types/fs-extra": "^5.0.5",
|
||||
"@types/node-fetch": "^2.3.0",
|
||||
"@types/tar": "^4.0.0",
|
||||
"@vercel/build-utils": "5.0.8",
|
||||
"@vercel/build-utils": "5.1.1",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"async-retry": "1.3.1",
|
||||
"execa": "^1.0.0",
|
||||
|
||||
@@ -6,5 +6,5 @@ import (
|
||||
)
|
||||
|
||||
func Handler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "hello:RANDOMNESS_PLACEHOLDER")
|
||||
fmt.Fprintf(w, "Req Path: %s", r.URL.Path)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "api/**/*.go", "use": "@vercel/go" }],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/api/[hello].go",
|
||||
"mustContain": "hello:RANDOMNESS_PLACEHOLDER"
|
||||
"mustContain": "Req Path: /api/[hello].go"
|
||||
},
|
||||
{
|
||||
"path": "/api/whatever",
|
||||
"mustContain": "Req Path: /api/whatever"
|
||||
},
|
||||
{ "path": "/api/sub/[hi].go", "mustContain": "hi:RANDOMNESS_PLACEHOLDER" }
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/hydrogen",
|
||||
"version": "0.0.9",
|
||||
"version": "0.0.11",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
@@ -12,8 +12,7 @@
|
||||
"scripts": {
|
||||
"build": "node build.js",
|
||||
"test-integration-once": "yarn test test/test.js",
|
||||
"test": "jest --env node --verbose --bail --runInBand",
|
||||
"prepublishOnly": "node build.js"
|
||||
"test": "jest --env node --verbose --bail --runInBand"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -22,7 +21,7 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "27.5.1",
|
||||
"@types/node": "*",
|
||||
"@vercel/build-utils": "5.0.8",
|
||||
"@vercel/build-utils": "5.1.1",
|
||||
"typescript": "4.6.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "3.1.12",
|
||||
"version": "3.1.14",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
|
||||
@@ -11,8 +11,7 @@
|
||||
"test-unit": "yarn test test/build.test.ts test/unit/",
|
||||
"test-next-local": "jest --env node --verbose --bail --forceExit --testTimeout=360000 test/integration/*.test.js test/integration/*.test.ts",
|
||||
"test-next-local:middleware": "jest --env node --verbose --bail --useStderr --testTimeout=360000 test/integration/middleware.test.ts",
|
||||
"test-integration-once": "rm test/builder-info.json; jest --env node --verbose --runInBand --bail test/fixtures/**/*.test.js",
|
||||
"prepublishOnly": "yarn build"
|
||||
"test-integration-once": "rm test/builder-info.json; jest --env node --verbose --runInBand --bail test/fixtures/**/*.test.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -45,9 +44,9 @@
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/text-table": "0.2.1",
|
||||
"@types/webpack-sources": "3.2.0",
|
||||
"@vercel/build-utils": "5.0.8",
|
||||
"@vercel/build-utils": "5.1.1",
|
||||
"@vercel/nft": "0.21.0",
|
||||
"@vercel/routing-utils": "2.0.0",
|
||||
"@vercel/routing-utils": "2.0.1",
|
||||
"async-sema": "3.0.1",
|
||||
"buffer-crc32": "0.2.13",
|
||||
"cheerio": "1.0.0-rc.10",
|
||||
|
||||
@@ -151,6 +151,7 @@ export async function serverBuild({
|
||||
nextVersion,
|
||||
CORRECTED_MANIFESTS_VERSION
|
||||
);
|
||||
|
||||
let hasStatic500 = !!staticPages[path.join(entryDirectory, '500')];
|
||||
|
||||
if (lambdaPageKeys.length === 0) {
|
||||
@@ -1209,10 +1210,6 @@ export async function serverBuild({
|
||||
]
|
||||
: []),
|
||||
|
||||
// ensure prerender's for notFound: true static routes
|
||||
// have 404 status code when not in preview mode
|
||||
...notFoundPreviewRoutes,
|
||||
|
||||
...headers,
|
||||
|
||||
...redirects,
|
||||
@@ -1223,6 +1220,10 @@ export async function serverBuild({
|
||||
|
||||
...beforeFilesRewrites,
|
||||
|
||||
// ensure prerender's for notFound: true static routes
|
||||
// have 404 status code when not in preview mode
|
||||
...notFoundPreviewRoutes,
|
||||
|
||||
// Make sure to 404 for the /404 path itself
|
||||
...(i18n
|
||||
? [
|
||||
@@ -1328,7 +1329,8 @@ export async function serverBuild({
|
||||
dest: '$0',
|
||||
},
|
||||
|
||||
// remove locale prefixes to check public files
|
||||
// remove locale prefixes to check public files and
|
||||
// to allow checking non-prefixed lambda outputs
|
||||
...(i18n
|
||||
? [
|
||||
{
|
||||
@@ -1341,20 +1343,6 @@ export async function serverBuild({
|
||||
]
|
||||
: []),
|
||||
|
||||
// for non-shared lambdas remove locale prefix if present
|
||||
// to allow checking for lambda
|
||||
...(!i18n
|
||||
? []
|
||||
: [
|
||||
{
|
||||
src: `${path.join('/', entryDirectory, '/')}(?:${i18n?.locales
|
||||
.map(locale => escapeStringRegexp(locale))
|
||||
.join('|')})/(.*)`,
|
||||
dest: '/$1',
|
||||
check: true,
|
||||
},
|
||||
]),
|
||||
|
||||
// routes that are called after each rewrite or after routes
|
||||
// if there no rewrites
|
||||
{ handle: 'rewrite' },
|
||||
@@ -1362,8 +1350,54 @@ export async function serverBuild({
|
||||
// re-build /_next/data URL after resolving
|
||||
...denormalizeNextDataRoute(),
|
||||
|
||||
...(isNextDataServerResolving
|
||||
? dataRoutes.filter(route => {
|
||||
// filter to only static data routes as dynamic routes will be handled
|
||||
// below
|
||||
const { pathname } = new URL(route.dest || '/', 'http://n');
|
||||
return !isDynamicRoute(pathname.replace(/\.json$/, ''));
|
||||
})
|
||||
: []),
|
||||
|
||||
// /_next/data routes for getServerProps/getStaticProps pages
|
||||
...dataRoutes,
|
||||
...(isNextDataServerResolving
|
||||
? // when resolving data routes for middleware we need to include
|
||||
// all dynamic routes including non-SSG/SSP so that the priority
|
||||
// is correct
|
||||
dynamicRoutes
|
||||
.map(route => {
|
||||
route = Object.assign({}, route);
|
||||
route.src = path.posix.join(
|
||||
'^/',
|
||||
entryDirectory,
|
||||
'_next/data/',
|
||||
escapedBuildId,
|
||||
route.src.replace(/(^\^|\$$)/g, '') + '.json$'
|
||||
);
|
||||
|
||||
const { pathname } = new URL(route.dest || '/', 'http://n');
|
||||
let isPrerender = !!prerenders[path.join('./', pathname)];
|
||||
|
||||
if (routesManifest.i18n) {
|
||||
for (const locale of routesManifest.i18n?.locales || []) {
|
||||
const prerenderPathname = pathname.replace(
|
||||
/^\/\$nextLocale/,
|
||||
`/${locale}`
|
||||
);
|
||||
if (prerenders[path.join('./', prerenderPathname)]) {
|
||||
isPrerender = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isPrerender) {
|
||||
route.dest = `/_next/data/${buildId}${pathname}.json`;
|
||||
}
|
||||
return route;
|
||||
})
|
||||
.filter(Boolean)
|
||||
: dataRoutes),
|
||||
|
||||
...(!isNextDataServerResolving
|
||||
? [
|
||||
@@ -1395,6 +1429,7 @@ export async function serverBuild({
|
||||
'x-nextjs-matched-path': '/$1',
|
||||
},
|
||||
continue: true,
|
||||
override: true,
|
||||
},
|
||||
// add a catch-all data route so we don't 404 when getting
|
||||
// middleware effects
|
||||
|
||||
8
packages/next/test/fixtures/00-middleware-i18n/index.test.js
vendored
Normal file
8
packages/next/test/fixtures/00-middleware-i18n/index.test.js
vendored
Normal 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);
|
||||
});
|
||||
});
|
||||
248
packages/next/test/fixtures/00-middleware-i18n/middleware.js
vendored
Normal file
248
packages/next/test/fixtures/00-middleware-i18n/middleware.js
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
const ALLOWED = ['allowed'];
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
'/dynamic/:path*',
|
||||
'/_sites/:path*',
|
||||
'/:teamId/:slug',
|
||||
'/:path*',
|
||||
'/',
|
||||
],
|
||||
};
|
||||
|
||||
export function middleware(request) {
|
||||
const url = request.nextUrl;
|
||||
const pathname = url.pathname;
|
||||
|
||||
if (process.env.FOO) {
|
||||
console.log(`Includes env variable ${process.env.FOO}`);
|
||||
}
|
||||
|
||||
if (url.pathname === '/redirect-me') {
|
||||
url.pathname = '/from-middleware';
|
||||
return NextResponse.redirect(url, 307);
|
||||
}
|
||||
|
||||
if (url.pathname === '/next') {
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
if (url.pathname === '/version') {
|
||||
return NextResponse.json({
|
||||
enumerable: Object.keys(self).includes('VercelRuntime'),
|
||||
version: self.VercelRuntime.version,
|
||||
});
|
||||
}
|
||||
|
||||
if (url.pathname === '/globals') {
|
||||
const globalThisKeys = Object.keys(globalThis);
|
||||
const globalKeys = globalThisKeys.reduce((acc, globalName) => {
|
||||
const key = globalName.toString();
|
||||
if (global[key]) acc.push(key);
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const res = NextResponse.next();
|
||||
res.headers.set(
|
||||
'data',
|
||||
JSON.stringify({ globals: globalKeys, globalThis: globalThisKeys })
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (url.pathname === '/log') {
|
||||
console.log('hi there');
|
||||
return;
|
||||
}
|
||||
|
||||
if (url.pathname === '/somewhere') {
|
||||
url.pathname = '/from-middleware';
|
||||
return NextResponse.redirect(url);
|
||||
}
|
||||
|
||||
if (url.pathname === '/logs') {
|
||||
console.clear();
|
||||
for (let i = 0; i < 3; i++) console.count();
|
||||
console.count('test');
|
||||
console.count('test');
|
||||
console.dir({ hello: 'world' });
|
||||
console.log('hello');
|
||||
console.log('world');
|
||||
return;
|
||||
}
|
||||
|
||||
if (url.pathname === '/greetings') {
|
||||
const data = { message: 'hello world!' };
|
||||
const res = NextResponse.next();
|
||||
res.headers.set('x-example', 'edge');
|
||||
res.headers.set('data', JSON.stringify(data));
|
||||
return res;
|
||||
}
|
||||
|
||||
if (url.pathname === '/rewrite-me-to-about') {
|
||||
url.pathname = '/about';
|
||||
url.searchParams.set('middleware', 'foo');
|
||||
return NextResponse.rewrite(url);
|
||||
}
|
||||
|
||||
if (url.pathname === '/rewrite-to-site') {
|
||||
const customUrl = new URL(url);
|
||||
customUrl.pathname = '/_sites/subdomain-1/';
|
||||
console.log('rewriting to', customUrl.pathname, customUrl.href);
|
||||
return NextResponse.rewrite(customUrl);
|
||||
}
|
||||
|
||||
if (url.pathname === '/redirect-me-to-about') {
|
||||
url.pathname = '/about';
|
||||
url.searchParams.set('middleware', 'foo');
|
||||
return Response.redirect(url);
|
||||
}
|
||||
|
||||
if (url.pathname === '/rewrite-absolute') {
|
||||
return NextResponse.rewrite('https://example.vercel.sh/foo?foo=bar');
|
||||
}
|
||||
|
||||
if (url.pathname === '/rewrite-relative') {
|
||||
url.pathname = '/foo';
|
||||
url.searchParams.set('foo', 'bar');
|
||||
return NextResponse.rewrite(url);
|
||||
}
|
||||
|
||||
if (url.pathname === '/redirect-absolute') {
|
||||
return Response.redirect('https://vercel.com');
|
||||
}
|
||||
|
||||
if (url.pathname === '/redirect-301') {
|
||||
url.pathname = '/greetings';
|
||||
return NextResponse.redirect(url, 301);
|
||||
}
|
||||
|
||||
if (url.pathname === '/reflect') {
|
||||
const res = NextResponse.next();
|
||||
res.headers.set(
|
||||
'data',
|
||||
JSON.stringify({
|
||||
geo: request.geo,
|
||||
headers: Object.fromEntries(request.headers),
|
||||
ip: request.ip,
|
||||
method: request.method,
|
||||
nextUrl: {
|
||||
hash: request.nextUrl.hash,
|
||||
hostname: request.nextUrl.hostname,
|
||||
pathname: request.nextUrl.pathname,
|
||||
port: request.nextUrl.port,
|
||||
protocol: request.nextUrl.protocol,
|
||||
search: request.nextUrl.search,
|
||||
},
|
||||
url: request.url,
|
||||
})
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (url.pathname === '/stream-response') {
|
||||
const { readable, writable } = new TransformStream();
|
||||
const waitUntil = (async () => {
|
||||
const enc = new TextEncoder();
|
||||
const writer = writable.getWriter();
|
||||
writer.write(enc.encode('this is a streamed '));
|
||||
writer.write(enc.encode('response '));
|
||||
return writer.close();
|
||||
})();
|
||||
|
||||
return {
|
||||
waitUntil,
|
||||
response: NextResponse.next(),
|
||||
};
|
||||
}
|
||||
|
||||
if (url.pathname === '/throw-error') {
|
||||
const error = new Error('oh no!');
|
||||
console.log('This is not worker.js');
|
||||
console.error(error);
|
||||
return new Promise((_, reject) => reject(error));
|
||||
}
|
||||
|
||||
if (url.pathname === '/throw-error-internal') {
|
||||
function myFunctionName() {
|
||||
throw new Error('Oh no!');
|
||||
}
|
||||
|
||||
function anotherFunction() {
|
||||
return myFunctionName();
|
||||
}
|
||||
|
||||
try {
|
||||
anotherFunction();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
return new Promise((_, reject) => reject(new Error('oh no!')));
|
||||
}
|
||||
|
||||
if (url.pathname === '/unhandledrejection') {
|
||||
Promise.reject(new TypeError('captured unhandledrejection error.'));
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/query-params')) {
|
||||
if (pathname.endsWith('/clear')) {
|
||||
const strategy =
|
||||
url.searchParams.get('strategy') === 'rewrite' ? 'rewrite' : 'redirect';
|
||||
|
||||
for (const key of [...url.searchParams.keys()]) {
|
||||
if (!ALLOWED.includes(key)) {
|
||||
url.searchParams.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
const newPath = url.pathname.replace(/\/clear$/, '');
|
||||
url.pathname = newPath;
|
||||
|
||||
if (strategy === 'redirect') {
|
||||
return NextResponse.redirect(url);
|
||||
} else {
|
||||
return NextResponse.rewrite(url);
|
||||
}
|
||||
}
|
||||
|
||||
const obj = Object.fromEntries([...url.searchParams.entries()]);
|
||||
|
||||
const res = NextResponse.next();
|
||||
res.headers.set('data', JSON.stringify(obj));
|
||||
return res;
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/home')) {
|
||||
if (!request.cookies.get('bucket')) {
|
||||
const bucket = Math.random() >= 0.5 ? 'a' : 'b';
|
||||
url.pathname = `/home/${bucket}`;
|
||||
const response = NextResponse.rewrite(url);
|
||||
response.cookies.set('bucket', bucket);
|
||||
return response;
|
||||
}
|
||||
|
||||
url.pathname = `/home/${request.cookies.get('bucket')}`;
|
||||
return NextResponse.rewrite(url);
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/fetch-subrequest')) {
|
||||
const destinationUrl =
|
||||
url.searchParams.get('url') || 'https://example.vercel.sh';
|
||||
return fetch(destinationUrl, { headers: request.headers });
|
||||
}
|
||||
|
||||
if (url.pathname === '/dynamic/greet') {
|
||||
const res = NextResponse.next();
|
||||
res.headers.set(
|
||||
'data',
|
||||
JSON.stringify({
|
||||
message: url.searchParams.get('greeting') || 'Hi friend',
|
||||
})
|
||||
);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
42
packages/next/test/fixtures/00-middleware-i18n/next.config.js
vendored
Normal file
42
packages/next/test/fixtures/00-middleware-i18n/next.config.js
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
module.exports = {
|
||||
generateBuildId() {
|
||||
return 'testing-build-id';
|
||||
},
|
||||
i18n: {
|
||||
locales: ['en', 'fr'],
|
||||
defaultLocale: 'en',
|
||||
},
|
||||
redirects() {
|
||||
return [
|
||||
{
|
||||
source: '/redirect-me',
|
||||
destination: '/from-next-config',
|
||||
permanent: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
rewrites() {
|
||||
return {
|
||||
beforeFiles: [
|
||||
{
|
||||
source: '/rewrite-before-files',
|
||||
destination: '/somewhere',
|
||||
},
|
||||
],
|
||||
afterFiles: [
|
||||
{
|
||||
source: '/after-file-rewrite',
|
||||
destination: '/about',
|
||||
},
|
||||
{
|
||||
source: '/after-file-rewrite-auto-static',
|
||||
destination: '/home/a',
|
||||
},
|
||||
{
|
||||
source: '/after-file-rewrite-auto-static-dynamic',
|
||||
destination: '/dynamic/first',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
11
packages/next/test/fixtures/00-middleware-i18n/package.json
vendored
Normal file
11
packages/next/test/fixtures/00-middleware-i18n/package.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "canary",
|
||||
"react": "latest",
|
||||
"react-dom": "latest"
|
||||
}
|
||||
}
|
||||
3
packages/next/test/fixtures/00-middleware-i18n/pages/[teamId]/[slug].js
vendored
Normal file
3
packages/next/test/fixtures/00-middleware-i18n/pages/[teamId]/[slug].js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Page() {
|
||||
return <p>/[teamId]/[slug]</p>;
|
||||
}
|
||||
31
packages/next/test/fixtures/00-middleware-i18n/pages/_sites/[site].js
vendored
Normal file
31
packages/next/test/fixtures/00-middleware-i18n/pages/_sites/[site].js
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
export default function Page(props) {
|
||||
return (
|
||||
<>
|
||||
<p>/_sites/[site]</p>
|
||||
<p>{JSON.stringify(props)}</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function getStaticProps({ params }) {
|
||||
return {
|
||||
props: {
|
||||
params,
|
||||
now: Date.now(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function getStaticPaths() {
|
||||
return {
|
||||
paths: [
|
||||
{
|
||||
params: { site: 'subdomain-1' },
|
||||
},
|
||||
{
|
||||
params: { site: 'subdomain-2' },
|
||||
},
|
||||
],
|
||||
fallback: 'blocking',
|
||||
};
|
||||
}
|
||||
17
packages/next/test/fixtures/00-middleware-i18n/pages/about.js
vendored
Normal file
17
packages/next/test/fixtures/00-middleware-i18n/pages/about.js
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
export default function Main({ message, middleware }) {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="title">About Page</h1>
|
||||
<p className={message}>{message}</p>
|
||||
<p className="middleware">{middleware}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = ({ query }) => ({
|
||||
props: {
|
||||
middleware: query.middleware || '',
|
||||
message: query.message || '',
|
||||
page: 'about',
|
||||
},
|
||||
});
|
||||
3
packages/next/test/fixtures/00-middleware-i18n/pages/dynamic/[id]/index.js
vendored
Normal file
3
packages/next/test/fixtures/00-middleware-i18n/pages/dynamic/[id]/index.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Index() {
|
||||
return <p className="title">Dynamic route</p>;
|
||||
}
|
||||
3
packages/next/test/fixtures/00-middleware-i18n/pages/dynamic/static.js
vendored
Normal file
3
packages/next/test/fixtures/00-middleware-i18n/pages/dynamic/static.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Index() {
|
||||
return <p className="title">static route</p>;
|
||||
}
|
||||
16
packages/next/test/fixtures/00-middleware-i18n/pages/fetch-subrequest/index.js
vendored
Normal file
16
packages/next/test/fixtures/00-middleware-i18n/pages/fetch-subrequest/index.js
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
export default function Main({ message, middleware }) {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="title">About Page</h1>
|
||||
<p className={message}>{message}</p>
|
||||
<p className="middleware">{middleware}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = ({ query }) => ({
|
||||
props: {
|
||||
middleware: query.middleware || '',
|
||||
message: query.message || '',
|
||||
},
|
||||
});
|
||||
3
packages/next/test/fixtures/00-middleware-i18n/pages/home/a.js
vendored
Normal file
3
packages/next/test/fixtures/00-middleware-i18n/pages/home/a.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Home() {
|
||||
return <h1>Home A</h1>;
|
||||
}
|
||||
3
packages/next/test/fixtures/00-middleware-i18n/pages/home/b.js
vendored
Normal file
3
packages/next/test/fixtures/00-middleware-i18n/pages/home/b.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Home() {
|
||||
return <h1>Home B</h1>;
|
||||
}
|
||||
81
packages/next/test/fixtures/00-middleware-i18n/pages/index.js
vendored
Normal file
81
packages/next/test/fixtures/00-middleware-i18n/pages/index.js
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function Index() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Demo</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<Link href="/home">
|
||||
<a>A/B Testing</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/rewrite-me-to-about">
|
||||
<a>Rewrite to existing page</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/redirect-me-to-about">
|
||||
<a>Redirect to existing page</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/rewrite">
|
||||
<a>Rewrite to external site</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/redirect">
|
||||
<a>Redirect to external site</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/greetings">
|
||||
<a>Respond with JSON</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/stream-response">
|
||||
<a>Respond with Stream</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/dynamic/greet?greeting=hola">
|
||||
<a>Dynamic Nested Middleware</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/eval">
|
||||
<a>do a eval</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/logs">
|
||||
<a>print some logs</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/fetch">
|
||||
<a>perform a fetch</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/throw-error">
|
||||
<a>throw an error</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/throw-error-internal">
|
||||
<a>throw a controller error</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/timeout">
|
||||
<a>simulate timeout</a>
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
120
packages/next/test/fixtures/00-middleware-i18n/vercel.json
vendored
Normal file
120
packages/next/test/fixtures/00-middleware-i18n/vercel.json
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next"
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/en/dynamic/static.json",
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"x-nextjs-data": 1
|
||||
},
|
||||
"responseHeaders": {
|
||||
"x-matched-path": "/en/dynamic/static"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/en/team-1/slug-1.json",
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"x-nextjs-data": 1
|
||||
},
|
||||
"responseHeaders": {
|
||||
"x-matched-path": "/en/[teamId]/[slug]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/en/dynamic/id-1.json",
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"x-nextjs-data": 1
|
||||
},
|
||||
"responseHeaders": {
|
||||
"x-matched-path": "/en/dynamic/[id]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/en/rewrite-to-site.json",
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"x-nextjs-data": 1
|
||||
},
|
||||
"mustContain": "site\":\"subdomain-1\"",
|
||||
"mustNotContain": "<html>"
|
||||
},
|
||||
{
|
||||
"path": "/redirect-me",
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"Location": "/from-next-config/"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/rewrite-before-files",
|
||||
"status": 404,
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/somewhere",
|
||||
"status": 307,
|
||||
"responseHeaders": {
|
||||
"Location": "/from-middleware/"
|
||||
},
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/after-file-rewrite",
|
||||
"status": 200,
|
||||
"mustContain": "About Page"
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/en/after-file-rewrite.json",
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"x-nextjs-data": "1"
|
||||
},
|
||||
"mustContain": "page\":\"about\""
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/en/after-file-rewrite-auto-static-dynamic.json",
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"x-nextjs-data": "1"
|
||||
},
|
||||
"responseHeaders": {
|
||||
"x-matched-path": "/en/dynamic/[id]"
|
||||
},
|
||||
"mustContain": "{}"
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/en/redirect-me.json",
|
||||
"status": 307,
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"responseHeaders": {
|
||||
"Location": "/from-middleware/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/en/_sites/subdomain-1.json",
|
||||
"status": 200,
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"mustNotContain": "<html>",
|
||||
"mustContain": "\"site\":\"subdomain-1\""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2,6 +2,16 @@ import { NextResponse } from 'next/server';
|
||||
|
||||
const ALLOWED = ['allowed'];
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
'/dynamic/:path*',
|
||||
'/_sites/:path*',
|
||||
'/:teamId/:slug',
|
||||
'/:path*',
|
||||
'/',
|
||||
],
|
||||
};
|
||||
|
||||
export function middleware(request) {
|
||||
const url = request.nextUrl;
|
||||
const pathname = url.pathname;
|
||||
@@ -12,7 +22,7 @@ export function middleware(request) {
|
||||
|
||||
if (url.pathname === '/redirect-me') {
|
||||
url.pathname = '/from-middleware';
|
||||
return NextResponse.redirect(url);
|
||||
return NextResponse.redirect(url, 307);
|
||||
}
|
||||
|
||||
if (url.pathname === '/next') {
|
||||
|
||||
3
packages/next/test/fixtures/00-middleware/pages/[teamId]/[slug].js
vendored
Normal file
3
packages/next/test/fixtures/00-middleware/pages/[teamId]/[slug].js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Page() {
|
||||
return <p>/[teamId]/[slug]</p>;
|
||||
}
|
||||
3
packages/next/test/fixtures/00-middleware/pages/dynamic/static.js
vendored
Normal file
3
packages/next/test/fixtures/00-middleware/pages/dynamic/static.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Index() {
|
||||
return <p className="title">static route</p>;
|
||||
}
|
||||
@@ -1,7 +1,42 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [{ "src": "package.json", "use": "@vercel/next" }],
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next"
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/dynamic/static.json",
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"x-nextjs-data": 1
|
||||
},
|
||||
"responseHeaders": {
|
||||
"x-matched-path": "/dynamic/static"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/team-1/slug-1.json",
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"x-nextjs-data": 1
|
||||
},
|
||||
"responseHeaders": {
|
||||
"x-matched-path": "/[teamId]/[slug]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/dynamic/id-1.json",
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"x-nextjs-data": 1
|
||||
},
|
||||
"responseHeaders": {
|
||||
"x-matched-path": "/dynamic/[id]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/rewrite-to-site.json",
|
||||
"status": 200,
|
||||
@@ -58,9 +93,28 @@
|
||||
"x-nextjs-data": "1"
|
||||
},
|
||||
"responseHeaders": {
|
||||
"x-nextjs-matched-path": "/dynamic/first"
|
||||
"x-matched-path": "/dynamic/[id]"
|
||||
},
|
||||
"mustContain": "{}"
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/redirect-me.json",
|
||||
"status": 307,
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"responseHeaders": {
|
||||
"Location": "/from-middleware/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/_next/data/testing-build-id/_sites/subdomain-1.json",
|
||||
"status": 200,
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"mustNotContain": "<html>",
|
||||
"mustContain": "\"site\":\"subdomain-1\""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,4 +5,13 @@ module.exports = {
|
||||
experimental: {
|
||||
nftTracing: true,
|
||||
},
|
||||
redirects() {
|
||||
return [
|
||||
{
|
||||
source: '/build-time-not-found/:path*',
|
||||
destination: '/somewhere-else',
|
||||
permanent: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
30
packages/next/test/fixtures/00-server-build/pages/build-time-not-found/[slug].js
vendored
Normal file
30
packages/next/test/fixtures/00-server-build/pages/build-time-not-found/[slug].js
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
export default function Page(props) {
|
||||
return (
|
||||
<>
|
||||
<p>build-time-not-found</p>
|
||||
<p>{JSON.stringify(props)}</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function getStaticPaths() {
|
||||
return {
|
||||
paths: ['/build-time-not-found/first', '/build-time-not-found/second'],
|
||||
fallback: 'blocking',
|
||||
};
|
||||
}
|
||||
|
||||
export function getStaticProps({ params }) {
|
||||
console.log('getStaticProps', params);
|
||||
if (params.slug === 'first') {
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
props: {
|
||||
params,
|
||||
now: Date.now(),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -31,6 +31,26 @@
|
||||
"status": 200,
|
||||
"mustContain": "index page"
|
||||
},
|
||||
{
|
||||
"path": "/build-time-not-found/first",
|
||||
"status": 307,
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"responseHeaders": {
|
||||
"location": "//somewhere-else/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/build-time-not-found/second",
|
||||
"status": 307,
|
||||
"fetchOptions": {
|
||||
"redirect": "manual"
|
||||
},
|
||||
"responseHeaders": {
|
||||
"location": "//somewhere-else/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "/static",
|
||||
"status": 200,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/node",
|
||||
"version": "2.5.3",
|
||||
"version": "2.5.5",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
||||
@@ -13,8 +13,7 @@
|
||||
"build": "node build",
|
||||
"test": "jest --env node --verbose --bail --runInBand",
|
||||
"test-unit": "yarn test test/prepare-cache.test.ts test/utils.test.ts",
|
||||
"test-integration-once": "yarn test test/integration-*.test.js",
|
||||
"prepublishOnly": "node build"
|
||||
"test-integration-once": "yarn test test/integration-*.test.js"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
@@ -32,9 +31,9 @@
|
||||
"dependencies": {
|
||||
"@edge-runtime/vm": "1.1.0-beta.23",
|
||||
"@types/node": "*",
|
||||
"@vercel/build-utils": "5.0.8",
|
||||
"@vercel/build-utils": "5.1.1",
|
||||
"@vercel/node-bridge": "3.0.0",
|
||||
"@vercel/static-config": "2.0.1",
|
||||
"@vercel/static-config": "2.0.2",
|
||||
"edge-runtime": "1.1.0-beta.23",
|
||||
"esbuild": "0.14.47",
|
||||
"exit-hook": "2.2.1",
|
||||
|
||||
@@ -224,7 +224,7 @@ async function compileUserCode(
|
||||
} catch (error) {
|
||||
// We can't easily show a meaningful stack trace from ncc -> edge-runtime.
|
||||
// So, stick with just the message for now.
|
||||
console.error(`Failed to instantiate edge runtime.`);
|
||||
console.error(`Failed to compile user code for edge runtime.`);
|
||||
logError(error);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -14,12 +14,14 @@ export function getRegExpFromMatchers(matcherOrMatchers: unknown): string {
|
||||
function getRegExpFromMatcher(matcher: unknown): string {
|
||||
if (typeof matcher !== 'string') {
|
||||
throw new Error(
|
||||
'`matcher` must be a path matcher or an array of path matchers'
|
||||
"Middleware's `config.matcher` must be a path matcher (string) or an array of path matchers (string[])"
|
||||
);
|
||||
}
|
||||
|
||||
if (!matcher.startsWith('/')) {
|
||||
throw new Error('`matcher`: path matcher must start with /');
|
||||
throw new Error(
|
||||
`Middleware's \`config.matcher\` values must start with "/". Received: ${matcher}`
|
||||
);
|
||||
}
|
||||
|
||||
const re = pathToRegexp(matcher);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/python",
|
||||
"version": "3.1.4",
|
||||
"version": "3.1.6",
|
||||
"main": "./dist/index.js",
|
||||
"license": "MIT",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
|
||||
@@ -17,13 +17,12 @@
|
||||
"build": "node build",
|
||||
"test": "jest --env node --verbose --runInBand --bail",
|
||||
"test-unit": "yarn test test/unit.test.ts",
|
||||
"test-integration-once": "yarn test test/integration.test.ts",
|
||||
"prepublishOnly": "node build"
|
||||
"test-integration-once": "yarn test test/integration.test.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/execa": "^0.9.0",
|
||||
"@types/jest": "27.4.1",
|
||||
"@vercel/build-utils": "5.0.8",
|
||||
"@vercel/build-utils": "5.1.1",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"execa": "^1.0.0",
|
||||
"typescript": "4.3.4"
|
||||
|
||||
@@ -109,13 +109,16 @@ export const build = async ({
|
||||
if (pipfileLockDir) {
|
||||
debug('Found "Pipfile.lock"');
|
||||
|
||||
let lock: {
|
||||
_meta?: {
|
||||
requires?: {
|
||||
python_version?: string;
|
||||
};
|
||||
};
|
||||
} = {};
|
||||
try {
|
||||
const json = await readFile(join(pipfileLockDir, 'Pipfile.lock'), 'utf8');
|
||||
const obj = JSON.parse(json);
|
||||
pythonVersion = getSupportedPythonVersion({
|
||||
isDev: meta.isDev,
|
||||
pipLockPythonVersion: obj?._meta?.requires?.python_version,
|
||||
});
|
||||
lock = JSON.parse(json);
|
||||
} catch (err) {
|
||||
throw new NowBuildError({
|
||||
code: 'INVALID_PIPFILE_LOCK',
|
||||
@@ -123,6 +126,11 @@ export const build = async ({
|
||||
});
|
||||
}
|
||||
|
||||
pythonVersion = getSupportedPythonVersion({
|
||||
isDev: meta.isDev,
|
||||
pipLockPythonVersion: lock?._meta?.requires?.python_version,
|
||||
});
|
||||
|
||||
// Convert Pipenv.Lock to requirements.txt.
|
||||
// We use a different`workPath` here because we want `pipfile-requirements` and it's dependencies
|
||||
// to not be part of the lambda environment. By using pip's `--target` directive we can isolate
|
||||
|
||||
1
packages/python/test/fixtures/30-fail-build-invalid-pipfile/Pipfile
vendored
Normal file
1
packages/python/test/fixtures/30-fail-build-invalid-pipfile/Pipfile
vendored
Normal file
@@ -0,0 +1 @@
|
||||
blarg
|
||||
1
packages/python/test/fixtures/30-fail-build-invalid-pipfile/Pipfile.lock
generated
vendored
Normal file
1
packages/python/test/fixtures/30-fail-build-invalid-pipfile/Pipfile.lock
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
blarg
|
||||
10
packages/python/test/fixtures/30-fail-build-invalid-pipfile/api/index.py
vendored
Normal file
10
packages/python/test/fixtures/30-fail-build-invalid-pipfile/api/index.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
|
||||
class handler(BaseHTTPRequestHandler):
|
||||
|
||||
def do_GET(self):
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type','text/plain')
|
||||
self.end_headers()
|
||||
self.wfile.write(self.path.encode())
|
||||
return
|
||||
1
packages/python/test/fixtures/30-fail-build-invalid-pipfile/vercel.json
vendored
Normal file
1
packages/python/test/fixtures/30-fail-build-invalid-pipfile/vercel.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
12
packages/python/test/fixtures/31-fail-build-invalid-python36/Pipfile
vendored
Normal file
12
packages/python/test/fixtures/31-fail-build-invalid-python36/Pipfile
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
[[source]]
|
||||
name = "pypi"
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[packages]
|
||||
cowpy = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.6"
|
||||
29
packages/python/test/fixtures/31-fail-build-invalid-python36/Pipfile.lock
generated
vendored
Normal file
29
packages/python/test/fixtures/31-fail-build-invalid-python36/Pipfile.lock
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "841b49dd1836f7373490faa2a1d6abb4ecd53c0b45e8196df7c6e852e21f458b"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.6"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"cowpy": {
|
||||
"hashes": [
|
||||
"sha256:1bdc61d107df02fd34a9241f2220d0704a01d8ce16bed8bff3512a34a0efa56a",
|
||||
"sha256:91a861bfbfa644dfdba5b2250d141b2227a94f61d4dcbeaf7653524d048935a9"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.1.0"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
}
|
||||
13
packages/python/test/fixtures/31-fail-build-invalid-python36/api/index.py
vendored
Normal file
13
packages/python/test/fixtures/31-fail-build-invalid-python36/api/index.py
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
from http.server import BaseHTTPRequestHandler
|
||||
from cowpy import cow
|
||||
|
||||
|
||||
class handler(BaseHTTPRequestHandler):
|
||||
|
||||
def do_GET(self):
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/plain')
|
||||
self.end_headers()
|
||||
message = cow.Cowacter().milk('pip:RANDOMNESS_PLACEHOLDER')
|
||||
self.wfile.write(message.encode())
|
||||
return
|
||||
1
packages/python/test/fixtures/31-fail-build-invalid-python36/vercel.json
vendored
Normal file
1
packages/python/test/fixtures/31-fail-build-invalid-python36/vercel.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
22
packages/python/test/integration.test.ts
vendored
22
packages/python/test/integration.test.ts
vendored
@@ -71,8 +71,30 @@ it('should match the probes against Python dev servers', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
const testsThatFailToBuild = new Map([
|
||||
['30-fail-build-invalid-pipfile', 'Unable to parse Pipfile.lock'],
|
||||
[
|
||||
'31-fail-build-invalid-python36',
|
||||
'Python version "3.6" detected in Pipfile.lock is discontinued and must be upgraded.',
|
||||
],
|
||||
]);
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const fixture of fs.readdirSync(fixturesPath)) {
|
||||
const errMsg = testsThatFailToBuild.get(fixture);
|
||||
if (errMsg) {
|
||||
// eslint-disable-next-line no-loop-func
|
||||
it(`should fail to build ${fixture}`, async () => {
|
||||
try {
|
||||
await testDeployment(path.join(fixturesPath, fixture));
|
||||
} catch (err) {
|
||||
expect(err).toBeTruthy();
|
||||
expect(err.deployment).toBeTruthy();
|
||||
expect(err.deployment.errorMessage).toBe(errMsg);
|
||||
}
|
||||
});
|
||||
continue; //eslint-disable-line
|
||||
}
|
||||
// eslint-disable-next-line no-loop-func
|
||||
it(`should build ${fixture}`, async () => {
|
||||
await expect(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/redwood",
|
||||
"version": "1.0.13",
|
||||
"version": "1.0.15",
|
||||
"main": "./dist/index.js",
|
||||
"license": "MIT",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
@@ -16,18 +16,17 @@
|
||||
"build": "node build.js",
|
||||
"test-integration-once": "yarn test test/test.js",
|
||||
"test": "jest --env node --verbose --bail --runInBand",
|
||||
"test-unit": "yarn test test/prepare-cache.test.js",
|
||||
"prepublishOnly": "node build.js"
|
||||
"test-unit": "yarn test test/prepare-cache.test.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/nft": "0.21.0",
|
||||
"@vercel/routing-utils": "2.0.0",
|
||||
"@vercel/routing-utils": "2.0.1",
|
||||
"semver": "6.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/aws-lambda": "8.10.19",
|
||||
"@types/node": "*",
|
||||
"@types/semver": "6.0.0",
|
||||
"@vercel/build-utils": "5.0.8"
|
||||
"@vercel/build-utils": "5.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/remix",
|
||||
"version": "1.0.14",
|
||||
"version": "1.0.16",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
@@ -13,8 +13,7 @@
|
||||
"build": "node build.js",
|
||||
"test-integration-once": "yarn test test/test.js",
|
||||
"test": "jest --env node --verbose --bail --runInBand",
|
||||
"test-unit": "yarn test test/build.test.ts",
|
||||
"prepublishOnly": "node build.js"
|
||||
"test-unit": "yarn test test/build.test.ts"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -26,7 +25,7 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "27.5.1",
|
||||
"@types/node": "*",
|
||||
"@vercel/build-utils": "5.0.8",
|
||||
"@vercel/build-utils": "5.1.1",
|
||||
"typescript": "4.6.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/routing-utils",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"description": "Vercel routing utilities",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -14,9 +14,7 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"prepublishOnly": "tsc",
|
||||
"build": "tsc",
|
||||
"watch": "tsc --watch",
|
||||
"test": "jest --env node --verbose --runInBand --bail",
|
||||
"test-unit": "yarn test"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@vercel/ruby",
|
||||
"author": "Nathan Cahill <nathan@nathancahill.com>",
|
||||
"version": "1.3.20",
|
||||
"version": "1.3.22",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
|
||||
@@ -17,13 +17,12 @@
|
||||
"scripts": {
|
||||
"build": "node build",
|
||||
"test": "jest --env node --verbose --runInBand --bail",
|
||||
"test-integration-once": "yarn test",
|
||||
"prepublishOnly": "node build"
|
||||
"test-integration-once": "yarn test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "8.0.0",
|
||||
"@types/semver": "6.0.0",
|
||||
"@vercel/build-utils": "5.0.8",
|
||||
"@vercel/build-utils": "5.1.1",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"execa": "2.0.4",
|
||||
"fs-extra": "^7.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/static-build",
|
||||
"version": "1.0.13",
|
||||
"version": "1.0.15",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/build-step",
|
||||
@@ -16,8 +16,7 @@
|
||||
"build": "node build",
|
||||
"test": "jest --env node --verbose --bail --runInBand",
|
||||
"test-unit": "yarn test test/build.test.ts test/prepare-cache.test.ts",
|
||||
"test-integration-once": "yarn test test/integration-*.test.js",
|
||||
"prepublishOnly": "node build"
|
||||
"test-integration-once": "yarn test test/integration-*.test.js"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "ts-jest/presets/default",
|
||||
@@ -37,10 +36,10 @@
|
||||
"@types/ms": "0.7.31",
|
||||
"@types/node-fetch": "2.5.4",
|
||||
"@types/promise-timeout": "1.3.0",
|
||||
"@vercel/build-utils": "5.0.8",
|
||||
"@vercel/frameworks": "1.1.1",
|
||||
"@vercel/build-utils": "5.1.1",
|
||||
"@vercel/frameworks": "1.1.2",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/routing-utils": "2.0.0",
|
||||
"@vercel/routing-utils": "2.0.1",
|
||||
"fs-extra": "10.0.0",
|
||||
"get-port": "5.0.0",
|
||||
"is-port-reachable": "2.0.1",
|
||||
|
||||
@@ -367,16 +367,20 @@ export const build: BuildV2 = async ({
|
||||
);
|
||||
|
||||
if (process.env.VERCEL_URL) {
|
||||
const vercelSystemEnvPrefix = 'VERCEL_';
|
||||
const { envPrefix } = framework;
|
||||
if (envPrefix) {
|
||||
Object.keys(process.env)
|
||||
.filter(key => key.startsWith('VERCEL_'))
|
||||
.filter(key => key.startsWith(vercelSystemEnvPrefix))
|
||||
.forEach(key => {
|
||||
const newKey = `${envPrefix}${key}`;
|
||||
if (!(newKey in process.env)) {
|
||||
process.env[newKey] = process.env[key];
|
||||
}
|
||||
});
|
||||
|
||||
// tell turbo to exclude all vercel system env vars (envPrefix includes trailing underscore)
|
||||
process.env.TURBO_CI_VENDOR_ENV_KEY = `${envPrefix}${vercelSystemEnvPrefix}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": {
|
||||
"zeroConfig": true,
|
||||
"framework": "gatsby"
|
||||
}
|
||||
}
|
||||
],
|
||||
"probes": [{ "path": "/", "mustContain": "Welcome to your new Gatsby site" }]
|
||||
}
|
||||
3
packages/static-build/test/fixtures/10-gatsby-without-build-script/probes.json
vendored
Normal file
3
packages/static-build/test/fixtures/10-gatsby-without-build-script/probes.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"probes": [{ "path": "/", "mustContain": "Welcome to your new Gatsby site" }]
|
||||
}
|
||||
@@ -1,12 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": { "zeroConfig": true }
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "Welcome to your new Gatsby site" },
|
||||
{
|
||||
@@ -1,12 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": { "zeroConfig": true }
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "Svelte app" },
|
||||
{ "path": "/bundle.js", "mustContain": "Hello" },
|
||||
@@ -1,12 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": { "zeroConfig": true }
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "React App" },
|
||||
{ "path": "/testing-output-dir.txt", "mustContain": "This is some content" }
|
||||
@@ -1,14 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": {
|
||||
"zeroConfig": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/",
|
||||
@@ -1,12 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": { "zeroConfig": true }
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "Angular + Node.js API" },
|
||||
{ "path": "/main-es2015.js", "mustContain": "Loading date..." },
|
||||
@@ -1,12 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": { "zeroConfig": true }
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "AngularApp" },
|
||||
{ "path": "/main-es2015.js", "mustContain": "Tour of Heroes" },
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": { "zeroConfig": true }
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "15-preact" },
|
||||
{ "path": "/iudieufwunifiwwnuwfuw", "mustContain": "15-preact" }
|
||||
]
|
||||
}
|
||||
6
packages/static-build/test/fixtures/15-preact/probes.json
vendored
Normal file
6
packages/static-build/test/fixtures/15-preact/probes.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "15-preact" },
|
||||
{ "path": "/iudieufwunifiwwnuwfuw", "mustContain": "15-preact" }
|
||||
]
|
||||
}
|
||||
@@ -1,12 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": { "zeroConfig": true }
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "Hello World" },
|
||||
{ "path": "/", "mustContain": "Hexo" },
|
||||
@@ -1,12 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": { "zeroConfig": true }
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "Home" },
|
||||
{ "path": "/", "mustContain": "Gridsome" },
|
||||
@@ -1,12 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": { "zeroConfig": true }
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "/umi.css" },
|
||||
{ "path": "/iudieufwunifiwwnuwfuw", "mustContain": "/umi.css" },
|
||||
@@ -1,12 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": { "zeroConfig": true }
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "My App" },
|
||||
{ "path": "/iudieufwunifiwwnuwfuw", "mustContain": "My App" },
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": { "zeroConfig": true }
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "Site" },
|
||||
{ "path": "/iudieufwunifiwwnuwfuw", "mustContain": "Site" }
|
||||
]
|
||||
}
|
||||
6
packages/static-build/test/fixtures/20-ember/probes.json
vendored
Normal file
6
packages/static-build/test/fixtures/20-ember/probes.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "Site" },
|
||||
{ "path": "/iudieufwunifiwwnuwfuw", "mustContain": "Site" }
|
||||
]
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "https://files-dwshfhr7d.now.sh",
|
||||
"config": {
|
||||
"zeroConfig": true,
|
||||
"framework": "docusaurus"
|
||||
}
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "Test Site · A website for testing" }
|
||||
]
|
||||
}
|
||||
5
packages/static-build/test/fixtures/21-docusaurus-with-framework/probes.json
vendored
Normal file
5
packages/static-build/test/fixtures/21-docusaurus-with-framework/probes.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "Test Site · A website for testing" }
|
||||
]
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": { "zeroConfig": true }
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": { "zeroConfig": true }
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "My Site" },
|
||||
{ "path": "/docs/doc1", "mustContain": "Style Guide" }
|
||||
]
|
||||
}
|
||||
6
packages/static-build/test/fixtures/22-docusaurus-2/probes.json
vendored
Normal file
6
packages/static-build/test/fixtures/22-docusaurus-2/probes.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "My Site" },
|
||||
{ "path": "/docs/doc1", "mustContain": "Style Guide" }
|
||||
]
|
||||
}
|
||||
@@ -1,12 +1,4 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build",
|
||||
"config": { "zeroConfig": true }
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{ "path": "/", "mustContain": "Great" },
|
||||
{ "path": "/about", "mustContain": "page" },
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/static-build"
|
||||
}
|
||||
],
|
||||
"probes": [{ "path": "/", "mustContain": "mypage" }]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user