Compare commits

...

37 Commits

Author SHA1 Message Date
Ethan Arrowood
d91bca7d6b Publish Canary
- @vercel/build-utils@2.16.1-canary.2
 - vercel@24.2.1-canary.2
 - @vercel/client@11.0.1-canary.2
 - @vercel/frameworks@0.8.1-canary.0
 - @vercel/go@1.4.1-canary.2
 - @vercel/node@1.15.1-canary.2
 - @vercel/python@2.3.1-canary.2
 - @vercel/redwood@0.8.1-canary.2
 - @vercel/ruby@1.3.4-canary.2
 - @vercel/static-build@0.24.1-canary.2
2022-05-06 14:16:00 -06:00
Ethan Arrowood
be54fce67b [build-utils] add pnpm7 to path when lockfile v5.4 is detected (#7758)
### Related Issues

Adds support for `pnpm@7` using a similar lock file detection and path setting method that `npm@7` uses

### 📋 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
- [ ] Issue from task tracker has a link to this PR
2022-05-06 19:47:41 +00:00
Sean Massa
7753bb8d89 [cli] check prebuilt target env against deploy target env (#7748)
This check prevents a user from creating a build output targeting one environment (like `preview`) and deploying targeting another environment (like `production`). It checks `.vercel/output/builds.json` for the `target` property to decide this. If that file is missing, the check is not run.
2022-05-06 07:06:07 +00:00
Logan McAnsh
ce17ac5c35 [frameworks] Update Remix detection to check for "remix.config.js" (#7761)
The "remix" magic package is no longer preferred by Remix,
so anyone who is using a modern template won't have it installed.

Checking for `remix.config.js` is safer detection logic.
2022-05-05 19:13:44 -07:00
Sean Massa
8006fc32b8 [cli] require non-TTY uses of pull to also pass --yes (#7700)
Checks for `TTY` input to decide to handle the command or not. If the input is not TTY and the user did not pass the `--yes` option, the CLi will exit with the message:

> Command `vercel pull` requires confirmation. Use option "--yes" to confirm.
2022-05-05 22:40:26 +00:00
Nathan Rajlich
8038a90db1 [node] Use TypeScript for unit tests (#7756)
Random change I had in my working directory.
2022-05-05 19:13:01 +00:00
agadzik
f88c862e9d Publish Canary
- @vercel/build-utils@2.16.1-canary.1
 - vercel@24.2.1-canary.1
 - @vercel/client@11.0.1-canary.1
 - @vercel/go@1.4.1-canary.1
 - @vercel/node@1.15.1-canary.1
 - @vercel/python@2.3.1-canary.1
 - @vercel/redwood@0.8.1-canary.1
 - @vercel/ruby@1.3.4-canary.1
 - @vercel/static-build@0.24.1-canary.1
2022-05-05 14:11:13 -04:00
Nathan Rajlich
9170820371 [build-utils] Add ShouldServe type (#7755)
Version 3 Builders can define a `shouldServe()` function that is used in `vercel dev`, so add the proper type for that.
2022-05-05 17:06:20 +00:00
Andrew Gadzik
c881546e0e [build-utils] Add supported list of workspace managers (#7737)
In order to support monorepo detection, we need to build out the list of supported workspace managers so that our helper functions and API endpoints can utilize the same `detectFramework` function / logic to detect a workspace manager for a given git repository.

### Related Issues

- Closes https://github.com/vercel/vercel/issues/7731

### 📋 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-05-05 14:44:34 +00:00
Nathan Rajlich
fa21db98e4 [cli] Revert "Use @vercel/fetch-retry in CLI integration tests" (#7738)
This reverts commit 03a8fbd3a7 (#7360).
2022-05-04 17:15:54 +00:00
Ethan Arrowood
8eabbfc666 Publish Canary
- @vercel/build-utils@2.16.1-canary.0
 - vercel@24.2.1-canary.0
 - @vercel/client@11.0.1-canary.0
 - @vercel/go@1.4.1-canary.0
 - @vercel/node@1.15.1-canary.0
 - @vercel/python@2.3.1-canary.0
 - @vercel/redwood@0.8.1-canary.0
 - @vercel/ruby@1.3.4-canary.0
 - @vercel/static-build@0.24.1-canary.0
2022-05-02 11:33:22 -06:00
Ethan Arrowood
6783f7afc9 [build-utils] Fix package manager auto detection precedence (#7733)
### Related Issues

improves package manager auto detection so that when multiple lock files are found, the highest priority one is selected rather than defaulting to yarn. 

### 📋 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
- [ ] 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-05-02 17:22:01 +00:00
Steven
a400b9b29d Publish Stable
- @vercel/build-utils@2.16.0
 - vercel@24.2.0
 - @vercel/client@11.0.0
 - @vercel/frameworks@0.8.0
 - @vercel/go@1.4.0
 - @vercel/node-bridge@2.2.1
 - @vercel/node@1.15.0
 - @vercel/python@2.3.0
 - @vercel/redwood@0.8.0
 - @vercel/routing-utils@1.13.2
 - @vercel/ruby@1.3.3
 - @vercel/static-build@0.24.0
 - @vercel/static-config@1.0.0
2022-04-29 14:11:32 -04:00
Steven
b549c37149 [build-utils][static-build] Replace 00a0 with space (#7732) 2022-04-28 22:25:24 -04:00
Nathan Rajlich
30d5e64291 Publish Canary
- @vercel/build-utils@2.15.2-canary.6
 - vercel@24.1.1-canary.8
 - @vercel/client@10.4.2-canary.7
 - @vercel/go@1.3.3-canary.6
 - @vercel/node@1.14.2-canary.7
 - @vercel/python@2.2.3-canary.6
 - @vercel/redwood@0.7.1-canary.6
 - @vercel/ruby@1.3.3-canary.6
 - @vercel/static-build@0.23.2-canary.6
2022-04-28 17:50:59 -07:00
Nathan Rajlich
47c2c361d2 [build-utils] Update "yazl" dependency (#7734)
The older version of "yazl" was using `new Buffer()` which causes
deprecation warnings to be printed. The latest version avoids that.
2022-04-28 17:47:10 -07:00
Nathan Rajlich
438576fc7c Publish Canary
- @vercel/build-utils@2.15.2-canary.5
 - vercel@24.1.1-canary.7
 - @vercel/client@10.4.2-canary.6
 - @vercel/frameworks@0.7.2-canary.1
 - @vercel/go@1.3.3-canary.5
 - @vercel/node@1.14.2-canary.6
 - @vercel/python@2.2.3-canary.5
 - @vercel/redwood@0.7.1-canary.5
 - @vercel/ruby@1.3.3-canary.5
 - @vercel/static-build@0.23.2-canary.5
 - @vercel/static-config@1.0.0-canary.1
2022-04-28 11:53:39 -07:00
Steven
b30343ef7b [tests] Bump dependencies for jest/eslint/prettier/turbo/etc (#7727) 2022-04-28 14:52:46 -04:00
Ethan Arrowood
2dc0dfa572 [node][static-build][redwood] Add root path pattern to prepareCache() (#7710) 2022-04-27 21:17:44 -07:00
Steven
9ee54b3dd6 [static-build] Resolve git.io links (#7722)
https://github.blog/changelog/2022-04-25-git-io-deprecation/
2022-04-26 21:57:12 -04:00
Steven
9d67e0bc06 [python] Add discontinue date for Python 3.6 (#7709)
This PR does a few things:
- Changes the existing warning message for Python 3.6 to print a discontinue date
- Will automatically fail new Python 3.6 deployments created after that date
- Consolidates logic to make Python version selection work in a similar manner to Node.js version selection
- Changes tests from JS to TS
2022-04-26 15:49:19 -04:00
Steven
466135cf84 Publish Canary
- @vercel/build-utils@2.15.2-canary.4
 - vercel@24.1.1-canary.6
 - @vercel/client@10.4.2-canary.5
 - @vercel/go@1.3.3-canary.4
 - @vercel/node@1.14.2-canary.5
 - @vercel/python@2.2.3-canary.4
 - @vercel/redwood@0.7.1-canary.4
 - @vercel/ruby@1.3.3-canary.4
 - @vercel/static-build@0.23.2-canary.4
2022-04-25 12:49:34 -04:00
Steven
eab2e229dc [build-utils] Add warning for experimental Node.js (#7717) 2022-04-25 12:49:04 -04:00
Steven
698b89a2ba Publish Canary
- @vercel/build-utils@2.15.2-canary.3
 - vercel@24.1.1-canary.5
 - @vercel/client@10.4.2-canary.4
 - @vercel/frameworks@0.7.2-canary.0
 - @vercel/go@1.3.3-canary.3
 - @vercel/node@1.14.2-canary.4
 - @vercel/python@2.2.3-canary.3
 - @vercel/redwood@0.7.1-canary.3
 - @vercel/ruby@1.3.3-canary.3
 - @vercel/static-build@0.23.2-canary.3
2022-04-25 12:02:13 -04:00
Steven
bae2a2e4df [python] Upgrade tests (#7711)
* [python] Upgrade tests

* Fix latest sanic asgi

* Bump flask

* Change requirements.txt to Pipfile

* Fix dev test

* Use verbose requirements.txt

* Flip requirements
2022-04-25 12:01:49 -04:00
Steven
57916bb712 [build-utils] Add env var ENABLE_EXPERIMENTAL_NODE16 (#7489)
This PR uses an environment variable since this feature is not available to all accounts yet.
2022-04-25 11:07:16 -04:00
Sean Massa
12bbd4e8eb [cli] Update language of prebuilt error (#7702) 2022-04-22 09:55:27 -07:00
Aaron Morris
4e4c7023dc [frameworks] Add opt-in darkModeLogo to Framework in packages/frameworks (#7693)
Supply light mode logos over the frameworks API to be consumed by front

### Related Issues

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-04-22 16:49:39 +00:00
Nathan Rajlich
41805790e7 [static-build] Support Build Output API v3 cache property in prepareCache() (#7704)
When a framework that outputs Build Output API v3 specifies the `cache`
property in the `config.json` file then static-build will include those
files in the `prepareCache()` function result.
2022-04-21 18:04:21 -07:00
Ethan Arrowood
6ad77ae8e1 Publish Canary
- vercel@24.1.1-canary.4
 - @vercel/client@10.4.2-canary.3
2022-04-21 16:01:41 -06:00
Ethan Arrowood
e8daf36cd7 only revert override feature (#7701) 2022-04-21 15:59:55 -06:00
Nathan Rajlich
c319a2c499 [client] Remove single file deployment root path / route (#7699)
This is strange behavior, and also inconsistent compared to how a Git deployment is created. Let's remove it.
2022-04-21 00:35:25 +00:00
Sean Massa
a410baa797 [cli] check for prebuilt directory (#7697)
When using `vc deploy --prebuilt`, we need to check for an actual ".vercel/output" before attempting to deploy. This PR adds a a check for that. Now...

In a directory without `.vercel/output`:

```
$ vc deploy --prebuilt
Error! Option `--prebuilt` was used, but no prebuilt deploy found in ".vercel/output"
```

In a direcotry with `.vercel/output` (where I've not linked it yet):

```
$ vc deploy --prebuilt
? Set up and deploy “~/source/vercel/examples/build-output-api/serverless-function”? [Y/n]
```
2022-04-20 19:54:47 +00:00
Dominik Ferber
625568e659 fix ts-eager (#7677)
Switches await import() to require().default so that ts-eager understands them.

closes #7676
2022-04-20 10:30:48 -07:00
Nathan Rajlich
41868c1fe0 [cli] Ensure .vercel directory is created in vc pull (#7695)
When the `.vercel` directory does not exist and the env vars
(`VERCEL_PROJECT_ID`/`VERCEL_ORG_ID`) are used for project
linking, the `vercel pull` command was throwing an error:

```
$ VERCEL_PROJECT_ID=xxxxxxxx VERCEL_ORG_ID=xxxxxxxxxx vercel pull
Vercel CLI 24.1.1-canary.3 — https://vercel.com/feedback
Downloading "development" Environment Variables for Project t
Error! ENOENT: no such file or directory, open '/Code/t/.vercel/.env.development.local'
```
2022-04-19 20:49:44 -07:00
Steven
1b644f1218 [docs] Update available runtimes (#7692) 2022-04-19 12:08:46 -04:00
Sean Massa
29ea0fb06b [examples] Remove amp example (#7686) 2022-04-17 19:29:56 -04:00
127 changed files with 10971 additions and 1843 deletions

View File

@@ -307,15 +307,15 @@ This is a [class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refere
This is an abstract enumeration type that is implemented by one of the following possible `String` values:
- `nodejs14.x`
- `nodejs12.x`
- `nodejs10.x`
- `go1.x`
- `java11`
- `python3.9`
- `python3.6`
- `dotnetcore2.1`
- `ruby2.5`
- `provided`
- `dotnet6`
- `dotnetcore3.1`
- `ruby2.7`
- `provided.al2`
## `@vercel/build-utils` Helper Functions

View File

@@ -1 +0,0 @@
.env

View File

@@ -1,19 +0,0 @@
# AMP Example
This directory is a brief example of an [AMP](https://amp.dev/) site that can be deployed to Vercel with zero configuration.
## Deploy Your Own
Deploy your own AMP project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/vercel/tree/main/examples/amp)
_Live Example: https://amp-template.vercel.app_
### How We Created This Example
To get started deploying AMP with Vercel, you can use the [Vercel CLI](https://vercel.com/download) to initialize the project:
```shell
$ vercel init amp
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -1,72 +0,0 @@
<!DOCTYPE html>
<html >
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,minimum-scale=1" />
<link rel="shortcut icon" href="favicon.png">
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
<link rel="canonical" href="index.html" />
<title>AMP Website</title>
<script async src="https://cdn.ampproject.org/v0.js"></script>
<style amp-custom>
body > * {
margin: 3rem 1rem;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
color: #525252;
}
h3 {
font-size: 2rem;
}
h4 {
margin-top: 2rem;
}
p {
font-size: 1.2rem;
line-height: 2rem;
}
.links {
display: flex;
justify-content: center;
margin-bottom: 3rem;
}
.links a {
margin: 0 10px;
font-size: 1rem;
color: #005af0;
}
</style>
</head>
<body>
<center>
<amp-img width=150 height=150 layout="fixed" class="logo" src="logo.png"></amp-img>
<h3>Welcome to your AMP page</h3>
<p>AMP is a web component framework to <br> easily create user-first websites, stories, ads and emails.</p>
<h4>Links</h4>
<div class="links">
<a href="https://amp.dev/">Homepage</a>
<a href="https://amp.dev/documentation/guides-and-tutorials/?format=websites">Tutorials</a>
<a href="https://amp.dev/documentation/examples/">Examples</a>
<a href="https://blog.amp.dev">Blog</a>
</div>
<h4>Ready to get started?</h4>
<div class="links">
<a href="https://amp.dev/documentation/guides-and-tutorials/start/create/?format=websites">Create your first AMP page</a>
</div>
<h4>Get involved</h4>
<div class="links">
<a href="https://twitter.com/amphtml">Twitter</a>
<a href="https://amphtml.slack.com">Slack</a>
<a href="https://amp.dev/events/amp-conf-2019">AMP Conf</a>
<a href="https://amp.dev/events/amp-roadshow">AMP Roadshow</a>
</div>
</center>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -15,7 +15,7 @@ cache:
env:
global:
# See https://git.io/vdao3 for details.
# See https://github.com/ember-cli/ember-cli/blob/master/docs/build-concurrency.md
- JOBS=1
script:

View File

@@ -15,23 +15,23 @@
"lerna": "3.16.4"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "4.28.0",
"@typescript-eslint/parser": "4.28.0",
"@typescript-eslint/eslint-plugin": "5.21.0",
"@typescript-eslint/parser": "5.21.0",
"async-retry": "1.2.3",
"buffer-replace": "1.0.0",
"eslint": "7.29.0",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-jest": "24.3.6",
"husky": "6.0.0",
"jest": "27.3.1",
"eslint": "8.14.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-jest": "26.1.5",
"husky": "7.0.4",
"jest": "28.0.2",
"json5": "2.1.1",
"lint-staged": "9.2.5",
"node-fetch": "2.6.1",
"npm-package-arg": "6.1.0",
"prettier": "2.3.1",
"prettier": "2.6.2",
"ts-eager": "2.0.2",
"ts-jest": "27.0.4",
"turbo": "1.2.2"
"ts-jest": "28.0.0-next.1",
"turbo": "1.2.5"
},
"scripts": {
"lerna": "lerna",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "2.15.2-canary.2",
"version": "2.16.1-canary.2",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -23,14 +23,14 @@
"@types/end-of-stream": "^1.4.0",
"@types/fs-extra": "9.0.13",
"@types/glob": "^7.1.1",
"@types/jest": "27.0.1",
"@types/jest": "27.4.1",
"@types/js-yaml": "3.12.1",
"@types/ms": "0.7.31",
"@types/multistream": "2.1.1",
"@types/node-fetch": "^2.1.6",
"@types/semver": "6.0.0",
"@types/yazl": "^2.4.1",
"@vercel/frameworks": "0.7.1",
"@types/yazl": "2.4.2",
"@vercel/frameworks": "0.8.1-canary.0",
"@vercel/ncc": "0.24.0",
"aggregate-error": "3.0.1",
"async-retry": "1.2.3",
@@ -47,6 +47,6 @@
"node-fetch": "2.6.1",
"semver": "6.1.1",
"typescript": "4.3.4",
"yazl": "2.4.3"
"yazl": "2.5.1"
}
}

View File

@@ -222,6 +222,12 @@ export async function getNodeVersion(
const latest = getLatestNodeVersion();
return { ...latest, runtime: 'nodejs' };
}
if (process.env.ENABLE_EXPERIMENTAL_NODE16 === '1') {
console.warn(
'Warning: Using experimental Node.js 16.x due to ENABLE_EXPERIMENTAL_NODE16=1'
);
return { major: 16, range: '16.x', runtime: 'nodejs16.x' };
}
const { packageJson } = await scanParentDirs(destPath, true);
let { nodeVersion } = config;
let isAuto = true;
@@ -279,15 +285,17 @@ export async function scanParentDirs(
),
]);
if (packageLockJson && !hasYarnLock && !pnpmLockYaml) {
cliType = 'npm';
lockfileVersion = packageLockJson.lockfileVersion;
}
if (!packageLockJson && !hasYarnLock && pnpmLockYaml) {
// Priority order is Yarn > pnpm > npm
// - find highest priority lock file and use that
if (hasYarnLock) {
cliType = 'yarn';
} else if (pnpmLockYaml) {
cliType = 'pnpm';
// just ensure that it is read as a number and not a string
lockfileVersion = Number(pnpmLockYaml.lockfileVersion);
} else if (packageLockJson) {
cliType = 'npm';
lockfileVersion = packageLockJson.lockfileVersion;
}
// Only stop iterating if a lockfile was found, because it's possible
@@ -434,6 +442,16 @@ export function getEnvForPackageManager({
newEnv.PATH = `/node16/bin-npm7:${env.PATH}`;
console.log('Detected `package-lock.json` generated by npm 7...');
}
} else if (cliType === 'pnpm') {
if (
typeof lockfileVersion === 'number' &&
lockfileVersion === 5.4 &&
(nodeVersion?.major || 0) > 12
) {
// Ensure that pnpm 7 is at the beginning of the `$PATH`
newEnv.PATH = `/pnpm7/pnpm:${env.PATH}`;
console.log('Detected `pnpm-lock.yaml` generated by pnpm 7...');
}
} else {
// Yarn v2 PnP mode may be activated, so force "node-modules" linker style
if (!env.YARN_NODE_LINKER) {
@@ -544,7 +562,7 @@ export async function runPipInstall(
meta?: Meta
) {
if (meta && meta.isDev) {
debug('Skipping dependency installation because dev mode is enabled');
debug('Skipping dependency installation because dev mode is enabled');
return;
}

View File

@@ -33,7 +33,6 @@ import {
getDiscontinuedNodeVersions,
} from './fs/node-version';
import streamToBuffer from './fs/stream-to-buffer';
import shouldServe from './should-serve';
import debug from './debug';
import getIgnoreFilter from './get-ignore-filter';
import { getPlatformEnv } from './get-platform-env';
@@ -73,7 +72,6 @@ export {
getSpawnOptions,
getPlatformEnv,
streamToBuffer,
shouldServe,
debug,
isSymbolicLink,
getLambdaOptionsFromFunction,
@@ -94,6 +92,7 @@ export { DetectorFilesystem } from './detectors/filesystem';
export { readConfigFile } from './fs/read-config-file';
export { normalizePath } from './fs/normalize-path';
export * from './should-serve';
export * from './schemas';
export * from './types';
export * from './errors';
@@ -116,3 +115,5 @@ export const isOfficialRuntime = (desired: string, name?: string): boolean => {
export const isStaticRuntime = (name?: string): boolean => {
return isOfficialRuntime('static', name);
};
export { workspaceManagers } from './workspaces/workspace-managers';

View File

@@ -1,12 +1,12 @@
import { parse } from 'path';
import { ShouldServeOptions } from './types';
import FileFsRef from './file-fs-ref';
import type FileFsRef from './file-fs-ref';
import type { ShouldServe } from './types';
export default function shouldServe({
export const shouldServe: ShouldServe = ({
entrypoint,
files,
requestPath,
}: ShouldServeOptions): boolean {
}) => {
requestPath = requestPath.replace(/\/$/, ''); // sanitize trailing '/'
entrypoint = entrypoint.replace(/\\/, '/'); // windows compatibility
@@ -20,7 +20,7 @@ export default function shouldServe({
}
return false;
}
};
function hasProp(obj: { [path: string]: FileFsRef }, key: string): boolean {
return Object.hasOwnProperty.call(obj, key);

View File

@@ -345,6 +345,7 @@ export interface BuilderV3 {
version: 3;
build: BuildV3;
prepareCache?: PrepareCache;
shouldServe?: ShouldServe;
startDevServer?: StartDevServer;
}
@@ -401,6 +402,9 @@ export interface BuildResultV3 {
export type BuildV2 = (options: BuildOptions) => Promise<BuildResultV2>;
export type BuildV3 = (options: BuildOptions) => Promise<BuildResultV3>;
export type PrepareCache = (options: PrepareCacheOptions) => Promise<Files>;
export type ShouldServe = (
options: ShouldServeOptions
) => boolean | Promise<boolean>;
export type StartDevServer = (
options: StartDevServerOptions
) => Promise<StartDevServerResult>;

View File

@@ -0,0 +1,129 @@
import type { Framework } from '@vercel/frameworks';
/**
* The supported list of workspace managers.
*
* This list is designed to work with the @see {@link detectFramework} function.
*
* @example
* import { workspaceManagers as frameworkList } from '@vercel/build-utils/workspaces'
* import { detectFramework } from '@vercel/build-utils'
*
* const fs = new GitDetectorFilesystem(...)
* detectFramwork({ fs, frameworkList }) // returns the 'slug' field if detected, otherwise null
*
* @todo Will be used by the detect-eligible-projects API endpoint for a given git url.
*/
export const workspaceManagers: Array<Framework> = [
{
name: 'Yarn',
slug: 'yarn',
detectors: {
every: [
{
path: 'package.json',
matchContent:
'"workspaces":\\s*(?:\\[[^\\]]*]|{[^}]*"packages":[^}]*})',
},
{
path: 'yarn.lock',
},
],
},
// unused props - needed for typescript
description: '',
logo: '',
settings: {
buildCommand: {
value: '',
placeholder: '',
},
devCommand: {
value: '',
placeholder: '',
},
installCommand: {
value: '',
placeholder: '',
},
outputDirectory: {
value: '',
placeholder: '',
},
},
getOutputDirName: () => Promise.resolve(''),
},
{
name: 'pnpm',
slug: 'pnpm',
detectors: {
every: [
{
path: 'pnpm-workspace.yaml',
},
],
},
// unused props - needed for typescript
description: '',
logo: '',
settings: {
buildCommand: {
value: '',
placeholder: '',
},
devCommand: {
value: '',
placeholder: '',
},
installCommand: {
value: '',
placeholder: '',
},
outputDirectory: {
value: '',
placeholder: '',
},
},
getOutputDirName: () => Promise.resolve(''),
},
{
name: 'npm',
slug: 'npm',
detectors: {
every: [
{
path: 'package.json',
matchContent:
'"workspaces":\\s*(?:\\[[^\\]]*]|{[^}]*"packages":[^}]*})',
},
{
path: 'package-lock.json',
},
],
},
// unused props - needed for typescript
description: '',
logo: '',
settings: {
buildCommand: {
value: '',
placeholder: '',
},
devCommand: {
value: '',
placeholder: '',
},
installCommand: {
value: '',
placeholder: '',
},
outputDirectory: {
value: '',
placeholder: '',
},
},
getOutputDirName: () => Promise.resolve(''),
},
];
export default workspaceManagers;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
{
"private": "true",
"name": "25-multiple-lock-files-yarn",
"workspaces": [
"a",
"b"
],
"scripts": {
"build": "mkdir -p public && (printf \"yarn version: \" && yarn -v) > public/index.txt"
},
"dependencies": {
"once": "^1.4.0"
}
}

View File

@@ -0,0 +1,19 @@
lockfileVersion: 5.3
specifiers:
once: ^1.4.0
dependencies:
once: 1.4.0
packages:
/once/1.4.0:
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
dependencies:
wrappy: 1.0.2
dev: false
/wrappy/1.0.2:
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
dev: false

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@vercel/static-build" }],
"probes": [
{
"path": "/",
"mustContain": "yarn version: 1",
"logMustContain": "yarn run build"
}
]
}

View File

@@ -0,0 +1,15 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
wrappy "1"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=

View File

@@ -0,0 +1,44 @@
{
"name": "26-multiple-lock-files-pnpm",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "26-multiple-lock-files-pnpm",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
},
"dependencies": {
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
}
}

View File

@@ -0,0 +1,14 @@
{
"private": "true",
"name": "26-multiple-lock-files-pnpm",
"workspaces": [
"a",
"b"
],
"scripts": {
"build": "mkdir -p public && (printf \"pnpm version: \" && pnpm -v) > public/index.txt"
},
"dependencies": {
"once": "^1.4.0"
}
}

View File

@@ -0,0 +1,19 @@
lockfileVersion: 5.3
specifiers:
once: ^1.4.0
dependencies:
once: 1.4.0
packages:
/once/1.4.0:
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
dependencies:
wrappy: 1.0.2
dev: false
/wrappy/1.0.2:
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
dev: false

View File

@@ -0,0 +1,3 @@
packages:
- 'a'
- 'b'

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@vercel/static-build" }],
"probes": [
{
"path": "/",
"mustContain": "pnpm version: 6",
"logMustContain": "pnpm run build"
}
]
}

View File

@@ -0,0 +1,15 @@
{
"name": "a",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"debug": "^4.3.2"
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "b",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"cowsay": "^1.5.0"
}
}

View File

@@ -0,0 +1,9 @@
{
"name": "21-npm-workspaces",
"version": "1.0.0",
"private": true,
"workspaces": [
"a",
"b"
]
}

View File

@@ -0,0 +1,232 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
ansi-regex@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1"
integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
ansi-styles@^4.0.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
dependencies:
color-convert "^2.0.1"
camelcase@^5.0.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
cliui@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
dependencies:
string-width "^4.2.0"
strip-ansi "^6.0.0"
wrap-ansi "^6.2.0"
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
dependencies:
color-name "~1.1.4"
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
cowsay@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/cowsay/-/cowsay-1.5.0.tgz#4a2a453b8b59383c7d7a50e44d765c5de0bf615f"
integrity sha512-8Ipzr54Z8zROr/62C8f0PdhQcDusS05gKTS87xxdji8VbWefWly0k8BwGK7+VqamOrkv3eGsCkPtvlHzrhWsCA==
dependencies:
get-stdin "8.0.0"
string-width "~2.1.1"
strip-final-newline "2.0.0"
yargs "15.4.1"
debug@^4.3.2:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
find-up@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
dependencies:
locate-path "^5.0.0"
path-exists "^4.0.0"
get-caller-file@^2.0.1:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-stdin@8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53"
integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==
is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
locate-path@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
dependencies:
p-locate "^4.1.0"
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
dependencies:
p-try "^2.0.0"
p-locate@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
dependencies:
p-limit "^2.2.0"
p-try@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
require-main-filename@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
string-width@^4.1.0, string-width@^4.2.0:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@~2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
dependencies:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
dependencies:
ansi-regex "^3.0.0"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-final-newline@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
wrap-ansi@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
y18n@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
yargs-parser@^18.1.2:
version "18.1.3"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs@15.4.1:
version "15.4.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
dependencies:
cliui "^6.0.0"
decamelize "^1.2.0"
find-up "^4.1.0"
get-caller-file "^2.0.1"
require-directory "^2.1.1"
require-main-filename "^2.0.0"
set-blocking "^2.0.0"
string-width "^4.2.0"
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^18.1.2"

View File

@@ -0,0 +1,9 @@
{
"private": "true",
"scripts": {
"build": "mkdir -p public && (printf \"pnpm version: \" && pnpm -v) > public/index.txt"
},
"dependencies": {
"once": "^1.4.0"
}
}

View File

@@ -0,0 +1,19 @@
lockfileVersion: 5.4
specifiers:
once: ^1.4.0
dependencies:
once: 1.4.0
packages:
/once/1.4.0:
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
dependencies:
wrappy: 1.0.2
dev: false
/wrappy/1.0.2:
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
dev: false

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@vercel/static-build" }],
"probes": [
{
"path": "/",
"mustContain": "pnpm version: 7",
"logMustContain": "pnpm run build"
}
]
}

View File

@@ -32,6 +32,7 @@ const skipFixtures: string[] = [
'08-zero-config-middleman',
'21-npm-workspaces',
'23-pnpm-workspaces',
'27-yarn-workspaces',
];
// eslint-disable-next-line no-restricted-syntax

View File

@@ -0,0 +1,31 @@
import path from 'path';
import { detectFramework } from '../src/detect-framework';
import workspaceManagers from '../src/workspaces/workspace-managers';
import { FixtureFilesystem } from './utils/fixture-filesystem';
describe('workspace-managers', () => {
describe.each([
['npm', '21-npm-workspaces'],
['pnpm', '23-pnpm-workspaces'],
['yarn', '27-yarn-workspaces'],
['yarn', '25-multiple-lock-files-yarn'],
['pnpm', '26-multiple-lock-files-pnpm'],
[null, '22-pnpm'],
])('with detectFramework', (frameworkSlug, fixturePath) => {
const testName = frameworkSlug
? `should detect a ${frameworkSlug} workspace for ${fixturePath}`
: `should not detect framework for ${fixturePath}`;
it(testName, async () => {
const fixture = path.join(__dirname, 'fixtures', fixturePath);
const fs = new FixtureFilesystem(fixture);
const result = await detectFramework({
fs,
frameworkList: workspaceManagers,
});
expect(result).toBe(frameworkSlug);
});
});
});

View File

@@ -1,16 +1,17 @@
import assert from 'assert';
import { getEnvForPackageManager, NodeVersion } from '../src';
import { CliType } from '../src/fs/run-user-scripts';
import { getEnvForPackageManager } from '../src';
describe('Test `getEnvForPackageManager()`', () => {
const cases = [
const cases: Array<{
name: string;
args: Parameters<typeof getEnvForPackageManager>[0];
want: unknown;
}> = [
{
name: 'should do nothing to env for npm < 6 and node < 16',
args: {
cliType: 'npm' as CliType,
nodeVersion: {
major: 14,
} as NodeVersion,
cliType: 'npm',
nodeVersion: { major: 14, range: '14.x', runtime: 'nodejs14.x' },
lockfileVersion: 1,
env: {
FOO: 'bar',
@@ -23,10 +24,8 @@ describe('Test `getEnvForPackageManager()`', () => {
{
name: 'should set path if npm 7+ is detected and node < 16',
args: {
cliType: 'npm' as CliType,
nodeVersion: {
major: 14,
} as NodeVersion,
cliType: 'npm',
nodeVersion: { major: 14, range: '14.x', runtime: 'nodejs14.x' },
lockfileVersion: 2,
env: {
FOO: 'bar',
@@ -41,10 +40,8 @@ describe('Test `getEnvForPackageManager()`', () => {
{
name: 'should not set path if node is 16 and npm 7+ is detected',
args: {
cliType: 'npm' as CliType,
nodeVersion: {
major: 16,
} as NodeVersion,
cliType: 'npm',
nodeVersion: { major: 16, range: '16.x', runtime: 'nodejs16.x' },
lockfileVersion: 2,
env: {
FOO: 'bar',
@@ -59,10 +56,8 @@ describe('Test `getEnvForPackageManager()`', () => {
{
name: 'should set YARN_NODE_LINKER w/yarn if it is not already defined',
args: {
cliType: 'yarn' as CliType,
nodeVersion: {
major: 16,
} as NodeVersion,
cliType: 'yarn',
nodeVersion: { major: 16, range: '16.x', runtime: 'nodejs16.x' },
lockfileVersion: 2,
env: {
FOO: 'bar',
@@ -76,10 +71,8 @@ describe('Test `getEnvForPackageManager()`', () => {
{
name: 'should not set YARN_NODE_LINKER if it already exists',
args: {
cliType: 'yarn' as CliType,
nodeVersion: {
major: 16,
} as NodeVersion,
cliType: 'yarn',
nodeVersion: { major: 16, range: '16.x', runtime: 'nodejs16.x' },
lockfileVersion: 2,
env: {
FOO: 'bar',
@@ -91,6 +84,50 @@ describe('Test `getEnvForPackageManager()`', () => {
YARN_NODE_LINKER: 'exists',
},
},
{
name: 'should set path if pnpm 7+ is detected and Node version is greater than 12',
args: {
cliType: 'pnpm',
nodeVersion: { major: 16, range: '16.x', runtime: 'nodejs16.x' },
lockfileVersion: 5.4,
env: {
FOO: 'bar',
PATH: 'foo',
},
},
want: {
FOO: 'bar',
PATH: '/pnpm7/pnpm:foo',
},
},
{
name: 'should not set path if pnpm 7+ is detected and Node version is less than or equal to 12',
args: {
cliType: 'pnpm',
nodeVersion: { major: 12, range: '12.x', runtime: 'nodejs12.x' },
lockfileVersion: 5.4,
env: {
FOO: 'bar',
},
},
want: {
FOO: 'bar',
},
},
{
name: 'should not set path if pnpm 6 is detected',
args: {
cliType: 'pnpm',
nodeVersion: { major: 14, range: '14.x', runtime: 'nodejs14.x' },
lockfileVersion: 5.3,
env: {
FOO: 'bar',
},
},
want: {
FOO: 'bar',
},
},
];
for (const { name, want, args } of cases) {

View File

@@ -277,6 +277,16 @@ it('should not warn when package.json engines matches project setting from confi
expect(warningMessages).toStrictEqual([]);
});
it('should select nodejs16.x with ENABLE_EXPERIMENTAL_NODE16', async () => {
process.env.ENABLE_EXPERIMENTAL_NODE16 = '1';
const result = await getNodeVersion('/tmp', undefined, {}, {});
delete process.env.ENABLE_EXPERIMENTAL_NODE16;
expect(result).toEqual({ major: 16, range: '16.x', runtime: 'nodejs16.x' });
expect(warningMessages).toStrictEqual([
'Warning: Using experimental Node.js 16.x due to ENABLE_EXPERIMENTAL_NODE16=1',
]);
});
it('should get latest node version', async () => {
expect(getLatestNodeVersion()).toHaveProperty('major', 14);
});

View File

@@ -0,0 +1,35 @@
import { promises } from 'fs';
import path from 'path';
import { DetectorFilesystem } from '../../src';
const { stat, readFile } = promises;
export class FixtureFilesystem extends DetectorFilesystem {
private rootPath: string;
constructor(fixturePath: string) {
super();
this.rootPath = fixturePath;
}
async _hasPath(name: string): Promise<boolean> {
try {
const filePath = path.join(this.rootPath, name);
await stat(filePath);
return true;
} catch {
return false;
}
}
async _readFile(name: string): Promise<Buffer> {
const filePath = path.join(this.rootPath, name);
return readFile(filePath);
}
async _isFile(name: string): Promise<boolean> {
const filePath = path.join(this.rootPath, name);
return (await stat(filePath)).isFile();
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "24.1.1-canary.3",
"version": "24.2.1-canary.2",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -43,11 +43,11 @@
"node": ">= 12"
},
"dependencies": {
"@vercel/build-utils": "2.15.2-canary.2",
"@vercel/go": "1.3.3-canary.2",
"@vercel/node": "1.14.2-canary.3",
"@vercel/python": "2.2.3-canary.2",
"@vercel/ruby": "1.3.3-canary.2",
"@vercel/build-utils": "2.16.1-canary.2",
"@vercel/go": "1.4.1-canary.2",
"@vercel/node": "1.15.1-canary.2",
"@vercel/python": "2.3.1-canary.2",
"@vercel/ruby": "1.3.4-canary.2",
"update-notifier": "4.1.0"
},
"devDependencies": {
@@ -69,7 +69,7 @@
"@types/glob": "7.1.1",
"@types/http-proxy": "1.16.2",
"@types/inquirer": "7.3.1",
"@types/jest": "27.0.1",
"@types/jest": "27.4.1",
"@types/jest-expect-message": "1.0.3",
"@types/load-json-file": "2.0.7",
"@types/mime-types": "2.1.0",
@@ -90,9 +90,8 @@
"@types/update-notifier": "5.1.0",
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@vercel/client": "10.4.2-canary.2",
"@vercel/fetch-retry": "5.0.3",
"@vercel/frameworks": "0.7.1",
"@vercel/client": "11.0.1-canary.2",
"@vercel/frameworks": "0.8.1-canary.0",
"@vercel/ncc": "0.24.0",
"@zeit/fun": "0.11.2",
"@zeit/source-map-support": "0.6.2",

View File

@@ -61,6 +61,8 @@ import { getPreferredPreviewURL } from '../../util/deploy/get-preferred-preview-
import { Output } from '../../util/output';
import { help } from './args';
import { getDeploymentChecks } from '../../util/deploy/get-deployment-checks';
import parseTarget from '../../util/deploy/parse-target';
import getPrebuiltJson from '../../util/deploy/get-prebuilt-json';
export default async (client: Client) => {
const { output } = client;
@@ -155,7 +157,7 @@ export default async (client: Client) => {
}
}
const { log, debug, error, warn, isTTY } = output;
const { log, debug, error, prettyError, isTTY } = output;
const quiet = !isTTY;
@@ -181,6 +183,46 @@ export default async (client: Client) => {
);
}
// build `target`
const target = parseTarget(output, argv['--target'], argv['--prod']);
if (typeof target === 'number') {
return target;
}
// build `--prebuilt`
if (argv['--prebuilt']) {
const prebuiltExists = await fs.pathExists(join(path, '.vercel/output'));
if (!prebuiltExists) {
error(
`The ${param(
'--prebuilt'
)} option was used, but no prebuilt output found in ".vercel/output". Run ${getCommandName(
'build'
)} to generate a local build.`
);
return 1;
}
const prebuiltBuild = await getPrebuiltJson(path);
const assumedTarget = target || 'preview';
if (prebuiltBuild?.target && prebuiltBuild.target !== assumedTarget) {
let specifyTarget = '';
if (prebuiltBuild.target === 'production') {
specifyTarget = ` --prod`;
}
prettyError({
message: `The ${param(
'--prebuilt'
)} option was used with the target environment "${assumedTarget}", but the prebuilt output found in ".vercel/output" was built with target environment "${
prebuiltBuild.target
}". Please run ${getCommandName(`--prebuilt${specifyTarget}`)}.`,
link: 'https://vercel.link/prebuilt-environment-mismatch',
});
return 1;
}
}
// retrieve `project` and `org` from .vercel
const link = await getLinkedProject(client, path);
@@ -195,14 +237,6 @@ export default async (client: Client) => {
let sourceFilesOutsideRootDirectory: boolean | undefined = true;
if (status === 'not_linked') {
// In the future this will need to be implemented in both the CLI and vercel.com/new at the same time
if (localConfig?.projectSettings) {
output.error(
'Unexpected property detected in vercel.json: "projectSettings"'
);
return 1;
}
const shouldStartSetup =
autoConfirm ||
(await confirm(
@@ -411,33 +445,6 @@ export default async (client: Client) => {
.filter(Boolean);
const regions = regionFlag.length > 0 ? regionFlag : localConfig.regions;
// build `target`
let target;
if (argv['--target']) {
const deprecatedTarget = argv['--target'];
if (!['staging', 'production'].includes(deprecatedTarget)) {
error(
`The specified ${param('--target')} ${code(
deprecatedTarget
)} is not valid`
);
return 1;
}
if (deprecatedTarget === 'production') {
warn(
'We recommend using the much shorter `--prod` option instead of `--target production` (deprecated)'
);
}
output.debug(`Setting target to ${deprecatedTarget}`);
target = deprecatedTarget;
} else if (argv['--prod']) {
output.debug('Setting target to production');
target = 'production';
}
const currentTeam = org?.type === 'team' ? org.id : undefined;
const now = new Now({
client,
@@ -468,10 +475,7 @@ export default async (client: Client) => {
if (!localConfig.builds || localConfig.builds.length === 0) {
// Only add projectSettings for zero config deployments
createArgs.projectSettings = {
...localConfig.projectSettings,
sourceFilesOutsideRootDirectory,
};
createArgs.projectSettings = { sourceFilesOutsideRootDirectory };
}
deployment = await createDeploy(
@@ -503,10 +507,7 @@ export default async (client: Client) => {
);
// deploy again, but send projectSettings this time
createArgs.projectSettings = {
...settings,
...localConfig.projectSettings,
};
createArgs.projectSettings = settings;
deployStamp = stamp();
createArgs.deployStamp = deployStamp;

View File

@@ -10,6 +10,8 @@ import { ProjectSettings } from '../../types';
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
import setupAndLink from '../../util/link/setup-and-link';
import getSystemEnvValues from '../../util/env/get-system-env-values';
import { getCommandName } from '../../util/pkg-name';
import param from '../../util/output/param';
type Options = {
'--listen': string;
@@ -46,6 +48,13 @@ export default async function dev(
}
if (link.status === 'error') {
if (link.reason === 'HEADLESS') {
client.output.error(
`Command ${getCommandName(
'dev'
)} requires confirmation. Use option ${param('--confirm')} to confirm.`
);
}
return link.exitCode;
}

View File

@@ -1,5 +1,6 @@
import chalk from 'chalk';
import { closeSync, openSync, promises, readSync } from 'fs';
import { outputFile } from 'fs-extra';
import { closeSync, openSync, readSync } from 'fs';
import { resolve } from 'path';
import { Project, ProjectEnvTarget } from '../../types';
import Client from '../../util/client';
@@ -12,7 +13,6 @@ import { Output } from '../../util/output';
import param from '../../util/output/param';
import stamp from '../../util/output/stamp';
import { getCommandName } from '../../util/pkg-name';
const { writeFile } = promises;
const CONTENTS_PREFIX = '# Created by Vercel CLI\n';
@@ -111,7 +111,7 @@ export default async function pull(
.join('\n') +
'\n';
await writeFile(fullPath, contents, 'utf8');
await outputFile(fullPath, contents, 'utf8');
output.print(
`${prependEmoji(

View File

@@ -4,6 +4,8 @@ import getArgs from '../../util/get-args';
import logo from '../../util/output/logo';
import { getPkgName } from '../../util/pkg-name';
import setupAndLink from '../../util/link/setup-and-link';
import { getCommandName } from '../../util/pkg-name';
import param from '../../util/output/param';
const help = () => {
console.log(`
@@ -67,6 +69,13 @@ export default async function main(client: Client) {
});
if (link.status === 'error') {
if (link.reason === 'HEADLESS') {
client.output.error(
`Command ${getCommandName(
'link'
)} requires confirmation. Use option ${param('--confirm')} to confirm.`
);
}
return link.exitCode;
} else if (link.status === 'not_linked') {
// User aborted project linking questions

View File

@@ -15,7 +15,8 @@ import {
} from '../util/projects/link';
import { writeProjectSettings } from '../util/projects/project-settings';
import envPull from './env/pull';
import { getCommandName } from '../util/pkg-name';
import param from '../util/output/param';
import type { Project, Org } from '../types';
import {
isValidEnvTarget,
@@ -102,6 +103,13 @@ async function ensureLink(
}
if (link.status === 'error') {
if (link.reason === 'HEADLESS') {
client.output.error(
`Command ${getCommandName(
'pull'
)} requires confirmation. Use option ${param('--yes')} to confirm.`
);
}
return link.exitCode;
}

View File

@@ -82,7 +82,12 @@ let debug: (s: string) => void = () => {};
let apiUrl = 'https://api.vercel.com';
const main = async () => {
const { isTTY } = process.stdout;
let { isTTY } = process.stdout;
if (process.env.FORCE_TTY === '1') {
isTTY = true;
process.stdout.isTTY = true;
process.stdin.isTTY = true;
}
let argv;
@@ -585,73 +590,73 @@ const main = async () => {
let func: any;
switch (targetCommand) {
case 'alias':
func = await import('./commands/alias');
func = require('./commands/alias').default;
break;
case 'billing':
func = await import('./commands/billing');
func = require('./commands/billing').default;
break;
case 'bisect':
func = await import('./commands/bisect');
func = require('./commands/bisect').default;
break;
case 'certs':
func = await import('./commands/certs');
func = require('./commands/certs').default;
break;
case 'deploy':
func = await import('./commands/deploy');
func = require('./commands/deploy').default;
break;
case 'dev':
func = await import('./commands/dev');
func = require('./commands/dev').default;
break;
case 'dns':
func = await import('./commands/dns');
func = require('./commands/dns').default;
break;
case 'domains':
func = await import('./commands/domains');
func = require('./commands/domains').default;
break;
case 'env':
func = await import('./commands/env');
func = require('./commands/env').default;
break;
case 'init':
func = await import('./commands/init');
func = require('./commands/init').default;
break;
case 'inspect':
func = await import('./commands/inspect');
func = require('./commands/inspect').default;
break;
case 'link':
func = await import('./commands/link');
func = require('./commands/link').default;
break;
case 'list':
func = await import('./commands/list');
func = require('./commands/list').default;
break;
case 'logs':
func = await import('./commands/logs');
func = require('./commands/logs').default;
break;
case 'login':
func = await import('./commands/login');
func = require('./commands/login').default;
break;
case 'logout':
func = await import('./commands/logout');
func = require('./commands/logout').default;
break;
case 'projects':
func = await import('./commands/projects');
func = require('./commands/projects').default;
break;
case 'pull':
func = await import('./commands/pull');
func = require('./commands/pull').default;
break;
case 'remove':
func = await import('./commands/remove');
func = require('./commands/remove').default;
break;
case 'secrets':
func = await import('./commands/secrets');
func = require('./commands/secrets').default;
break;
case 'teams':
func = await import('./commands/teams');
func = require('./commands/teams').default;
break;
case 'update':
func = await import('./commands/update');
func = require('./commands/update').default;
break;
case 'whoami':
func = await import('./commands/whoami');
func = require('./commands/whoami').default;
break;
default:
func = null;

View File

@@ -277,7 +277,17 @@ export interface PaginationOptions {
export type ProjectLinkResult =
| { status: 'linked'; org: Org; project: Project }
| { status: 'not_linked'; org: null; project: null }
| { status: 'error'; exitCode: number };
| {
status: 'error';
exitCode: number;
reason?:
| 'HEADLESS'
| 'NOT_AUTHORIZED'
| 'TEAM_DELETED'
| 'PATH_IS_FILE'
| 'INVALID_ROOT_DIRECTORY'
| 'MISSING_PROJECT_SETTINGS';
};
export interface Token {
id: string;

View File

@@ -0,0 +1,12 @@
import fs from 'fs-extra';
import { join } from 'path';
export default async function getPrebuiltJson(directory: string) {
try {
return await fs.readJSON(join(directory, '.vercel/output/builds.json'));
} catch (error) {
// ignoring error
}
return null;
}

View File

@@ -0,0 +1,41 @@
import { Output } from '../../util/output';
import param from '../../util/output/param';
import code from '../../util/output/code';
/**
* Parses the environment target from the `--target` and `--prod` flags.
*/
export default function parseTarget(
output: Output,
targetArg?: string,
prodArg?: boolean
): string | number | undefined {
if (targetArg) {
const deprecatedTarget = targetArg;
if (!['staging', 'production'].includes(deprecatedTarget)) {
output.error(
`The specified ${param('--target')} ${code(
deprecatedTarget
)} is not valid`
);
return 1;
}
if (deprecatedTarget === 'production') {
output.warn(
'We recommend using the much shorter `--prod` option instead of `--target production` (deprecated)'
);
}
output.debug(`Setting target to ${deprecatedTarget}`);
return deprecatedTarget;
}
if (prodArg) {
output.debug('Setting target to production');
return 'production';
}
return undefined;
}

View File

@@ -6,6 +6,8 @@ import {
getLinkedProject,
linkFolderToProject,
getVercelDirectory,
VERCEL_DIR_README,
VERCEL_DIR_PROJECT,
} from '../projects/link';
import createProject from '../projects/create-project';
import updateProject from '../projects/update-project';
@@ -49,7 +51,7 @@ export default async function setupAndLink(
const isFile = !isDirectory(path);
if (isFile) {
output.error(`Expected directory but found file: ${path}`);
return { status: 'error', exitCode: 1 };
return { status: 'error', exitCode: 1, reason: 'PATH_IS_FILE' };
}
const link = await getLinkedProject(client, path);
const isTTY = process.stdout.isTTY;
@@ -65,7 +67,12 @@ export default async function setupAndLink(
if (forceDelete) {
const vercelDir = getVercelDirectory(path);
remove(vercelDir);
remove(join(vercelDir, VERCEL_DIR_README));
remove(join(vercelDir, VERCEL_DIR_PROJECT));
}
if (!isTTY && !autoConfirm) {
return { status: 'error', exitCode: 1, reason: 'HEADLESS' };
}
const shouldStartSetup =
@@ -87,9 +94,14 @@ export default async function setupAndLink(
autoConfirm
);
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
if (err.code === 'NOT_AUTHORIZED') {
output.prettyError(err);
return { status: 'error', exitCode: 1 };
return { status: 'error', exitCode: 1, reason: 'NOT_AUTHORIZED' };
}
if (err.code === 'TEAM_DELETED') {
output.prettyError(err);
return { status: 'error', exitCode: 1, reason: 'TEAM_DELETED' };
}
throw err;
@@ -135,7 +147,7 @@ export default async function setupAndLink(
rootDirectory &&
!(await validateRootDirectory(output, path, sourcePath, ''))
) {
return { status: 'error', exitCode: 1 };
return { status: 'error', exitCode: 1, reason: 'INVALID_ROOT_DIRECTORY' };
}
config.currentTeam = org.type === 'team' ? org.id : undefined;
@@ -191,7 +203,11 @@ export default async function setupAndLink(
if (debug) {
console.log(deployment);
}
return { status: 'error', exitCode: 1 };
return {
status: 'error',
exitCode: 1,
reason: 'MISSING_PROJECT_SETTINGS',
};
}
const { projectSettings, framework } = deployment;

View File

@@ -1,4 +1,4 @@
import { writeFile } from 'fs-extra';
import { outputJSON } from 'fs-extra';
import { Org, Project, ProjectLink } from '../../types';
import { getLinkFromDir, VERCEL_DIR, VERCEL_DIR_PROJECT } from './link';
import { join } from 'path';
@@ -22,21 +22,22 @@ export async function writeProjectSettings(
project: Project,
org: Org
) {
return await writeFile(
join(cwd, VERCEL_DIR, VERCEL_DIR_PROJECT),
JSON.stringify({
projectId: project.id,
orgId: org.id,
settings: {
buildCommand: project.buildCommand,
devCommand: project.devCommand,
outputDirectory: project.outputDirectory,
directoryListing: project.directoryListing,
rootDirectory: project.rootDirectory,
framework: project.framework,
},
})
);
const data = {
projectId: project.id,
orgId: org.id,
settings: {
buildCommand: project.buildCommand,
devCommand: project.devCommand,
outputDirectory: project.outputDirectory,
directoryListing: project.directoryListing,
rootDirectory: project.rootDirectory,
framework: project.framework,
},
};
const path = join(cwd, VERCEL_DIR, VERCEL_DIR_PROJECT);
return await outputJSON(path, data, {
spaces: 2,
});
}
export async function readProjectSettings(cwd: string) {

View File

@@ -2,6 +2,10 @@ import { join } from 'path';
import { fileNameSymbol } from '@vercel/client';
import { client } from '../mocks/client';
import deploy from '../../src/commands/deploy';
import { setupFixture } from '../helpers/setup-fixture';
import { defaultProject, useProject } from '../mocks/project';
import { useTeams } from '../mocks/team';
import { useUser } from '../mocks/user';
describe('deploy', () => {
it('should reject deploying a single file', async () => {
@@ -31,6 +35,59 @@ describe('deploy', () => {
);
});
it('should reject deploying a directory that does not contain ".vercel/output" when `--prebuilt` is used', async () => {
client.setArgv('deploy', __dirname, '--prebuilt');
const exitCode = await deploy(client);
expect(exitCode).toEqual(1);
expect(client.outputBuffer).toEqual(
'Error! The "--prebuilt" option was used, but no prebuilt output found in ".vercel/output". Run `vercel build` to generate a local build.\n'
);
});
it('should reject deploying a directory that was built with a different target environment when `--prebuilt --prod` is used on "preview" output', async () => {
const cwd = setupFixture('build-output-api-preview');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'build-output-api-preview',
name: 'build-output-api-preview',
});
client.setArgv('deploy', cwd, '--prebuilt', '--prod');
const exitCode = await deploy(client);
expect(exitCode).toEqual(1);
expect(client.outputBuffer).toEqual(
'Error! The "--prebuilt" option was used with the target environment "production",' +
' but the prebuilt output found in ".vercel/output" was built with target environment "preview".' +
' Please run `vercel --prebuilt`.\n' +
'Learn More: https://vercel.link/prebuilt-environment-mismatch\n'
);
});
it('should reject deploying a directory that was built with a different target environment when `--prebuilt` is used on "production" output', async () => {
const cwd = setupFixture('build-output-api-production');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'build-output-api-preview',
name: 'build-output-api-preview',
});
client.setArgv('deploy', cwd, '--prebuilt');
const exitCode = await deploy(client);
expect(exitCode).toEqual(1);
expect(client.outputBuffer).toEqual(
'Error! The "--prebuilt" option was used with the target environment "preview",' +
' but the prebuilt output found in ".vercel/output" was built with target environment "production".' +
' Please run `vercel --prebuilt --prod`.\n' +
'Learn More: https://vercel.link/prebuilt-environment-mismatch\n'
);
});
it('should reject deploying "version: 1"', async () => {
client.setArgv('deploy');
client.localConfig = {

View File

@@ -17,7 +17,7 @@ describe('pull', () => {
id: 'vercel-pull-next',
name: 'vercel-pull-next',
});
client.setArgv('pull', '--yes', cwd);
client.setArgv('pull', cwd);
const exitCode = await pull(client);
expect(exitCode, client.outputBuffer).toEqual(0);
@@ -28,6 +28,76 @@ describe('pull', () => {
expect(devFileHasDevEnv).toBeTruthy();
});
it('should fail with message to pull without a link and without --env', async () => {
try {
process.stdout.isTTY = undefined;
const cwd = setupFixture('vercel-pull-unlinked');
useUser();
useTeams('team_dummy');
client.setArgv('pull', cwd);
const exitCode = await pull(client);
expect(exitCode, client.outputBuffer).toEqual(1);
expect(client.outputBuffer).toMatch(
/Command `vercel pull` requires confirmation. Use option "--yes" to confirm./gm
);
} finally {
process.stdout.isTTY = true;
}
});
it('should fail without message to pull without a link and with --env', async () => {
const cwd = setupFixture('vercel-pull-next');
useUser();
useTeams('team_dummy');
client.setArgv('pull', cwd, '--yes');
const exitCode = await pull(client);
expect(exitCode, client.outputBuffer).toEqual(1);
expect(client.outputBuffer).not.toMatch(
/Command `vercel pull` requires confirmation. Use option "--yes" to confirm./gm
);
});
it('should handle pulling with env vars (headless mode)', async () => {
try {
process.env.VERCEL_PROJECT_ID = 'vercel-pull-next';
process.env.VERCEL_ORG_ID = 'team_dummy';
const cwd = setupFixture('vercel-pull-next');
// Remove the `.vercel` dir to ensure that the `pull`
// command creates a new one based on env vars
await fs.remove(path.join(cwd, '.vercel'));
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-pull-next',
name: 'vercel-pull-next',
});
client.setArgv('pull', cwd);
const exitCode = await pull(client);
expect(exitCode, client.outputBuffer).toEqual(0);
const config = await fs.readJSON(path.join(cwd, '.vercel/project.json'));
expect(config).toMatchInlineSnapshot(`
Object {
"orgId": "team_dummy",
"projectId": "vercel-pull-next",
"settings": Object {},
}
`);
} finally {
delete process.env.VERCEL_PROJECT_ID;
delete process.env.VERCEL_ORG_ID;
}
});
it('should handle --environment=preview flag', async () => {
const cwd = setupFixture('vercel-pull-next');
useUser();
@@ -37,7 +107,7 @@ describe('pull', () => {
id: 'vercel-pull-next',
name: 'vercel-pull-next',
});
client.setArgv('pull', '--yes', '--environment=preview', cwd);
client.setArgv('pull', '--environment=preview', cwd);
const exitCode = await pull(client);
expect(exitCode).toEqual(0);
@@ -59,7 +129,7 @@ describe('pull', () => {
id: 'vercel-pull-next',
name: 'vercel-pull-next',
});
client.setArgv('pull', '--yes', '--environment=production', cwd);
client.setArgv('pull', '--environment=production', cwd);
const exitCode = await pull(client);
expect(exitCode).toEqual(0);

View File

@@ -15,7 +15,7 @@ cache:
env:
global:
# See https://git.io/vdao3 for details.
# See https://github.com/ember-cli/ember-cli/blob/master/docs/build-concurrency.md
- JOBS=1
script:

View File

@@ -0,0 +1,3 @@
{
"target": "preview"
}

View File

@@ -0,0 +1,3 @@
{
"target": "production"
}

View File

@@ -0,0 +1,2 @@
.next
yarn.lock

View File

@@ -0,0 +1,12 @@
{
"scripts": {
"build": "next build",
"dev": "next",
"now-build": "next build"
},
"dependencies": {
"next": "^8.0.0",
"react": "^16.7.0",
"react-dom": "^16.7.0"
}
}

View File

@@ -0,0 +1,11 @@
import { withRouter } from 'next/router';
function Index({ router }) {
const data = {
pathname: router.pathname,
query: router.query,
};
return <div>{JSON.stringify(data)}</div>;
}
export default withRouter(Index);

View File

@@ -0,0 +1 @@
User-Agent: *

View File

@@ -0,0 +1,10 @@
{
"version": 2,
"name": "vercel-pull-next",
"routes": [
{
"src": "/(.*)",
"dest": "/index?route-param=b"
}
]
}

View File

@@ -179,6 +179,19 @@ module.exports = async function prepare(session, binaryPath) {
'list/README.md':
'readme contents for deploy-default-with-conflicting-sub-directory',
},
'deploy-default-with-prebuilt-preview': {
'vercel.json': JSON.stringify({ version: 2 }),
'.vercel/output/builds.json': JSON.stringify({ target: 'preview' }),
'.vercel/output/config.json': JSON.stringify({ version: 3 }),
'.vercel/output/static/README.md':
'readme contents for deploy-default-with-prebuilt-preview',
},
'build-output-api-raw': {
'vercel.json': JSON.stringify({ version: 2 }),
'.vercel/output/config.json': JSON.stringify({ version: 3 }),
'.vercel/output/static/README.md':
'readme contents for build-output-api-raw',
},
'local-config-v2': {
[`main-${session}.html`]: '<h1>hello main</h1>',
[`test-${session}.html`]: '<h1>hello test</h1>',
@@ -413,10 +426,6 @@ module.exports = async function prepare(session, binaryPath) {
projectId: 'QmRoBYhejkkmssotLZr8tWgewPdPcjYucYUNERFbhJrRNi',
}),
},
'project-settings-overrides': {
'vercel.json': '{}',
'package.json': '{}',
},
};
for (const [typeName, needed] of Object.entries(spec)) {

View File

@@ -7,10 +7,9 @@ import { Readable } from 'stream';
import { homedir } from 'os';
import _execa from 'execa';
import XDGAppPaths from 'xdg-app-paths';
import nodeFetch from 'node-fetch';
import fetch from 'node-fetch';
import tmp from 'tmp-promise';
import retry from 'async-retry';
import createFetchRetry from '@vercel/fetch-retry';
import fs, {
writeFile,
readFile,
@@ -18,7 +17,6 @@ import fs, {
copy,
ensureDir,
exists,
mkdir,
} from 'fs-extra';
import logo from '../src/util/output/logo';
import sleep from '../src/util/sleep';
@@ -26,8 +24,6 @@ import pkg from '../package';
import prepareFixtures from './helpers/prepare';
import { fetchTokenWithRetry } from '../../../test/lib/deployment/now-deploy';
const fetch = createFetchRetry(nodeFetch);
// log command when running `execa`
function execa(file, args, options) {
console.log(`$ vercel ${args.join(' ')}`);
@@ -498,6 +494,56 @@ test('default command should work with --cwd option', async t => {
);
});
test('should allow deploying a directory that was built with a target environment of "preview" and `--prebuilt` is used without specifying a target', async t => {
const projectDir = fixture('deploy-default-with-prebuilt-preview');
await vcLink(t, projectDir);
const { exitCode, stderr, stdout } = await execa(
binaryPath,
[
// omit the default "deploy" command
'--prebuilt',
...defaultArgs,
],
{
cwd: projectDir,
}
);
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
const url = stdout;
const deploymentResult = await fetch(`${url}/README.md`);
const body = await deploymentResult.text();
t.deepEqual(body, 'readme contents for deploy-default-with-prebuilt-preview');
});
test('should allow deploying a directory that was prebuilt, but has no builds.json', async t => {
const projectDir = fixture('build-output-api-raw');
await vcLink(t, projectDir);
const { exitCode, stderr, stdout } = await execa(
binaryPath,
[
// omit the default "deploy" command
'--prebuilt',
...defaultArgs,
],
{
cwd: projectDir,
}
);
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
const url = stdout;
const deploymentResult = await fetch(`${url}/README.md`);
const body = await deploymentResult.text();
t.deepEqual(body, 'readme contents for build-output-api-raw');
});
test('deploy using only now.json with `redirects` defined', async t => {
const target = fixture('redirects-v2');
@@ -798,15 +844,10 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
const contents = fs.readFileSync(path.join(target, '.env'), 'utf8');
t.regex(contents, /^# Created by Vercel CLI\n/);
const lines = new Set(contents.split('\n'));
t.true(lines.has('MY_NEW_ENV_VAR="my plaintext value"'), 'MY_NEW_ENV_VAR');
t.true(lines.has('MY_STDIN_VAR="{"expect":"quotes"}"'), 'MY_STDIN_VAR');
t.true(
lines.has('MY_DECRYPTABLE_SECRET_ENV="decryptable value"'),
'MY_DECRYPTABLE_SECRET_ENV'
);
t.false(lines.has('MY_PREVIEW'), 'MY_PREVIEW');
t.regex(contents, /MY_NEW_ENV_VAR="my plaintext value"/);
t.regex(contents, /MY_STDIN_VAR="{"expect":"quotes"}"/);
t.regex(contents, /MY_DECRYPTABLE_SECRET_ENV="decryptable value"/);
t.notRegex(contents, /MY_PREVIEW/);
}
async function vcEnvPullOverwrite() {
@@ -1091,29 +1132,48 @@ test('Deploy `api-env` fixture and test `vercel env` command', async t => {
t.is(exitCode, 0, formatOutput({ stderr, stdout }));
}
await vcLink();
await vcEnvLsIsEmpty();
await vcEnvAddWithPrompts();
await vcEnvAddFromStdin();
await vcEnvAddFromStdinPreview();
await vcEnvAddFromStdinPreviewWithBranch();
await vcEnvLsIncludesVar();
await createEnvWithDecryptableSecret();
await vcEnvPull();
await vcEnvPullOverwrite();
await vcEnvPullConfirm();
await vcDeployWithVar();
await vcDevWithEnv();
fs.unlinkSync(path.join(target, '.env'));
await vcDevAndFetchCloudVars();
await enableAutoExposeSystemEnvs();
await vcEnvPullFetchSystemVars();
fs.unlinkSync(path.join(target, '.env'));
await vcDevAndFetchSystemVars();
await vcEnvRemove();
await vcEnvRemoveWithArgs();
await vcEnvRemoveWithNameOnly();
await vcEnvLsIsEmpty();
function vcEnvRemoveByName(name) {
return execa(binaryPath, ['env', 'rm', name, '-y', ...defaultArgs], {
reject: false,
cwd: target,
});
}
async function vcEnvRemoveAll() {
await vcEnvRemoveByName('MY_PREVIEW');
await vcEnvRemoveByName('MY_STDIN_VAR');
await vcEnvRemoveByName('MY_DECRYPTABLE_SECRET_ENV');
await vcEnvRemoveByName('MY_NEW_ENV_VAR');
}
try {
await vcEnvRemoveAll();
await vcLink();
await vcEnvLsIsEmpty();
await vcEnvAddWithPrompts();
await vcEnvAddFromStdin();
await vcEnvAddFromStdinPreview();
await vcEnvAddFromStdinPreviewWithBranch();
await vcEnvLsIncludesVar();
await createEnvWithDecryptableSecret();
await vcEnvPull();
await vcEnvPullOverwrite();
await vcEnvPullConfirm();
await vcDeployWithVar();
await vcDevWithEnv();
fs.unlinkSync(path.join(target, '.env'));
await vcDevAndFetchCloudVars();
await enableAutoExposeSystemEnvs();
await vcEnvPullFetchSystemVars();
fs.unlinkSync(path.join(target, '.env'));
await vcDevAndFetchSystemVars();
await vcEnvRemove();
await vcEnvRemoveWithArgs();
await vcEnvRemoveWithNameOnly();
await vcEnvLsIsEmpty();
} finally {
await vcEnvRemoveAll();
}
});
test('[vc projects] should create a project successfully', async t => {
@@ -1451,6 +1511,9 @@ test('try to purchase a domain', async t => {
{
reject: false,
input: stream,
env: {
FORCE_TTY: '1',
},
}
);
@@ -2149,11 +2212,6 @@ const verifyExampleAngular = (cwd, dir) =>
fs.existsSync(path.join(cwd, dir, 'tsconfig.json')) &&
fs.existsSync(path.join(cwd, dir, 'angular.json'));
const verifyExampleAmp = (cwd, dir) =>
fs.existsSync(path.join(cwd, dir, 'index.html')) &&
fs.existsSync(path.join(cwd, dir, 'logo.png')) &&
fs.existsSync(path.join(cwd, dir, 'favicon.png'));
test('initialize example "angular"', async t => {
tmpDir = tmp.dirSync({ unsafeCleanup: true });
const cwd = tmpDir.name;
@@ -2188,21 +2246,6 @@ test('initialize example ("angular") to specified directory', async t => {
t.true(verifyExampleAngular(cwd, 'ang'), formatOutput({ stdout, stderr }));
});
test('initialize selected example ("amp")', async t => {
tmpDir = tmp.dirSync({ unsafeCleanup: true });
const cwd = tmpDir.name;
const goal = '> Success! Initialized "amp" example in';
const { stdout, stderr, exitCode } = await execute(['init'], {
cwd,
input: '\n',
});
t.is(exitCode, 0, formatOutput({ stdout, stderr }));
t.true(stderr.includes(goal), formatOutput({ stdout, stderr }));
t.true(verifyExampleAmp(cwd, 'amp'), formatOutput({ stdout, stderr }));
});
test('initialize example to existing directory with "-f"', async t => {
tmpDir = tmp.dirSync({ unsafeCleanup: true });
const cwd = tmpDir.name;
@@ -2382,6 +2425,9 @@ test('[vercel dev] fails when development commad calls vercel dev recursively',
const dev = execa(binaryPath, ['dev', ...defaultArgs], {
cwd: dir,
reject: false,
env: {
FORCE_TTY: '1',
},
});
await setupProject(dev, projectName, {
@@ -2787,6 +2833,9 @@ test('change user', async t => {
await execute(['login', email, '--api', loginApiUrl, '--debug'], {
stdio: 'inherit',
env: {
FORCE_TTY: '1',
},
});
const auth = await fs.readJSON(getConfigAuthPath());
@@ -2916,7 +2965,11 @@ test('should prefill "project name" prompt with folder name', async t => {
const directory = path.join(src, '../', projectName);
await copy(src, directory);
const now = execa(binaryPath, [directory, ...defaultArgs]);
const now = execa(binaryPath, [directory, ...defaultArgs], {
env: {
FORCE_TTY: '1',
},
});
await waitForPrompt(now, chunk => /Set up and deploy [^?]+\?/.test(chunk));
now.stdin.write('yes\n');
@@ -2959,12 +3012,15 @@ test('should prefill "project name" prompt with --name', async t => {
// remove previously linked project if it exists
await remove(path.join(directory, '.vercel'));
const now = execa(binaryPath, [
directory,
'--name',
projectName,
...defaultArgs,
]);
const now = execa(
binaryPath,
[directory, '--name', projectName, ...defaultArgs],
{
env: {
FORCE_TTY: '1',
},
}
);
let isDeprecated = false;
@@ -3023,7 +3079,11 @@ test('should prefill "project name" prompt with now.json `name`', async t => {
})
);
const now = execa(binaryPath, [directory, ...defaultArgs]);
const now = execa(binaryPath, [directory, ...defaultArgs], {
env: {
FORCE_TTY: '1',
},
});
let isDeprecated = false;
@@ -3444,7 +3504,12 @@ test('[vc link] should show prompts to set up project', async t => {
// remove previously linked project if it exists
await remove(path.join(dir, '.vercel'));
const vc = execa(binaryPath, ['link', ...defaultArgs], { cwd: dir });
const vc = execa(binaryPath, ['link', ...defaultArgs], {
cwd: dir,
env: {
FORCE_TTY: '1',
},
});
await setupProject(vc, projectName, {
buildCommand: `mkdir -p o && echo '<h1>custom hello</h1>' > o/index.html`,
@@ -3517,7 +3582,13 @@ test('[vc link] should not duplicate paths in .gitignore', async t => {
const { exitCode, stderr, stdout } = await execa(
binaryPath,
['link', '--confirm', ...defaultArgs],
{ cwd: dir, reject: false }
{
cwd: dir,
reject: false,
env: {
FORCE_TTY: '1',
},
}
);
// Ensure the exit code is right
@@ -3543,6 +3614,9 @@ test('[vc dev] should show prompts to set up project', async t => {
const dev = execa(binaryPath, ['dev', '--listen', port, ...defaultArgs], {
cwd: dir,
env: {
FORCE_TTY: '1',
},
});
await setupProject(dev, projectName, {
@@ -3587,7 +3661,12 @@ test('[vc link] should show project prompts but not framework when `builds` defi
// remove previously linked project if it exists
await remove(path.join(dir, '.vercel'));
const vc = execa(binaryPath, ['link', ...defaultArgs], { cwd: dir });
const vc = execa(binaryPath, ['link', ...defaultArgs], {
cwd: dir,
env: {
FORCE_TTY: '1',
},
});
await waitForPrompt(vc, chunk => /Set up [^?]+\?/.test(chunk));
vc.stdin.write('yes\n');
@@ -3646,6 +3725,9 @@ test('[vc dev] should send the platform proxy request headers to frontend dev se
const dev = execa(binaryPath, ['dev', '--listen', port, ...defaultArgs], {
cwd: dir,
env: {
FORCE_TTY: '1',
},
});
await setupProject(dev, projectName, {
@@ -3682,127 +3764,3 @@ test('[vc link] should support the `--project` flag', async t => {
formatOutput(output)
);
});
test('vercel.json projectSettings overrides', async t => {
// create project directory and get path to vercel.json
const projectName = 'project-settings-overrides';
const directory = fixture(projectName);
const vercelJsonPath = path.join(directory, 'vercel.json');
async function deploy(expectedStatusCode = 0) {
// deploy and assert deployment is successful
const deployment = await execa(
binaryPath,
[directory, ...defaultArgs, '--public', '--confirm'],
{ reject: false }
);
t.is(
deployment.exitCode,
expectedStatusCode,
formatOutput({
stderr: deployment.stderr,
stdout: deployment.stdout,
})
);
return deployment;
}
// Test that deployment fails with override settings on new deployments
await writeFile(
vercelJsonPath,
JSON.stringify({
projectSettings: {
outputDirectory: 'output',
},
})
);
let deployment = await deploy(1);
t.true(
deployment.stderr.includes(
'Error! Unexpected property detected in vercel.json: "projectSettings"'
)
);
// Make sure vercel.json is empty for first deployment
await writeFile(vercelJsonPath, JSON.stringify({}));
deployment = await deploy();
// create and write override project settings
const BUILD_COMMAND =
'mkdir output && echo "Hello, World 1" >> output/index.txt';
const OUTPUT_DIRECTORY = 'output';
await writeFile(
vercelJsonPath,
JSON.stringify({
projectSettings: {
buildCommand: BUILD_COMMAND,
outputDirectory: OUTPUT_DIRECTORY,
},
})
);
deployment = await deploy();
// assert the command were executed
let page = await fetch(deployment.stdout);
let text = await page.text();
t.is(text, `Hello, World 1\n`);
// Failure Case
await writeFile(
vercelJsonPath,
JSON.stringify({
projectSettings: {
invalidProjectSetting: 'invalid project setting',
},
})
);
// Deployment should fail
deployment = await deploy(1);
t.true(
deployment.stderr.includes(
'Error! Invalid request: `projectSettings` should NOT have additional property `invalidProjectSetting`.'
)
);
// Test Next.js Framework deployment
await mkdir(`${directory}/pages`);
await writeFile(
`${directory}/pages/index.js`,
`export default () => 'Hello, World 2'`
);
await writeFile(
vercelJsonPath,
JSON.stringify({
projectSettings: {
framework: 'nextjs',
},
})
);
await writeFile(
`${directory}/package.json`,
JSON.stringify({
scripts: {
dev: 'next',
start: 'next start',
build: 'next build',
},
dependencies: {
next: 'latest',
react: 'latest',
'react-dom': 'latest',
},
})
);
deployment = await deploy();
page = await fetch(deployment.stdout);
text = await page.text();
t.true(text.includes(`Hello, World 2`));
});

View File

@@ -152,6 +152,7 @@ export function useProject(project: Partial<Project> = defaultProject) {
if (typeof target === 'string') {
const targetEnvs = envs.filter(env => env.target.includes(target));
res.json({ envs: targetEnvs });
return;
}
res.json({ envs });

View File

@@ -0,0 +1,55 @@
import parseTarget from '../../../src/util/deploy/parse-target';
import { Output } from '../../../src/util/output';
describe('parseTarget', () => {
let output: Output;
beforeEach(() => {
output = new Output();
output.warn = jest.fn();
output.error = jest.fn();
});
it('defaults to `undefined`', () => {
let result = parseTarget(output);
expect(result).toEqual(undefined);
});
it('fails when given invalid target', () => {
const result = parseTarget(output, 'not-a-real-environment');
expect(result).toEqual(1);
const errorMock = (output.error as jest.Mock<any, any>).mock;
expect(errorMock.calls[0][0]).toMatch(
/not-a-real-environment.+is not valid/g
);
});
it('parses "production" target', () => {
let result = parseTarget(output, 'production');
expect(result).toEqual('production');
expect(output.warn).toHaveBeenCalledWith(
'We recommend using the much shorter `--prod` option instead of `--target production` (deprecated)'
);
});
it('parses "staging" target', () => {
let result = parseTarget(output, 'staging');
expect(result).toEqual('staging');
});
it('prefers target over production argument', () => {
let result = parseTarget(output, 'staging', true);
expect(result).toEqual('staging');
});
it('parses production argument when `true`', () => {
let result = parseTarget(output, undefined, true);
expect(result).toEqual('production');
});
it('parses production argument when `false`', () => {
let result = parseTarget(output, undefined, false);
expect(result).toEqual(undefined);
});
});

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "10.4.2-canary.2",
"version": "11.0.1-canary.2",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -24,7 +24,7 @@
"devDependencies": {
"@types/async-retry": "1.4.1",
"@types/fs-extra": "7.0.0",
"@types/jest": "27.0.1",
"@types/jest": "27.4.1",
"@types/minimatch": "3.0.5",
"@types/ms": "0.7.30",
"@types/node": "12.0.4",
@@ -41,7 +41,7 @@
]
},
"dependencies": {
"@vercel/build-utils": "2.15.2-canary.2",
"@vercel/build-utils": "2.16.1-canary.2",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",
"async-sema": "3.0.0",

View File

@@ -126,27 +126,6 @@ export async function* deploy(
}
}
if (
files.size === 1 &&
deploymentOptions.builds === undefined &&
deploymentOptions.routes === undefined &&
deploymentOptions.cleanUrls === undefined &&
deploymentOptions.rewrites === undefined &&
deploymentOptions.redirects === undefined &&
deploymentOptions.headers === undefined &&
deploymentOptions.trailingSlash === undefined
) {
debug(`Assigning '/' route for single file deployment`);
const filePath = Array.from(files.values())[0].names[0];
deploymentOptions.routes = [
{
src: '/',
dest: `/${filePath.split('/').pop()}`,
},
];
}
if (!deploymentOptions.name) {
deploymentOptions.name =
clientOptions.defaultName || getDefaultName(files, clientOptions);

View File

@@ -0,0 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M22.428.013c-.103.01-.431.042-.727.066C14.883.693 8.497 4.37 4.453 10.024A23.754 23.754 0 0 0 .216 20.51C.023 21.828 0 22.217 0 24.005c0 1.787.023 2.177.216 3.495 1.304 9.012 7.718 16.584 16.417 19.39 1.558.501 3.2.844 5.068 1.05.727.08 3.87.08 4.598 0 3.224-.356 5.954-1.154 8.648-2.529.412-.21.492-.267.436-.314-.038-.028-1.797-2.388-3.909-5.24l-3.838-5.184-4.809-7.117c-2.646-3.913-4.824-7.112-4.842-7.112-.019-.005-.038 3.157-.047 7.018-.014 6.76-.019 7.033-.103 7.192-.122.23-.216.324-.413.427-.15.075-.282.09-.99.09h-.812l-.216-.137a.878.878 0 0 1-.314-.342l-.099-.211.01-9.407.014-9.41.145-.184c.075-.098.235-.225.347-.286.193-.094.268-.103 1.08-.103.957 0 1.116.038 1.365.31.07.075 2.674 3.997 5.79 8.721s7.376 11.175 9.469 14.342l3.8 5.756.192-.127c1.704-1.107 3.505-2.683 4.932-4.325a23.888 23.888 0 0 0 5.65-12.268c.192-1.319.215-1.708.215-3.495 0-1.788-.023-2.177-.216-3.495-1.304-9.013-7.718-16.584-16.417-19.39C29.832.623 28.199.28 26.369.074c-.45-.047-3.551-.099-3.94-.061zm9.825 14.515a.947.947 0 0 1 .474.554c.038.122.047 2.73.038 8.608l-.014 8.436-1.488-2.28-1.492-2.28v-6.132c0-3.964.019-6.193.047-6.3a.957.957 0 0 1 .465-.592c.192-.098.262-.108 1-.108.694 0 .816.01.97.094z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/frameworks",
"version": "0.7.1",
"version": "0.8.1-canary.0",
"main": "./dist/frameworks.js",
"types": "./dist/frameworks.d.ts",
"files": [
@@ -16,11 +16,11 @@
"js-yaml": "3.13.1"
},
"devDependencies": {
"@types/jest": "27.0.1",
"@types/jest": "27.4.1",
"@types/js-yaml": "3.12.1",
"@types/node": "12.0.4",
"@types/node-fetch": "2.5.8",
"@vercel/routing-utils": "1.13.1",
"@vercel/routing-utils": "1.13.2",
"ajv": "6.12.2",
"typescript": "4.3.4"
}

View File

@@ -60,6 +60,7 @@ export const frameworks = [
slug: 'nextjs',
demo: 'https://nextjs-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/next.svg',
darkModeLogo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/next-dark.svg',
screenshot:
'https://assets.vercel.com/image/upload/v1647366075/front/import/nextjs.png',
tagline:
@@ -203,9 +204,7 @@ export const frameworks = [
detectors: {
every: [
{
path: 'package.json',
matchContent:
'"(dev)?(d|D)ependencies":\\s*{[^}]*"remix":\\s*".+?"[^}]*}',
path: 'remix.config.js',
},
],
},

View File

@@ -60,6 +60,11 @@ export interface Framework {
* @example "https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/next.svg"
*/
logo: string;
/**
* An additional URL to the logo of the framework optimized for dark mode
* @example "https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/next-dark.svg"
*/
darkModeLogo?: string;
/**
* A URL to a screenshot of the demo
* @example "https://assets.vercel.com/image/upload/v1647366075/front/import/nextjs.png"

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/go",
"version": "1.3.3-canary.2",
"version": "1.4.1-canary.2",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
@@ -24,7 +24,7 @@
"@types/fs-extra": "^5.0.5",
"@types/node-fetch": "^2.3.0",
"@types/tar": "^4.0.0",
"@vercel/build-utils": "2.15.2-canary.2",
"@vercel/build-utils": "2.16.1-canary.2",
"@vercel/ncc": "0.24.0",
"async-retry": "1.3.1",
"execa": "^1.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/node-bridge",
"version": "2.2.1-canary.0",
"version": "2.2.1",
"license": "MIT",
"main": "./index.js",
"repository": {

View File

@@ -1,3 +1,4 @@
/dist
/test/fixtures/**/types.d.ts
/test/fixtures/11-symlinks/symlink
!test/cache-fixtures/**

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/node",
"version": "1.14.2-canary.3",
"version": "1.15.1-canary.2",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -12,14 +12,26 @@
"scripts": {
"build": "node build",
"test-integration-once": "jest --env node --verbose --runInBand --bail test/integration.test.js",
"test-unit": "jest --env node --verbose --bail test/prepare-cache.test.ts",
"prepublishOnly": "node build"
},
"files": [
"dist"
],
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"globals": {
"ts-jest": {
"diagnostics": true,
"isolatedModules": true
}
}
},
"dependencies": {
"@types/jest": "27.4.1",
"@types/node": "*",
"@vercel/node-bridge": "2.2.1-canary.0",
"@vercel/node-bridge": "2.2.1",
"ts-node": "8.9.1",
"typescript": "4.3.4"
},
@@ -32,7 +44,7 @@
"@types/cookie": "0.3.3",
"@types/etag": "1.8.0",
"@types/test-listen": "1.1.0",
"@vercel/build-utils": "2.15.2-canary.2",
"@vercel/build-utils": "2.16.1-canary.2",
"@vercel/ncc": "0.24.0",
"@vercel/nft": "0.18.1",
"content-type": "1.0.4",

View File

@@ -380,9 +380,8 @@ export const build: BuildV3 = async ({
return { output: lambda };
};
export const prepareCache: PrepareCache = async ({ workPath }) => {
const cache = await glob('node_modules/**', workPath);
return cache;
export const prepareCache: PrepareCache = ({ repoRootPath, workPath }) => {
return glob('**/node_modules/**', repoRootPath || workPath);
};
export const startDevServer: StartDevServer = async opts => {

View File

0
packages/node/test/cache-fixtures/node_modules/file generated vendored Normal file
View File

View File

@@ -0,0 +1,36 @@
import path from 'path';
import assert from 'assert';
import { prepareCache } from '../src';
describe('prepareCache()', () => {
test('should cache `**/node_modules/**`', async () => {
const files = await prepareCache({
files: {},
entrypoint: '.',
config: {},
workPath: path.resolve(__dirname, './cache-fixtures/'),
repoRootPath: path.resolve(__dirname, './cache-fixtures/'),
});
expect(files['foo/node_modules/file']).toBeDefined();
expect(files['node_modules/file']).toBeDefined();
expect(files['index.js']).toBeUndefined();
});
test('should ignore root modules', async () => {
const files = await prepareCache({
files: {},
entrypoint: '.',
config: {},
workPath: path.resolve(__dirname, './cache-fixtures/foo/'),
});
const file = files['node_modules/file'];
expect(file).toBeDefined();
assert(file.type === 'FileFsRef');
expect(
file.fsPath.includes('cache-fixtures/foo/node_modules/file')
).toBeTruthy();
expect(files['index.js']).toBeUndefined();
});
});

4
packages/node/test/tsconfig.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"extends": "../tsconfig.json",
"include": ["*.test.ts"]
}

View File

@@ -0,0 +1,5 @@
/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/python",
"version": "2.2.3-canary.2",
"version": "2.3.1-canary.2",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -15,12 +15,14 @@
},
"scripts": {
"build": "node build",
"test-integration-once": "jest --env node --verbose --runInBand --bail",
"test-unit": "jest --env node --verbose --runInBand --bail test/unit.test.ts ",
"test-integration-once": "jest --env node --verbose --runInBand --bail test/integration.test.ts",
"prepublishOnly": "node build"
},
"devDependencies": {
"@types/execa": "^0.9.0",
"@vercel/build-utils": "2.15.2-canary.2",
"@types/jest": "27.4.1",
"@vercel/build-utils": "2.16.1-canary.2",
"@vercel/ncc": "0.24.0",
"execa": "^1.0.0",
"typescript": "4.3.4"

View File

@@ -16,6 +16,7 @@ import {
NowBuildError,
} from '@vercel/build-utils';
import { installRequirement, installRequirementsFile } from './install';
import { getLatestPythonVersion, getSupportedPythonVersion } from './version';
async function pipenvConvert(cmd: string, srcDir: string) {
debug('Running pipfile2req...');
@@ -59,9 +60,7 @@ export const build = async ({
meta = {},
config,
}: BuildOptions) => {
let pipPath = meta.isDev ? 'pip3' : 'pip3.9';
let pythonPath = meta.isDev ? 'python3' : 'python3.9';
let pythonRuntime = meta.isDev ? 'python3' : 'python3.9';
let pythonVersion = getLatestPythonVersion(meta);
workPath = await downloadFilesInWorkPath({
workPath,
@@ -91,8 +90,8 @@ export const build = async ({
console.log('Installing required dependencies...');
await installRequirement({
pythonPath,
pipPath,
pythonPath: pythonVersion.pythonPath,
pipPath: pythonVersion.pipPath,
dependency: 'werkzeug',
version: '1.0.1',
workPath,
@@ -114,25 +113,10 @@ export const build = async ({
try {
const json = await readFile(join(pipfileLockDir, 'Pipfile.lock'), 'utf8');
const obj = JSON.parse(json);
const version = obj?._meta?.requires?.python_version;
if (!meta.isDev) {
if (version === '3.6') {
pipPath = 'pip3.6';
pythonPath = 'python3.6';
pythonRuntime = 'python3.6';
console.warn(
`Warning: Python version "${version}" detected in Pipfile.lock will reach End-Of-Life December 2021. Please upgrade. http://vercel.link/python-version`
);
} else if (version === '3.9') {
pipPath = 'pip3.9';
pythonPath = 'python3.9';
pythonRuntime = 'python3.9';
} else {
console.warn(
`Warning: Invalid Python version "${version}" detected in Pipfile.lock will be ignored. http://vercel.link/python-version`
);
}
}
pythonVersion = getSupportedPythonVersion({
isDev: meta.isDev,
pipLockPythonVersion: obj?._meta?.requires?.python_version,
});
} catch (err) {
throw new NowBuildError({
code: 'INVALID_PIPFILE_LOCK',
@@ -146,8 +130,8 @@ export const build = async ({
// it into a separate folder.
const tempDir = await getWriteableDirectory();
await installRequirement({
pythonPath,
pipPath,
pythonPath: pythonVersion.pythonPath,
pipPath: pythonVersion.pipPath,
dependency: 'pipfile-requirements',
version: '0.3.0',
workPath: tempDir,
@@ -169,8 +153,8 @@ export const build = async ({
debug('Found local "requirements.txt"');
const requirementsTxtPath = fsFiles[requirementsTxt].fsPath;
await installRequirementsFile({
pythonPath,
pipPath,
pythonPath: pythonVersion.pythonPath,
pipPath: pythonVersion.pipPath,
filePath: requirementsTxtPath,
workPath,
meta,
@@ -179,8 +163,8 @@ export const build = async ({
debug('Found global "requirements.txt"');
const requirementsTxtPath = fsFiles['requirements.txt'].fsPath;
await installRequirementsFile({
pythonPath,
pipPath,
pythonPath: pythonVersion.pythonPath,
pipPath: pythonVersion.pipPath,
filePath: requirementsTxtPath,
workPath,
meta,
@@ -205,9 +189,6 @@ export const build = async ({
await writeFile(join(workPath, `${handlerPyFilename}.py`), handlerPyContents);
// Use the system-installed version of `python3` when running via `vercel dev`
const runtime = meta.isDev ? 'python3' : pythonRuntime;
const globOptions: GlobOptions = {
cwd: workPath,
ignore:
@@ -219,7 +200,7 @@ export const build = async ({
const lambda = await createLambda({
files: await glob('**', globOptions),
handler: `${handlerPyFilename}.vc_handler`,
runtime,
runtime: pythonVersion.runtime,
environment: {},
});

View File

@@ -0,0 +1,95 @@
import { NowBuildError } from '@vercel/build-utils';
interface PythonVersion {
version: string;
pipPath: string;
pythonPath: string;
runtime: string;
discontinueDate?: Date;
}
// The order must be most recent first
const allOptions: PythonVersion[] = [
{
version: '3.9',
pipPath: 'pip3.9',
pythonPath: 'python3.9',
runtime: 'python3.9',
},
{
version: '3.6',
pipPath: 'pip3.6',
pythonPath: 'python3.6',
runtime: 'python3.6',
discontinueDate: new Date('2022-07-18'),
},
];
const upstreamProvider =
'This change is the result of a decision made by an upstream infrastructure provider (AWS)';
function getDevPythonVersion(): PythonVersion {
// Use the system-installed version of `python3` when running `vercel dev`
return {
version: '3',
pipPath: 'pip3',
pythonPath: 'python3',
runtime: 'python3',
};
}
export function getLatestPythonVersion({
isDev,
}: {
isDev?: boolean;
}): PythonVersion {
if (isDev) {
return getDevPythonVersion();
}
return allOptions[0];
}
export function getSupportedPythonVersion({
isDev,
pipLockPythonVersion,
}: {
isDev?: boolean;
pipLockPythonVersion: string | undefined;
}): PythonVersion {
if (isDev) {
return getDevPythonVersion();
}
let selection = getLatestPythonVersion({ isDev: false });
if (typeof pipLockPythonVersion === 'string') {
const found = allOptions.find(o => o.version === pipLockPythonVersion);
if (found) {
selection = found;
} else {
console.warn(
`Warning: Python version "${pipLockPythonVersion}" detected in Pipfile.lock is invalid and will be ignored. http://vercel.link/python-version`
);
}
}
if (isDiscontinued(selection)) {
throw new NowBuildError({
code: 'BUILD_UTILS_PYTHON_VERSION_DISCONTINUED',
link: 'http://vercel.link/python-version',
message: `Python version "${selection.version}" detected in Pipfile.lock is discontinued and must be upgraded. ${upstreamProvider}.`,
});
}
if (selection.discontinueDate) {
const d = selection.discontinueDate.toISOString().split('T')[0];
console.warn(
`Error: Python version "${selection.version}" detected in Pipfile.lock is deprecated. Deployments created on or after ${d} will fail to build. ${upstreamProvider}. http://vercel.link/python-version`
);
}
return selection;
}
function isDiscontinued({ discontinueDate }: PythonVersion): boolean {
const today = Date.now();
return discontinueDate !== undefined && discontinueDate.getTime() <= today;
}

View File

@@ -1,6 +1,6 @@
from sanic import Sanic
from sanic.response import json
app = Sanic()
app = Sanic(name='test')
@app.route('/')
@app.route('/<path:path>')

View File

@@ -1,2 +1,23 @@
sanic==19.6.0
flask==1.0.2
sanic==20.12.6
flask==2.1.1
# below is meant to lock the version of transitive deps
aiofiles==0.8.0; python_version >= "3.6" and python_version < "4.0"
certifi==2021.10.8
click==8.1.2; python_version >= "3.7"
h11==0.9.0
httpcore==0.11.1; python_version >= "3.6"
httptools==0.4.0; python_version >= "3.5"
httpx==0.15.4; python_version >= "3.6"
idna==3.3
importlib-metadata==4.11.3; python_version < "3.10"
itsdangerous==2.1.2; python_version >= "3.7"
jinja2==3.1.1; python_version >= "3.7"
markupsafe==2.1.1; python_version >= "3.7"
multidict==5.2.0; python_version >= "3.6"
rfc3986[idna2008]==1.5.0
sniffio==1.2.0; python_version >= "3.5"
ujson==5.2.0; sys_platform != "win32" and implementation_name == "cpython"
uvloop==0.16.0; sys_platform != "win32" and implementation_name == "cpython"
websockets==9.1; python_full_version >= "3.6.1"
werkzeug==2.1.1; python_version >= "3.7"
zipp==3.8.0; python_version >= "3.7"

View File

@@ -5,7 +5,7 @@
{
"path": "/",
"mustContain": "wsgi:RANDOMNESS_PLACEHOLDER",
"logMustContain": "Warning: Python version \"3.6\" detected in Pipfile.lock will reach End-Of-Life December 2021. Please upgrade."
"logMustContain": "Python version \"3.6\" detected in Pipfile.lock is deprecated. Deployments created on or after 2022-07-18 will fail to build"
}
]
}

View File

@@ -8,7 +8,7 @@
{
"path": "/",
"mustContain": "RANDOMNESS_PLACEHOLDER:env",
"logMustContain": "Warning: Python version \"3.6\" detected in Pipfile.lock will reach End-Of-Life December 2021. Please upgrade."
"logMustContain": "Python version \"3.6\" detected in Pipfile.lock is deprecated. Deployments created on or after 2022-07-18 will fail to build"
}
]
}

View File

@@ -5,7 +5,7 @@
{
"path": "/",
"mustContain": "pip:RANDOMNESS_PLACEHOLDER",
"logMustContain": "Warning: Invalid Python version \"3.7\" detected in Pipfile.lock will be ignored."
"logMustContain": "Python version \"3.7\" detected in Pipfile.lock is invalid and will be ignored."
}
]
}

View File

@@ -0,0 +1,12 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
flask = "*"
[dev-packages]
[requires]
python_version = "3.9"

View File

@@ -0,0 +1,123 @@
{
"_meta": {
"hash": {
"sha256": "f7f1cea682a03d85328caf2f88382c4380283d3892a9ba31b374784fb29536c4"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.9"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"click": {
"hashes": [
"sha256:24e1a4a9ec5bf6299411369b208c1df2188d9eb8d916302fe6bf03faed227f1e",
"sha256:479707fe14d9ec9a0757618b7a100a0ae4c4e236fac5b7f80ca68028141a1a72"
],
"markers": "python_version >= '3.7'",
"version": "==8.1.2"
},
"flask": {
"hashes": [
"sha256:8a4cf32d904cf5621db9f0c9fbcd7efabf3003f22a04e4d0ce790c7137ec5264",
"sha256:a8c9bd3e558ec99646d177a9739c41df1ded0629480b4c8d2975412f3c9519c8"
],
"index": "pypi",
"version": "==2.1.1"
},
"importlib-metadata": {
"hashes": [
"sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6",
"sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539"
],
"markers": "python_version < '3.10'",
"version": "==4.11.3"
},
"itsdangerous": {
"hashes": [
"sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44",
"sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"
],
"markers": "python_version >= '3.7'",
"version": "==2.1.2"
},
"jinja2": {
"hashes": [
"sha256:539835f51a74a69f41b848a9645dbdc35b4f20a3b601e2d9a7e22947b15ff119",
"sha256:640bed4bb501cbd17194b3cace1dc2126f5b619cf068a726b98192a0fde74ae9"
],
"markers": "python_version >= '3.7'",
"version": "==3.1.1"
},
"markupsafe": {
"hashes": [
"sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003",
"sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88",
"sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5",
"sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7",
"sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a",
"sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603",
"sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1",
"sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135",
"sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247",
"sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6",
"sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601",
"sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77",
"sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02",
"sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e",
"sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63",
"sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f",
"sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980",
"sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b",
"sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812",
"sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff",
"sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96",
"sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1",
"sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925",
"sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a",
"sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6",
"sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e",
"sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f",
"sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4",
"sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f",
"sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3",
"sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c",
"sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a",
"sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417",
"sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a",
"sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a",
"sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37",
"sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452",
"sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933",
"sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a",
"sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"
],
"markers": "python_version >= '3.7'",
"version": "==2.1.1"
},
"werkzeug": {
"hashes": [
"sha256:3c5493ece8268fecdcdc9c0b112211acd006354723b280d643ec732b6d4063d6",
"sha256:f8e89a20aeabbe8a893c24a461d3ee5dad2123b05cc6abd73ceed01d39c3ae74"
],
"markers": "python_version >= '3.7'",
"version": "==2.1.1"
},
"zipp": {
"hashes": [
"sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad",
"sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"
],
"markers": "python_version >= '3.7'",
"version": "==3.8.0"
}
},
"develop": {}
}

View File

@@ -1 +0,0 @@
Flask==1.0.2

View File

@@ -0,0 +1,12 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
flask = "*"
[dev-packages]
[requires]
python_version = "3.9"

View File

@@ -0,0 +1,123 @@
{
"_meta": {
"hash": {
"sha256": "f7f1cea682a03d85328caf2f88382c4380283d3892a9ba31b374784fb29536c4"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.9"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"click": {
"hashes": [
"sha256:24e1a4a9ec5bf6299411369b208c1df2188d9eb8d916302fe6bf03faed227f1e",
"sha256:479707fe14d9ec9a0757618b7a100a0ae4c4e236fac5b7f80ca68028141a1a72"
],
"markers": "python_version >= '3.7'",
"version": "==8.1.2"
},
"flask": {
"hashes": [
"sha256:8a4cf32d904cf5621db9f0c9fbcd7efabf3003f22a04e4d0ce790c7137ec5264",
"sha256:a8c9bd3e558ec99646d177a9739c41df1ded0629480b4c8d2975412f3c9519c8"
],
"index": "pypi",
"version": "==2.1.1"
},
"importlib-metadata": {
"hashes": [
"sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6",
"sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539"
],
"markers": "python_version < '3.10'",
"version": "==4.11.3"
},
"itsdangerous": {
"hashes": [
"sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44",
"sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"
],
"markers": "python_version >= '3.7'",
"version": "==2.1.2"
},
"jinja2": {
"hashes": [
"sha256:539835f51a74a69f41b848a9645dbdc35b4f20a3b601e2d9a7e22947b15ff119",
"sha256:640bed4bb501cbd17194b3cace1dc2126f5b619cf068a726b98192a0fde74ae9"
],
"markers": "python_version >= '3.7'",
"version": "==3.1.1"
},
"markupsafe": {
"hashes": [
"sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003",
"sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88",
"sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5",
"sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7",
"sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a",
"sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603",
"sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1",
"sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135",
"sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247",
"sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6",
"sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601",
"sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77",
"sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02",
"sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e",
"sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63",
"sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f",
"sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980",
"sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b",
"sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812",
"sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff",
"sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96",
"sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1",
"sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925",
"sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a",
"sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6",
"sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e",
"sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f",
"sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4",
"sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f",
"sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3",
"sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c",
"sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a",
"sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417",
"sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a",
"sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a",
"sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37",
"sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452",
"sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933",
"sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a",
"sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"
],
"markers": "python_version >= '3.7'",
"version": "==2.1.1"
},
"werkzeug": {
"hashes": [
"sha256:3c5493ece8268fecdcdc9c0b112211acd006354723b280d643ec732b6d4063d6",
"sha256:f8e89a20aeabbe8a893c24a461d3ee5dad2123b05cc6abd73ceed01d39c3ae74"
],
"markers": "python_version >= '3.7'",
"version": "==2.1.1"
},
"zipp": {
"hashes": [
"sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad",
"sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"
],
"markers": "python_version >= '3.7'",
"version": "==3.8.0"
}
},
"develop": {}
}

View File

@@ -1 +0,0 @@
Flask==1.0.2

View File

@@ -12,7 +12,7 @@ const {
jest.setTimeout(4 * 60 * 1000);
const buildUtilsUrl = '@canary';
let builderUrl;
let builderUrl: string;
beforeAll(async () => {
const builderPath = path.resolve(__dirname, '..');

4
packages/python/test/tsconfig.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"extends": "../tsconfig.json",
"include": ["*.test.ts"]
}

75
packages/python/test/unit.test.ts vendored Normal file
View File

@@ -0,0 +1,75 @@
import { getSupportedPythonVersion } from '../src/version';
let warningMessages: string[];
const originalConsoleWarn = console.warn;
const realDateNow = Date.now.bind(global.Date);
beforeEach(() => {
warningMessages = [];
console.warn = m => {
warningMessages.push(m);
};
});
afterEach(() => {
console.warn = originalConsoleWarn;
global.Date.now = realDateNow;
});
it('should only match supported versions, otherwise throw an error', async () => {
expect(
getSupportedPythonVersion({ pipLockPythonVersion: '3.9' })
).toHaveProperty('runtime', 'python3.9');
expect(
getSupportedPythonVersion({ pipLockPythonVersion: '3.6' })
).toHaveProperty('runtime', 'python3.6');
});
it('should ignore minor version in vercel dev', async () => {
expect(
getSupportedPythonVersion({ pipLockPythonVersion: '3.9', isDev: true })
).toHaveProperty('runtime', 'python3');
expect(
getSupportedPythonVersion({ pipLockPythonVersion: '3.6', isDev: true })
).toHaveProperty('runtime', 'python3');
expect(
getSupportedPythonVersion({ pipLockPythonVersion: '999', isDev: true })
).toHaveProperty('runtime', 'python3');
expect(warningMessages).toStrictEqual([]);
});
it('should select latest version when no Piplock detected', async () => {
expect(
getSupportedPythonVersion({ pipLockPythonVersion: undefined })
).toHaveProperty('runtime', 'python3.9');
expect(warningMessages).toStrictEqual([]);
});
it('should select latest version and warn when invalid Piplock detected', async () => {
expect(
getSupportedPythonVersion({ pipLockPythonVersion: '999' })
).toHaveProperty('runtime', 'python3.9');
expect(warningMessages).toStrictEqual([
'Warning: Python version "999" detected in Pipfile.lock is invalid and will be ignored. http://vercel.link/python-version',
]);
});
it('should throw for discontinued versions', async () => {
global.Date.now = () => new Date('2022-07-31').getTime();
expect(() =>
getSupportedPythonVersion({ pipLockPythonVersion: '3.6' })
).toThrow(
'Python version "3.6" detected in Pipfile.lock is discontinued and must be upgraded. This change is the result of a decision made by an upstream infrastructure provider (AWS).'
);
expect(warningMessages).toStrictEqual([]);
});
it('should warn for deprecated versions, soon to be discontinued', async () => {
global.Date.now = () => new Date('2021-07-01').getTime();
expect(
getSupportedPythonVersion({ pipLockPythonVersion: '3.6' })
).toHaveProperty('runtime', 'python3.6');
expect(warningMessages).toStrictEqual([
'Error: Python version "3.6" detected in Pipfile.lock is deprecated. Deployments created on or after 2022-07-18 will fail to build. This change is the result of a decision made by an upstream infrastructure provider (AWS). http://vercel.link/python-version',
]);
});

View File

@@ -11,8 +11,10 @@
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "dist",
"types": ["node"],
"types": ["node", "jest"],
"strict": true,
"target": "es2018"
}
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}

View File

@@ -16,7 +16,7 @@ __vc_variables = dir(__vc_module)
def format_headers(headers, decode=False):
keyToList = {}
for key, value in headers.items():
if decode:
if decode and 'decode' in dir(key) and 'decode' in dir(value):
key = key.decode()
value = value.decode()
if key not in keyToList:
@@ -102,7 +102,7 @@ elif 'app' in __vc_variables:
if isinstance(s, str):
s = s.encode(charset)
return s.decode("latin1", errors)
def vc_handler(event, context):
payload = json.loads(event['body'])

Some files were not shown because too many files have changed in this diff Show More