Compare commits

...

21 Commits

Author SHA1 Message Date
Steven
5eb8b16cbd Publish Stable
- @vercel/build-utils@5.1.1
 - vercel@27.3.6
 - @vercel/client@12.1.9
 - @vercel/edge@0.0.2
 - @vercel/frameworks@1.1.2
 - @vercel/fs-detectors@2.0.4
 - @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/routing-utils@2.0.1
 - @vercel/ruby@1.3.22
 - @vercel/static-build@1.0.15
 - @vercel/static-config@2.0.2
2022-08-04 11:39:58 -04:00
JJ Kasper
226bf02be2 [next] Remove middleware regexp modifying (#8321)
x-ref: [slack thread](https://vercel.slack.com/archives/C03SF65BYSG/p1659626639087909)
2022-08-04 11:39:21 -04:00
Steven
8505872f55 [tests] Update package.json scripts (#8318)
This PR consolidates all the `test` scripts to be the same and removes the `prepublishOnly` script since we always run `build` before publishing to npm.
2022-08-04 11:02:56 -04:00
Steven
7db6436797 Publish Stable
- @vercel/build-utils@5.1.0
 - vercel@27.3.5
 - @vercel/client@12.1.8
 - @vercel/go@2.0.13
 - @vercel/hydrogen@0.0.10
 - @vercel/next@3.1.13
 - @vercel/node@2.5.4
 - @vercel/python@3.1.5
 - @vercel/redwood@1.0.14
 - @vercel/remix@1.0.15
 - @vercel/ruby@1.3.21
 - @vercel/static-build@1.0.14
2022-08-04 08:37:04 -04:00
Chris Barber
e2d76e9c92 [cli] recreate symlinked files instead of copying (#8270)
Instead of copying symlinked files during a build, recreate the symlink.

### 📋 Checklist

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-04 04:07:33 +00:00
Nathan Rajlich
337cb21d67 [cli] Fix flaky error in builds.json tests (#8316)
Follow-up to #8305.

The `expect().toOutput()` call was frequently timing out before the
command writes the error to the terminal, causing the test to fail.
So wait for the command to return the exit code before running assertion
on the printed output.
2022-08-03 20:38:45 -07:00
JJ Kasper
6bfff3e9eb [next] Remove un-necessary duplicate i18n route (#8313)
* Remove un-necessary duplicate i18n route

* update
2022-08-03 21:02:19 -05:00
Sean Massa
ac5b259c11 [go] refactor away use of downloaded result (#8291)
Review Notes: Turn off diff whitespace.

### Refactor

This PR refactors away the use of the result of `download` for a couple of reasons:
- Keeping `files` and `downloadedFiles` in sync with the file system (like when we rename a file that starts with a bracket) is easy to forget to do, causing bugs
- Nate says that `files` is something we've wanted to move away from using anyway
- It simplifies the code in a few places
- It was getting in the way of other fixes that need to be made

We do still call `download`, but it should be a no-op most of the time.

As a consequence of these changes, this PR also addresses:
- the builder no longer leaves build artifacts around, in many cases
- the builder can compile files that start with brackets again; routes don't seem to allow this to file to respond to a dynamic segments yet, though

### Next Steps

Upcoming PRs will resolve builder issues:

- bracket endpoints responding to dynamic segments
- exported function name conflict handling
- compilation targets should only apply to the source code build, not the analyze go utility

### Operating In-place

We also now have a cleanup step that clears out created files, created directories, and undoes file renames. This fixes an issue where multiple builds on the same directory would fail. It also cleans the user's project code when they are using `vc build`.

Ideally, we'd probably copy all of the code to a separate location, then freely do filesystem operations there. It's not clear to me if this is preferred for large projects because it would have to happen once per endpoint: 100 Go files would cause 10,000 (100 * 100) file copies (or symlinks). 

It has to copy once per endpoint because we potentially need all of the files around in case any of them are imported. If we had nft-style tracing for Go, we could copy only what we needed.

This gets more complex in the next step where the exported function names will be renamed during compilation to fix the name conflict issue.
2022-08-04 01:11:35 +00:00
Steven
bfc553db11 [tests] Add support for probes.json (#8279)
Previously, our test fixtures used to use a probes prop in `vercel.json` that was removed right before it was deployed.

This PR allows a separate `probes.json` file with the same content to separate the test fixture input from the test probes.

This allows us to test real "zero config" deployments without a `vercel.json` file.
2022-08-03 20:15:50 -04:00
Nathan Rajlich
2b101d4692 [cli] Remove legacy config file migration logic (#8199)
Removes the legacy config file migration logic from back in the days when Zeit CLI supported multiple "providers". This was from a _very_ long time ago and we should expect that anyone who would have migrated at this point, has.
2022-08-03 23:48:17 +00:00
Matthew Stanciu
3316f38cb4 [cli] Strip scheme from vc inspect argument (#8307)
Right now, `vc inspect` fails to find a deployment if you include `http://` before it. But it works with no scheme and with `https://`.

Since it appears no scheme is what the API looks for anyway, and to avoid confusion, this PR strips any included scheme from the `deploymentIdOrHost` argument.

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR
2022-08-03 22:48:44 +00:00
Nathan Rajlich
7837387127 [cli] Print error from Builder in vc build (#8305)
Ensures that errors that are serialized into `builds.json` are also printed to the terminal when running the `vc build` command.

Co-authored-by: Steven <steven@ceriously.com>
2022-08-03 17:32:50 -04:00
Thomas Knickman
f478200dd3 [static-build] set TURBO_CI_VENDOR_ENV_KEY environment variable (#8306)
Set `TURBO_CI_VENDOR_ENV_KEY` to support https://github.com/vercel/turborepo/pull/1622
2022-08-03 17:31:18 -04:00
Matthew Stanciu
c29de8206a [cli] Minor vc env pull diff formatting changes (#8303)
#8170 added a new message at the end of `vc env pull` which shows a delta of what was added, modified, and removed. Some people shared feedback that the yellow chalk color and `~` prefix to indicate modified variables was confusing. This PR instead keeps the prefix as `+` with a green color, but adds a `(Modified)` suffix at the end of every modified variable.

<img width="638" alt="Screen Shot 2022-08-03 at 10 18 28 AM" src="https://user-images.githubusercontent.com/14811170/182670327-5a3df6db-d84d-40a1-956b-9cf159501759.png">

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-03 20:04:03 +00:00
JJ Kasper
a2df3b5463 [next] Update data route handling for i18n and static routes (#8304)
* Ensure dynamic data route handles missing default locale path

* Ensure static data routes are still handled
2022-08-03 13:40:00 -05:00
Steven
73446e544a [python] Fix error message for discontinued Python 3.6 (#8300)
This fixes the error message when a discontinued version of python (for example, Python 3.6) is detected.

https://vercel.com/changelog/python-3-6-is-being-deprecated
2022-08-03 13:09:03 -04:00
JJ Kasper
21ff4a58c3 [next] Ensure we resolve _next/data dynamic routes correctly with i18n (#8297)
* Ensure we resolve _next/data dynamic routes correctly with i18n

* remove test version
2022-08-03 09:03:15 -05:00
JJ Kasper
2b9eb02b8c [next] Fix _next/data resolving priority for dynamic routes (#8278)
* Fix _next/data resolving priority for dynamic routes

* Apply suggestions from code review

* Ensure we match middleware for _next/data without header

* fix nested middleware case

* Update data routes generating

* Add version lock for non-nested middleware

* use path.posix
2022-08-02 17:41:06 -05:00
JJ Kasper
4ef4722460 [next] Fix priority for notFound preview routes (#7902)
Fix priority for notFound preview routes
2022-08-02 17:09:09 -05:00
Sean Massa
be5308b137 [dev] log middleware errors in vc dev (#8267)
Middleware server setup wasn't logging errors the same way that dev server setup was. This meant that middleware instantiation errors (like invalid config) would cause requests to 500, but no errors to be logged to the console.

This PR updates the invalid config error, makes sure errors in this area are logged out, and adds a test for this behavior.

**It may be appropriate to fail the deploy (and crash `vc dev`) in this case instead, though. What do others think?**

---

During `vc dev` with middleware that has an invalid `config.matcher` value...

Before: You see a 500 response in the browser and no output in the terminal.

After: You see a 500 response in the browser and this output in the terminal:

```
Error! Middleware's `config.matcher` values must start with "/". Received: not-a-valid-matcher
```

---

Related Issue: https://github.com/vercel/edge-functions/issues/220
2022-08-02 20:01:42 +00:00
Steven
08a83a94f8 [docs] Link to Build Output API docs (#8292)
* [docs] Link to Build Output API docs

Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2022-08-02 12:35:36 -04:00
276 changed files with 1763 additions and 4977 deletions

View File

@@ -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:

View File

@@ -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",

View File

@@ -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);

View File

@@ -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,

View File

@@ -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",

View File

@@ -469,6 +469,8 @@ async function doBuild(
)
);
} catch (err: any) {
output.prettyError(err);
const writeConfigJsonPromise = fs.writeJSON(
join(outputDir, 'config.json'),
{ version: 3 },

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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) &&

View File

@@ -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;

View File

@@ -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);
}
/**

View File

@@ -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',
};

View File

@@ -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,

View File

@@ -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'
);
}

View 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)
}

View File

@@ -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")
}

View File

@@ -0,0 +1,7 @@
export const config = {
matcher: 'not-a-valid-matcher',
};
export default function middleware(request, _event) {
return new Response(null);
}

View File

@@ -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);

View File

@@ -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');
})
);

View File

@@ -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}".`);
}

View File

@@ -0,0 +1,7 @@
{
"orgId": ".",
"projectId": ".",
"settings": {
"framework": null
}
}

View File

@@ -0,0 +1 @@
<h1>Vercel</h1>

View File

@@ -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);

View File

@@ -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);
});
});

View File

@@ -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 });

View File

@@ -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",

View File

@@ -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",

View File

@@ -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"
}

View File

@@ -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",

View File

@@ -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 {

View File

@@ -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",

View File

@@ -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)
}

View File

@@ -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" }
]

View File

@@ -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"
}
}

View File

@@ -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",

View File

@@ -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

View File

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

View File

@@ -0,0 +1,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;
}
}

View 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',
},
],
};
},
};

View File

@@ -0,0 +1,11 @@
{
"scripts": {
"dev": "next dev",
"build": "next build"
},
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
}
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p>/[teamId]/[slug]</p>;
}

View 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',
};
}

View 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',
},
});

View File

@@ -0,0 +1,3 @@
export default function Index() {
return <p className="title">Dynamic route</p>;
}

View File

@@ -0,0 +1,3 @@
export default function Index() {
return <p className="title">static route</p>;
}

View 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 || '',
},
});

View File

@@ -0,0 +1,3 @@
export default function Home() {
return <h1>Home A</h1>;
}

View File

@@ -0,0 +1,3 @@
export default function Home() {
return <h1>Home B</h1>;
}

View 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>
);
}

View 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\""
}
]
}

View File

@@ -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') {

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p>/[teamId]/[slug]</p>;
}

View File

@@ -0,0 +1,3 @@
export default function Index() {
return <p className="title">static route</p>;
}

View File

@@ -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\""
}
]
}

View File

@@ -5,4 +5,13 @@ module.exports = {
experimental: {
nftTracing: true,
},
redirects() {
return [
{
source: '/build-time-not-found/:path*',
destination: '/somewhere-else',
permanent: false,
},
];
},
};

View 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(),
},
};
}

View File

@@ -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,

View File

@@ -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",

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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"

View File

@@ -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

View File

@@ -0,0 +1 @@
blarg

View File

@@ -0,0 +1 @@
blarg

View 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

View File

@@ -0,0 +1 @@
{}

View 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"

View 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": {}
}

View 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

View File

@@ -0,0 +1 @@
{}

View File

@@ -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(

View File

@@ -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"
}
}

View File

@@ -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"
}
}

View File

@@ -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"
},

View File

@@ -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",

View File

@@ -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",

View File

@@ -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}`;
}
}

View File

@@ -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" }]
}

View File

@@ -0,0 +1,3 @@
{
"probes": [{ "path": "/", "mustContain": "Welcome to your new Gatsby site" }]
}

View File

@@ -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" },
{

View File

@@ -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" },

View File

@@ -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" }

View File

@@ -1,14 +1,4 @@
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/static-build",
"config": {
"zeroConfig": true
}
}
],
"probes": [
{
"path": "/",

View File

@@ -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..." },

View File

@@ -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" },

View File

@@ -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" }
]
}

View File

@@ -0,0 +1,6 @@
{
"probes": [
{ "path": "/", "mustContain": "15-preact" },
{ "path": "/iudieufwunifiwwnuwfuw", "mustContain": "15-preact" }
]
}

View File

@@ -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" },

View File

@@ -1,12 +1,4 @@
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/static-build",
"config": { "zeroConfig": true }
}
],
"probes": [
{ "path": "/", "mustContain": "Home" },
{ "path": "/", "mustContain": "Gridsome" },

View File

@@ -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" },

View File

@@ -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" },

View File

@@ -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" }
]
}

View File

@@ -0,0 +1,6 @@
{
"probes": [
{ "path": "/", "mustContain": "Site" },
{ "path": "/iudieufwunifiwwnuwfuw", "mustContain": "Site" }
]
}

View File

@@ -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" }
]
}

View File

@@ -0,0 +1,5 @@
{
"probes": [
{ "path": "/", "mustContain": "Test Site · A website for testing" }
]
}

View File

@@ -1,10 +0,0 @@
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/static-build",
"config": { "zeroConfig": true }
}
]
}

View File

@@ -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" }
]
}

View File

@@ -0,0 +1,6 @@
{
"probes": [
{ "path": "/", "mustContain": "My Site" },
{ "path": "/docs/doc1", "mustContain": "Style Guide" }
]
}

View File

@@ -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" },

View File

@@ -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