Compare commits

..

8 Commits

Author SHA1 Message Date
Nathan Rajlich
be74f79fa0 Publish Canary
- @vercel/build-utils@3.1.1-canary.2
 - vercel@24.2.5-canary.2
 - @vercel/client@11.0.4-canary.2
 - @vercel/go@1.4.4-canary.2
 - @vercel/next@2.8.67-canary.2
 - @vercel/node@1.15.4-canary.2
 - @vercel/python@2.3.4-canary.2
 - @vercel/redwood@0.8.4-canary.2
 - @vercel/remix@0.0.2-canary.2
 - @vercel/ruby@1.3.7-canary.2
 - @vercel/static-build@0.25.3-canary.2
2022-05-24 09:14:29 -07:00
Andrew Gadzik
86886e6b60 [build-utils] Add getWorkspaces helper function (#7775)
```ts
/*

  Example 1:
	
  my-repo
    |-- packages
    |    |-- api-1
    |    |-- api-2
    |-- package.json
    |-- pnpm-workspaces.yaml // packages: ["packages/*"]
	
  getWorkspaces("my-repo") => [
    {
      implementation: "pnpm",
      rootPath: "https://github.com/.../my-repo"
    }
  ]
  
  Example 2:
	
  my-repo
    |-- backend
    |    |-- api-1
    |    |-- api-2
    |    |-- package.json // workspaces: ["api-1", "api-2"]
    |-- frontend
    |    |-- nextjs-app
    |    |-- gatsby-app
    |    |-- package.json
    |    |-- pnpm-workspaces.yaml // packages: ["nextjs-app", "gatsby-app"]
	
  getWorkspaces("my-repo") => [
    {
      implementation: "yarn",
      rootPath: "https://github.com/.../my-repo/backend"
    }, 
    {
      implementation: "pnpm",
      rootPath: "https://github.com/.../my-repo/frontend"
    }
  ]

 */
type Workspace = {
  implementation: "yarn" | "npm" | "pnpm" 
  rootPath: string // the path to the root of the workspace
}

function getWorkspaces(fs: DetectorFilesystem): Workspace[]
```

### Related Issues

> Closes https://github.com/vercel/vercel/issues/7730

### 📋 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-24 14:28:32 +00:00
Nathan Rajlich
e8a9000137 [cli] Symlink @now/build-utils when installing builders in "vc build" (#7844)
To support legacy Builders like `@frontity/now` which still depends on the old `@now/build-utils` package name, create a symlink to `@vercel/build-utils` to make import the package work transparently.

Depends on #7842 being merged first.
2022-05-24 05:36:34 +00:00
Nathan Rajlich
75ea68d445 [cli] Convert DevServer "Unit" suite tests to "Dev" suite tests (#7845)
The `DevServer` class as it's currently implement is not really suitable for unit testing, since it calls `process.exit()` when shutting down the server. This causes Jest to exit prematurely, and prevents tests after these one from running. This was in fact hiding some failing tests. So these tests were ported over to be "Dev" suite tests which spawn a child process of `vc dev` rather than running them in-process, so that the `process.exit()` doesn't exit Jest.
2022-05-23 23:38:02 +00:00
Sean Massa
a5a990995c [tests] improve error message when a deploy fails (#7864)
improve error message
2022-05-23 17:02:29 -05:00
Sean Massa
cd7dcc6731 [tests] bump node version to 14 (#7863) 2022-05-23 16:00:03 -05:00
Sean Massa
c4bd462b85 [tests] update fixture path (#7862)
It looks like the fixture location was shifted and these references to it were not updated. The crashing `dev` tests were hiding these failures.
2022-05-23 20:31:24 +00:00
Steven
5fc266bd8a [next][node] Bump @vercel/nft to 0.19.1 (#7860)
Bump `@vercel/nft` to 0.19.1 https://github.com/vercel/nft/releases/tag/0.19.1
2022-05-23 17:30:29 +00:00
89 changed files with 1223 additions and 500 deletions

View File

@@ -16,7 +16,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
node: [12]
node: [14]
runs-on: ${{ matrix.os }}
env:
TURBO_REMOTE_ONLY: true

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "3.1.1-canary.1",
"version": "3.1.1-canary.2",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",

View File

@@ -117,4 +117,5 @@ export const isStaticRuntime = (name?: string): boolean => {
};
export { workspaceManagers } from './workspaces/workspace-managers';
export { getWorkspaces } from './workspaces/get-workspaces';
export { monorepoManagers } from './monorepos/monorepo-managers';

View File

@@ -0,0 +1,59 @@
import path from 'path';
import { DetectorFilesystem } from '../detectors/filesystem';
import { workspaceManagers } from './workspace-managers';
import { detectFramework as detectWorkspaceManagers } from '../detect-framework';
const MAX_DEPTH_TRAVERSE = 3;
const posixPath = path.posix;
export interface GetWorkspaceOptions {
fs: DetectorFilesystem;
depth?: number;
cwd?: string;
}
export type WorkspaceType = 'yarn' | 'pnpm' | 'npm';
export type Workspace = {
type: WorkspaceType;
rootPath: string;
};
export async function getWorkspaces({
fs,
depth = MAX_DEPTH_TRAVERSE,
cwd = '/',
}: GetWorkspaceOptions): Promise<Workspace[]> {
if (depth === 0) return [];
const workspaceType = await detectWorkspaceManagers({
fs,
frameworkList: workspaceManagers,
});
if (workspaceType === null) {
const directoryContents = await fs.readdir('./');
const childDirectories = directoryContents.filter(
stat => stat.type === 'dir'
);
return (
await Promise.all(
childDirectories.map(childDirectory =>
getWorkspaces({
fs: fs.chdir(childDirectory.path),
depth: depth - 1,
cwd: posixPath.join(cwd, childDirectory.path),
})
)
)
).flat();
}
return [
{
type: workspaceType as WorkspaceType,
rootPath: cwd,
},
];
}

View File

@@ -0,0 +1,5 @@
{
"name": "c",
"license": "MIT",
"version": "0.1.0"
}

View File

@@ -0,0 +1,8 @@
{
"name": "d",
"license": "MIT",
"version": "0.1.0",
"devDependencies": {
"once": "1.4.0"
}
}

View File

@@ -0,0 +1,6 @@
{
"private": true,
"name": "29-nested-workspaces-backend",
"license": "MIT",
"version": "1.0.0"
}

View File

@@ -0,0 +1,27 @@
lockfileVersion: 5.3
importers:
.:
specifiers: {}
c:
specifiers: {}
d:
specifiers:
once: 1.4.0
devDependencies:
once: 1.4.0
packages:
/once/1.4.0:
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
dependencies:
wrappy: 1.0.2
dev: true
/wrappy/1.0.2:
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
dev: true

View File

@@ -0,0 +1,3 @@
packages:
- 'c'
- 'd'

View File

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

View File

@@ -0,0 +1,5 @@
{
"name": "c",
"license": "MIT",
"version": "0.1.0"
}

View File

@@ -0,0 +1,8 @@
{
"name": "d",
"license": "MIT",
"version": "0.1.0",
"devDependencies": {
"once": "1.4.0"
}
}

View File

@@ -0,0 +1,6 @@
{
"private": true,
"name": "30-double-nested-workspaces-packages-backend",
"license": "MIT",
"version": "1.0.0"
}

View File

@@ -0,0 +1,27 @@
lockfileVersion: 5.3
importers:
.:
specifiers: {}
c:
specifiers: {}
d:
specifiers:
once: 1.4.0
devDependencies:
once: 1.4.0
packages:
/once/1.4.0:
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
dependencies:
wrappy: 1.0.2
dev: true
/wrappy/1.0.2:
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
dev: true

View File

@@ -0,0 +1,3 @@
packages:
- 'c'
- 'd'

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": "30-double-nested-workspaces-packages-frontend",
"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,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,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

@@ -34,7 +34,9 @@ const skipFixtures: string[] = [
'23-pnpm-workspaces',
'27-yarn-workspaces',
'28-turborepo-with-yarn-workspaces',
'29-turborepo-in-package-json',
'29-nested-workspaces',
'30-double-nested-workspaces',
'31-turborepo-in-package-json',
];
// eslint-disable-next-line no-restricted-syntax

View File

@@ -5,10 +5,10 @@ import { FixtureFilesystem } from './utils/fixture-filesystem';
describe('monorepo-managers', () => {
describe.each([
['turbo', '28-turborepo-with-yarn-workspaces'],
['turbo', '29-turborepo-in-package-json'],
[null, '22-pnpm'],
])('with detectFramework', (frameworkSlug, fixturePath) => {
['28-turborepo-with-yarn-workspaces', 'turbo'],
['31-turborepo-in-package-json', 'turbo'],
['22-pnpm', null],
])('with detectFramework', (fixturePath, frameworkSlug) => {
const testName = frameworkSlug
? `should detect a ${frameworkSlug} workspace for ${fixturePath}`
: `should not detect a monorepo manager for ${fixturePath}`;

View File

@@ -5,13 +5,13 @@ 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) => {
['21-npm-workspaces', 'npm'],
['23-pnpm-workspaces', 'pnpm'],
['27-yarn-workspaces', 'yarn'],
['25-multiple-lock-files-yarn', 'yarn'],
['26-multiple-lock-files-pnpm', 'pnpm'],
['22-pnpm', null],
])('with detectFramework', (fixturePath, frameworkSlug) => {
const testName = frameworkSlug
? `should detect a ${frameworkSlug} workspace for ${fixturePath}`
: `should not detect framework for ${fixturePath}`;

View File

@@ -0,0 +1,43 @@
import path from 'path';
import { getWorkspaces, Workspace } from '../src/workspaces/get-workspaces';
import { FixtureFilesystem } from './utils/fixture-filesystem';
describe.each<[string, Workspace[]]>([
['21-npm-workspaces', [{ type: 'npm', rootPath: '/' }]],
['23-pnpm-workspaces', [{ type: 'pnpm', rootPath: '/' }]],
['27-yarn-workspaces', [{ type: 'yarn', rootPath: '/' }]],
['25-multiple-lock-files-yarn', [{ type: 'yarn', rootPath: '/' }]],
['26-multiple-lock-files-pnpm', [{ type: 'pnpm', rootPath: '/' }]],
[
'29-nested-workspaces',
[
{ type: 'pnpm', rootPath: '/backend' },
{ type: 'yarn', rootPath: '/frontend' },
],
],
[
'30-double-nested-workspaces',
[
{ type: 'pnpm', rootPath: '/packages/backend' },
{ type: 'yarn', rootPath: '/packages/frontend' },
],
],
['22-pnpm', []],
])('`getWorkspaces()`', (fixturePath, workspaces) => {
const expectedImplementations = workspaces.map(({ type }) => type);
const testName =
workspaces.length > 0
? `should detect ${expectedImplementations.join()} workspace${
expectedImplementations.length > 1 ? 's' : ''
} for ${fixturePath}`
: `should not detect any workspace for ${fixturePath}`;
it(testName, async () => {
const fixture = path.join(__dirname, 'fixtures', fixturePath);
const fs = new FixtureFilesystem(fixture);
const actualWorkspaces = await getWorkspaces({ fs });
expect(actualWorkspaces).toEqual(expect.arrayContaining(workspaces));
});
});

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "24.2.5-canary.1",
"version": "24.2.5-canary.2",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -43,15 +43,15 @@
"node": ">= 12"
},
"dependencies": {
"@vercel/build-utils": "3.1.1-canary.1",
"@vercel/go": "1.4.4-canary.1",
"@vercel/next": "2.8.67-canary.1",
"@vercel/node": "1.15.4-canary.1",
"@vercel/python": "2.3.4-canary.1",
"@vercel/redwood": "0.8.4-canary.1",
"@vercel/remix": "0.0.2-canary.1",
"@vercel/ruby": "1.3.7-canary.1",
"@vercel/static-build": "0.25.3-canary.1",
"@vercel/build-utils": "3.1.1-canary.2",
"@vercel/go": "1.4.4-canary.2",
"@vercel/next": "2.8.67-canary.2",
"@vercel/node": "1.15.4-canary.2",
"@vercel/python": "2.3.4-canary.2",
"@vercel/redwood": "0.8.4-canary.2",
"@vercel/remix": "0.0.2-canary.2",
"@vercel/ruby": "1.3.7-canary.2",
"@vercel/static-build": "0.25.3-canary.2",
"update-notifier": "4.1.0"
},
"devDependencies": {
@@ -95,7 +95,7 @@
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel/client": "11.0.4-canary.1",
"@vercel/client": "11.0.4-canary.2",
"@vercel/frameworks": "0.9.2-canary.0",
"@vercel/ncc": "0.24.0",
"@zeit/fun": "0.11.2",

View File

@@ -98,6 +98,12 @@ export default async function dev(
]);
}
// This is just for tests - can be removed once project settings
// are respected locally in `.vercel/project.json`
if (process.env.VERCEL_DEV_COMMAND) {
devCommand = process.env.VERCEL_DEV_COMMAND;
}
const devServer = new DevServer(cwd, {
output,
devCommand,

View File

@@ -1,7 +1,7 @@
import npa from 'npm-package-arg';
import { satisfies } from 'semver';
import { dirname, join } from 'path';
import { outputJSON, readJSON } from 'fs-extra';
import { mkdirp, outputJSON, readJSON, symlink } from 'fs-extra';
import {
BuilderV2,
BuilderV3,
@@ -203,6 +203,11 @@ async function installBuilders(
cwd: buildersDir,
});
// Symlink `@now/build-utils` -> `@vercel/build-utils` to support legacy Builders
const nowScopePath = join(buildersDir, 'node_modules/@now');
await mkdirp(nowScopePath);
await symlink('../@vercel/build-utils', join(nowScopePath, 'build-utils'));
// Cross-reference any builderSpecs from the saved `package.json` file,
// in case they were installed from a URL
const buildersPkg = await readJSONFile<PackageJson>(buildersPkgPath);

View File

@@ -0,0 +1 @@
export default () => <div>hello, this is the frontend</div>;

View File

@@ -1,6 +1,5 @@
{
"version": 2,
"name": "now-dev-headers",
"builds": [
{ "src": "index.js", "use": "@vercel/node" },
{ "src": "foo.txt", "use": "@vercel/static" }

View File

@@ -1,6 +1,5 @@
{
"version": 2,
"name": "now-dev-query-test",
"builds": [{ "src": "index.js", "use": "@vercel/node" }],
"routes": [
{

View File

@@ -1,5 +1,5 @@
{
"name": "now-dev-static-build-routing",
"name": "vc-dev-static-build-routing",
"private": true,
"scripts": {
"now-dev": "serve -l $PORT src",

View File

@@ -1,6 +1,9 @@
import os from 'os';
import url from 'url';
import fs from 'fs-extra';
import { join } from 'path';
import listen from 'async-listen';
import { createServer } from 'http';
const {
exec,
@@ -8,8 +11,374 @@ const {
fixture,
testFixture,
testFixtureStdio,
validateResponseHeaders,
} = require('./utils.js');
test('[vercel dev] should support request body', async () => {
const dir = fixture('node-request-body');
const { dev, port, readyResolver } = await testFixture(dir);
try {
await readyResolver;
const body = { hello: 'world' };
// Test that `req.body` works in dev
let res = await fetch(`http://localhost:${port}/api/req-body`, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(body),
});
validateResponseHeaders(res);
expect(await res.json()).toMatchObject(body);
// Test that `req` "data" events work in dev
res = await fetch(`http://localhost:${port}/api/data-events`, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(body),
});
} finally {
dev.kill('SIGTERM');
}
});
test('[vercel dev] should maintain query when invoking serverless function', async () => {
const dir = fixture('node-query-invoke');
const { dev, port, readyResolver } = await testFixture(dir);
try {
await readyResolver;
const res = await fetch(`http://localhost:${port}/something?url-param=a`);
validateResponseHeaders(res);
const text = await res.text();
const parsed = url.parse(text, true);
expect(parsed.pathname).toEqual('/something');
expect(parsed.query['url-param']).toEqual('a');
expect(parsed.query['route-param']).toEqual('b');
} finally {
dev.kill('SIGTERM');
}
});
test('[vercel dev] should maintain query when proxy passing', async () => {
const dir = fixture('query-proxy');
const { dev, port, readyResolver } = await testFixture(dir);
const dest = createServer((req, res) => {
res.end(req.url);
});
try {
await Promise.all([readyResolver, listen(dest, 0)]);
const destAddr = dest.address();
if (!destAddr || typeof destAddr === 'string') {
throw new Error('Unexpected HTTP address');
}
const res = await fetch(
`http://localhost:${port}/${destAddr.port}?url-param=a`
);
validateResponseHeaders(res);
const text = await res.text();
const parsed = url.parse(text, true);
expect(parsed.pathname).toEqual('/something');
expect(parsed.query['url-param']).toEqual('a');
expect(parsed.query['route-param']).toEqual('b');
} finally {
dest.close();
dev.kill('SIGTERM');
}
});
test('[vercel dev] should maintain query when dev server defines routes', async () => {
const dir = fixture('dev-server-query');
const { dev, port, readyResolver } = await testFixture(dir, {
env: {
VERCEL_DEV_COMMAND: 'next dev --port $PORT',
},
});
try {
await readyResolver;
const res = await fetch(`http://localhost:${port}/test?url-param=a`);
validateResponseHeaders(res);
const text = await res.text();
// Hacky way of getting the page payload from the response
// HTML since we don't have a HTML parser handy.
const json = text
.match(/<pre>(.*)<\/pre>/)![1]
.replace('</pre>', '')
.replace('<!-- -->', '')
.replace(/&amp;/g, '&')
.replace(/&quot;/g, '"');
const parsed = JSON.parse(json);
const query = url.parse(parsed.url, true).query;
expect(query['url-param']).toEqual('a');
expect(query['route-param']).toEqual('b');
} finally {
dev.kill('SIGTERM');
}
});
test('[vercel dev] should allow `cache-control` to be overwritten', async () => {
const dir = fixture('headers');
const { dev, port, readyResolver } = await testFixture(dir);
try {
await readyResolver;
const res = await fetch(
`http://localhost:${port}/?name=cache-control&value=immutable`
);
expect(res.headers.get('cache-control')).toEqual('immutable');
} finally {
dev.kill('SIGTERM');
}
});
test('[vercel dev] should send `etag` header for static files', async () => {
const dir = fixture('headers');
const { dev, port, readyResolver } = await testFixture(dir);
try {
await readyResolver;
const res = await fetch(`http://localhost:${port}/foo.txt`);
const expected = 'd263af8ab880c0b97eb6c5c125b5d44f9e5addd9';
expect(res.headers.get('etag')).toEqual(`"${expected}"`);
const body = await res.text();
expect(body.trim()).toEqual('hi');
} finally {
dev.kill('SIGTERM');
}
});
test('[vercel dev] should frontend dev server and routes', async () => {
const dir = fixture('dev-server-and-routes');
const { dev, port, readyResolver } = await testFixture(dir, {
env: {
VERCEL_DEV_COMMAND: 'next dev --port $PORT',
},
});
try {
await readyResolver;
let podId: string;
let res = await fetch(`http://localhost:${port}/`);
validateResponseHeaders(res);
podId = res.headers.get('x-vercel-id')!.match(/:(\w+)-/)![1];
let body = await res.text();
expect(body.includes('hello, this is the frontend')).toBeTruthy();
res = await fetch(`http://localhost:${port}/api/users`);
validateResponseHeaders(res, podId);
body = await res.text();
expect(body).toEqual('users');
res = await fetch(`http://localhost:${port}/api/users/1`);
validateResponseHeaders(res, podId);
body = await res.text();
expect(body).toEqual('users/1');
res = await fetch(`http://localhost:${port}/api/welcome`);
validateResponseHeaders(res, podId);
body = await res.text();
expect(body).toEqual('hello and welcome');
} finally {
dev.kill('SIGTERM');
}
});
test('[vercel dev] should support `@vercel/static` routing', async () => {
const dir = fixture('static-routes');
const { dev, port, readyResolver } = await testFixture(dir);
try {
await readyResolver;
const res = await fetch(`http://localhost:${port}/`);
expect(res.status).toEqual(200);
const body = await res.text();
expect(body.trim()).toEqual('<body>Hello!</body>');
} finally {
dev.kill('SIGTERM');
}
});
test('[vercel dev] should support `@vercel/static-build` routing', async () => {
const dir = fixture('static-build-routing');
const { dev, port, readyResolver } = await testFixture(dir);
try {
await readyResolver;
const res = await fetch(`http://localhost:${port}/api/date`);
expect(res.status).toEqual(200);
const body = await res.text();
expect(body.startsWith('The current date:')).toBeTruthy();
} finally {
dev.kill('SIGTERM');
}
});
test('[vercel dev] should support directory listing', async () => {
const dir = fixture('directory-listing');
const { dev, port, readyResolver } = await testFixture(dir);
try {
await readyResolver;
// Get directory listing
let res = await fetch(`http://localhost:${port}/`);
let body = await res.text();
expect(res.status).toEqual(200);
expect(body.includes('Index of')).toBeTruthy();
// Get a file
res = await fetch(`http://localhost:${port}/file.txt`);
body = await res.text();
expect(res.status).toEqual(200);
expect(body.trim()).toEqual('Hello from file!');
// Invoke a lambda
res = await fetch(`http://localhost:${port}/lambda.js`);
body = await res.text();
expect(res.status).toEqual(200);
expect(body).toEqual('Hello from Lambda!');
// Trigger a 404
res = await fetch(`http://localhost:${port}/does-not-exist`);
expect(res.status).toEqual(404);
} finally {
dev.kill('SIGTERM');
}
});
test('[vercel dev] should respond with 404 listing with Accept header support', async () => {
const dir = fixture('directory-listing');
const { dev, port, readyResolver } = await testFixture(dir);
try {
await readyResolver;
// HTML response
let res = await fetch(`http://localhost:${port}/does-not-exist`, {
headers: {
Accept: 'text/html',
},
});
expect(res.status).toEqual(404);
expect(res.headers.get('content-type')).toEqual('text/html; charset=utf-8');
let body = await res.text();
expect(body.startsWith('<!DOCTYPE html>')).toBeTruthy();
// JSON response
res = await fetch(`http://localhost:${port}/does-not-exist`, {
headers: {
Accept: 'application/json',
},
});
expect(res.status).toEqual(404);
expect(res.headers.get('content-type')).toEqual('application/json');
body = await res.text();
expect(body).toEqual(
'{"error":{"code":404,"message":"The page could not be found."}}\n'
);
// Plain text response
res = await fetch(`http://localhost:${port}/does-not-exist`);
expect(res.status).toEqual(404);
body = await res.text();
expect(res.headers.get('content-type')).toEqual(
'text/plain; charset=utf-8'
);
expect(body).toEqual('The page could not be found.\n\nNOT_FOUND\n');
} finally {
dev.kill('SIGTERM');
}
});
test('[vercel dev] should support `public` directory with zero config', async () => {
const dir = fixture('api-with-public');
const { dev, port, readyResolver } = await testFixture(dir);
try {
await readyResolver;
let res = await fetch(`http://localhost:${port}/api/user`);
let body = await res.text();
expect(body).toEqual('hello:user');
res = await fetch(`http://localhost:${port}/`);
body = await res.text();
expect(body.startsWith('<h1>hello world</h1>')).toBeTruthy();
} finally {
dev.kill('SIGTERM');
}
});
test('[vercel dev] should support static files with zero config', async () => {
const dir = fixture('api-with-static');
const { dev, port, readyResolver } = await testFixture(dir);
try {
await readyResolver;
let res = await fetch(`http://localhost:${port}/api/user`);
let body = await res.text();
expect(body).toEqual('bye:user');
res = await fetch(`http://localhost:${port}/`);
body = await res.text();
expect(body.startsWith('<h1>goodbye world</h1>')).toBeTruthy();
} finally {
dev.kill('SIGTERM');
}
});
test('[vercel dev] should support custom 404 routes', async () => {
const dir = fixture('custom-404');
const { dev, port, readyResolver } = await testFixture(dir);
try {
await readyResolver;
// Test custom 404 with static dest
let res = await fetch(`http://localhost:${port}/error.html`);
expect(res.status).toEqual(404);
let body = await res.text();
expect(body.trim()).toEqual('<div>Custom 404 page</div>');
// Test custom 404 with lambda dest
res = await fetch(`http://localhost:${port}/error.js`);
expect(res.status).toEqual(404);
body = await res.text();
expect(body).toEqual('Custom 404 Lambda\n');
// Test regular 404 still works
res = await fetch(`http://localhost:${port}/does-not-exist`);
expect(res.status).toEqual(404);
body = await res.text();
expect(body).toEqual('The page could not be found.\n\nNOT_FOUND\n');
} finally {
dev.kill('SIGTERM');
}
});
test('[vercel dev] prints `npm install` errors', async () => {
const dir = fixture('runtime-not-installed');
const result = await exec(dir);

View File

@@ -96,10 +96,16 @@ function shouldSkip(name, versions) {
return false;
}
function validateResponseHeaders(res) {
function validateResponseHeaders(res, podId) {
if (res.status < 500) {
expect(res.headers.get('server')).toEqual('Vercel');
expect(res.headers.get('cache-control').length > 0).toBeTruthy();
expect(res.headers.get('x-vercel-id')).toBeTruthy();
expect(res.headers.get('cache-control').length > 0).toBeTruthy;
if (podId) {
expect(
res.headers.get('x-vercel-id').includes(`::${podId}-`)
).toBeTruthy();
}
}
}

View File

@@ -1 +0,0 @@
export default () => <div>hello, this is the frontend</div>

View File

@@ -1,3 +1,4 @@
import ms from 'ms';
import { join } from 'path';
import { remove } from 'fs-extra';
import { getWriteableDirectory } from '@vercel/build-utils';
@@ -9,6 +10,8 @@ import {
import vercelNextPkg from '@vercel/next/package.json';
import vercelNodePkg from '@vercel/node/package.json';
jest.setTimeout(ms('20 seconds'));
describe('importBuilders()', () => {
it('should import built-in Builders', async () => {
const specs = new Set(['@vercel/node', '@vercel/next']);
@@ -16,9 +19,15 @@ describe('importBuilders()', () => {
expect(builders.size).toEqual(2);
expect(builders.get('@vercel/node')?.pkg).toMatchObject(vercelNodePkg);
expect(builders.get('@vercel/next')?.pkg).toMatchObject(vercelNextPkg);
expect(typeof builders.get('@vercel/node')?.builder.build).toEqual(
'function'
);
expect(typeof builders.get('@vercel/next')?.builder.build).toEqual(
'function'
);
});
it('should install 3rd party Builders', async () => {
it('should import 3rd party Builders', async () => {
const cwd = await getWriteableDirectory();
try {
const spec = 'vercel-deno@2.0.1';
@@ -30,6 +39,25 @@ describe('importBuilders()', () => {
expect(builders.get(spec)?.pkgPath).toEqual(
join(cwd, '.vercel/builders/node_modules/vercel-deno/package.json')
);
expect(typeof builders.get(spec)?.builder.build).toEqual('function');
} finally {
await remove(cwd);
}
});
it('should import legacy `@now/build-utils` Builders', async () => {
const cwd = await getWriteableDirectory();
try {
const spec = '@frontity/now@1.2.0';
const specs = new Set([spec]);
const builders = await importBuilders(specs, cwd, client.output);
expect(builders.size).toEqual(1);
expect(builders.get(spec)?.pkg.name).toEqual('@frontity/now');
expect(builders.get(spec)?.pkg.version).toEqual('1.2.0');
expect(builders.get(spec)?.pkgPath).toEqual(
join(cwd, '.vercel/builders/node_modules/@frontity/now/package.json')
);
expect(typeof builders.get(spec)?.builder.build).toEqual('function');
} finally {
await remove(cwd);
}

View File

@@ -1,438 +0,0 @@
import ms from 'ms';
import url from 'url';
import path from 'path';
import execa from 'execa';
import fs from 'fs-extra';
import fetch, { Response } from 'node-fetch';
import listen from 'async-listen';
import { createServer } from 'http';
import { client } from '../../../mocks/client';
import DevServer from '../../../../src/util/dev/server';
import { DevServerOptions } from '../../../../src/util/dev/types';
const IS_WINDOWS = process.platform === 'win32';
async function runNpmInstall(fixturePath: string) {
if (await fs.pathExists(path.join(fixturePath, 'package.json'))) {
return execa('yarn', ['install', '--network-timeout', '1000000'], {
cwd: fixturePath,
shell: true,
});
}
}
interface TestFixtureOptions extends Omit<DevServerOptions, 'output'> {
skip?: boolean;
}
const testFixture =
(
name: string,
fn: (server: DevServer) => Promise<void>,
options?: TestFixtureOptions
) =>
async () => {
if (options?.skip) {
console.log('Skipping this test for this platform.');
return;
}
let server: DevServer | undefined;
const fixturePath = path.join(__dirname, '../../fixtures/unit', name);
await runNpmInstall(fixturePath);
try {
server = new DevServer(fixturePath, {
...options,
output: client.output,
});
await server.start(0);
await fn(server);
} finally {
await server?.stop();
}
};
function validateResponseHeaders(res: Response, podId?: string) {
expect(res.headers.get('server')).toEqual('Vercel');
expect(res.headers.get('cache-control')!.length > 0).toBeTruthy();
expect(
/^dev1::(dev1::)?[0-9a-z]{5}-[1-9][0-9]+-[a-f0-9]{12}$/.test(
res.headers.get('x-vercel-id')!
)
).toBeTruthy();
if (podId) {
expect(
res.headers.get('x-vercel-id')!.startsWith(`dev1::${podId}`) ||
res.headers.get('x-vercel-id')!.startsWith(`dev1::dev1::${podId}`)
).toBeTruthy();
}
}
describe('DevServer', () => {
jest.setTimeout(ms('2m'));
it(
'should support request body',
testFixture('now-dev-request-body', async server => {
const body = { hello: 'world' };
// Test that `req.body` works in dev
let res = await fetch(`${server.address}/api/req-body`, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(body),
});
validateResponseHeaders(res);
expect(await res.json()).toMatchObject(body);
// Test that `req` "data" events work in dev
res = await fetch(`${server.address}/api/data-events`, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(body),
});
expect(await res.json()).toMatchObject(body);
})
);
it(
'should maintain query when invoking serverless function',
testFixture('now-dev-query-invoke', async server => {
const res = await fetch(`${server.address}/something?url-param=a`);
validateResponseHeaders(res);
const text = await res.text();
const parsed = url.parse(text, true);
expect(parsed.pathname).toEqual('/something');
expect(parsed.query['url-param']).toEqual('a');
expect(parsed.query['route-param']).toEqual('b');
})
);
it(
'should maintain query when proxy passing',
testFixture('now-dev-query-proxy', async server => {
const dest = createServer((req, res) => {
res.end(req.url);
});
await listen(dest, 0);
const addr = dest.address();
if (!addr || typeof addr === 'string') {
throw new Error('Unexpected HTTP address');
}
const { port } = addr;
try {
const res = await fetch(`${server.address}/${port}?url-param=a`);
validateResponseHeaders(res);
const text = await res.text();
const parsed = url.parse(text, true);
expect(parsed.pathname).toEqual('/something');
expect(parsed.query['url-param']).toEqual('a');
expect(parsed.query['route-param']).toEqual('b');
} finally {
dest.close();
}
})
);
it(
'should maintain query when builder defines routes',
testFixture(
'now-dev-next',
async server => {
const res = await fetch(`${server.address}/test?url-param=a`);
validateResponseHeaders(res);
const text = await res.text();
// Hacky way of getting the page payload from the response
// HTML since we don't have a HTML parser handy.
const json = text
.match(/<pre>(.*)<\/pre>/)![1]
.replace('</pre>', '')
.replace('<!-- -->', '')
.replace(/&amp;/g, '&')
.replace(/&quot;/g, '"');
const parsed = JSON.parse(json);
const query = url.parse(parsed.url, true).query;
expect(query['url-param']).toEqual('a');
expect(query['route-param']).toEqual('b');
},
{
devCommand: 'next dev --port $PORT',
}
)
);
it(
'should allow `cache-control` to be overwritten',
testFixture('now-dev-headers', async server => {
const res = await fetch(
`${server.address}/?name=cache-control&value=immutable`
);
expect(res.headers.get('cache-control')).toEqual('immutable');
})
);
it(
'should send `etag` header for static files',
testFixture('now-dev-headers', async server => {
const res = await fetch(`${server.address}/foo.txt`);
const expected = IS_WINDOWS
? '9dc423ab77c2e0446cd355256efff2ea1be27cbf'
: 'd263af8ab880c0b97eb6c5c125b5d44f9e5addd9';
expect(res.headers.get('etag')).toEqual(`"${expected}"`);
const body = await res.text();
expect(body.trim()).toEqual('hi');
})
);
it(
'should support default builds and routes',
testFixture(
'now-dev-default-builds-and-routes',
async server => {
let podId: string;
let res = await fetch(`${server.address}/`);
validateResponseHeaders(res);
podId = res.headers.get('x-vercel-id')!.match(/:(\w+)-/)![1];
let body = await res.text();
expect(body.includes('hello, this is the frontend')).toBeTruthy();
res = await fetch(`${server.address}/api/users`);
validateResponseHeaders(res, podId);
body = await res.text();
expect(body).toEqual('users');
res = await fetch(`${server.address}/api/users/1`);
validateResponseHeaders(res, podId);
body = await res.text();
expect(body).toEqual('users/1');
res = await fetch(`${server.address}/api/welcome`);
validateResponseHeaders(res, podId);
body = await res.text();
expect(body).toEqual('hello and welcome');
},
{
devCommand: 'next dev --port $PORT',
}
)
);
it(
'should support `@vercel/static` routing',
testFixture('now-dev-static-routes', async server => {
const res = await fetch(`${server.address}/`);
expect(res.status).toEqual(200);
const body = await res.text();
expect(body.trim()).toEqual('<body>Hello!</body>');
})
);
it(
'should support `@vercel/static-build` routing',
testFixture(
'now-dev-static-build-routing',
async server => {
const res = await fetch(`${server.address}/api/date`);
expect(res.status).toEqual(200);
const body = await res.text();
expect(body.startsWith('The current date:')).toBeTruthy();
},
{
// This test is currently failing on Windows, so skip for now:
// > Creating initial build
// $ serve -l $PORT src
// 'serve' is not recognized as an internal or external command,
// https://github.com/vercel/vercel/pull/6638/checks?check_run_id=3449662836
skip: IS_WINDOWS,
}
)
);
it(
'should support directory listing',
testFixture('now-dev-directory-listing', async server => {
// Get directory listing
let res = await fetch(`${server.address}/`);
let body = await res.text();
expect(res.status).toEqual(200);
expect(body.includes('Index of')).toBeTruthy();
// Get a file
res = await fetch(`${server.address}/file.txt`);
body = await res.text();
expect(res.status).toEqual(200);
expect(body.trim()).toEqual('Hello from file!');
// Invoke a lambda
res = await fetch(`${server.address}/lambda.js`);
body = await res.text();
expect(res.status).toEqual(200);
expect(body).toEqual('Hello from Lambda!');
// Trigger a 404
res = await fetch(`${server.address}/does-not-exist`);
expect(res.status).toEqual(404);
})
);
it(
'should support `public` directory with zero config',
testFixture('now-dev-api-with-public', async server => {
let res = await fetch(`${server.address}/api/user`);
let body = await res.text();
expect(body).toEqual('hello:user');
res = await fetch(`${server.address}/`);
body = await res.text();
expect(body.startsWith('<h1>hello world</h1>')).toBeTruthy();
})
);
it(
'should support static files with zero config',
testFixture('now-dev-api-with-static', async server => {
let res = await fetch(`${server.address}/api/user`);
let body = await res.text();
expect(body).toEqual('bye:user');
res = await fetch(`${server.address}/`);
body = await res.text();
expect(body.startsWith('<h1>goodbye world</h1>')).toBeTruthy();
})
);
it(
'should respond with 404 listing with Accept header support',
testFixture('now-dev-directory-listing', async server => {
// HTML response
let res = await fetch(`${server.address}/does-not-exist`, {
headers: {
Accept: 'text/html',
},
});
expect(res.status).toEqual(404);
expect(res.headers.get('content-type')).toEqual(
'text/html; charset=utf-8'
);
let body = await res.text();
expect(body.startsWith('<!DOCTYPE html>')).toBeTruthy();
// JSON response
res = await fetch(`${server.address}/does-not-exist`, {
headers: {
Accept: 'application/json',
},
});
expect(res.status).toEqual(404);
expect(res.headers.get('content-type')).toEqual('application/json');
body = await res.text();
expect(body).toEqual(
'{"error":{"code":404,"message":"The page could not be found."}}\n'
);
// Plain text response
res = await fetch(`${server.address}/does-not-exist`);
expect(res.status).toEqual(404);
body = await res.text();
expect(res.headers.get('content-type')).toEqual(
'text/plain; charset=utf-8'
);
expect(body).toEqual('The page could not be found.\n\nNOT_FOUND\n');
})
);
it(
'should support custom 404 routes',
testFixture('now-dev-custom-404', async server => {
// Test custom 404 with static dest
let res = await fetch(`${server.address}/error.html`);
expect(res.status).toEqual(404);
let body = await res.text();
expect(body.trim()).toEqual('<div>Custom 404 page</div>');
// Test custom 404 with lambda dest
res = await fetch(`${server.address}/error.js`);
expect(res.status).toEqual(404);
body = await res.text();
expect(body).toEqual('Custom 404 Lambda\n');
// Test regular 404 still works
res = await fetch(`${server.address}/does-not-exist`);
expect(res.status).toEqual(404);
body = await res.text();
expect(body).toEqual('The page could not be found.\n\nNOT_FOUND\n');
})
);
/*
it(
'should support edge middleware',
testFixture('edge-middleware', async server => {
const response = await fetch(`${server.address}/index.html`);
const body = await response.json();
expect(body).toEqual(
JSON.parse(
fs.readFileSync(
path.join(
__dirname,
'../../fixtures/unit/edge-middleware/response.json'
),
'utf8'
)
)
);
})
);
it(
'should work with middleware written in typescript',
testFixture('edge-middleware-ts', async server => {
const response = await fetch(`${server.address}/index.html`);
const body = await response.text();
expect(body).toStrictEqual('response');
})
);
it(
'should render an error page when the middleware throws',
testFixture('edge-middleware-error', async server => {
const response = await fetch(`${server.address}/index.html`);
const body = await response.text();
expect(body).toStrictEqual(
'A server error has occurred\n\nEDGE_FUNCTION_INVOCATION_FAILED\n'
);
})
);
it(
'should render an error page when the middleware returns not a Response',
testFixture('edge-middleware-invalid-response', async server => {
const response = await fetch(`${server.address}/index.html`);
const body = await response.text();
expect(body).toStrictEqual(
'A server error has occurred\n\nEDGE_FUNCTION_INVOCATION_FAILED\n'
);
})
);
it(
'should run middleware in strict mode',
testFixture('edge-middleware-strict', async server => {
const response = await fetch(`${server.address}/index.html`);
const body = await response.text();
expect(body).toStrictEqual('is strict mode? yes');
})
);
*/
});

View File

@@ -1,7 +1,8 @@
import { basename, join } from 'path';
import { getVercelDirectory } from '../../../../src/util/projects/link';
const fixture = (name: string) => join(__dirname, '../../fixtures/unit', name);
const fixture = (name: string) =>
join(__dirname, '../../../fixtures/unit', name);
describe('getVercelDirectory', () => {
it('should return ".vercel"', () => {

View File

@@ -8,7 +8,8 @@ import { useUser } from '../../../mocks/user';
type UnPromisify<T> = T extends Promise<infer U> ? U : T;
const fixture = (name: string) => join(__dirname, '../../fixtures/unit', name);
const fixture = (name: string) =>
join(__dirname, '../../../fixtures/unit', name);
describe('getLinkedProject', () => {
it('should fail to return a link when token is missing', async () => {

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "11.0.4-canary.1",
"version": "11.0.4-canary.2",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -42,7 +42,7 @@
]
},
"dependencies": {
"@vercel/build-utils": "3.1.1-canary.1",
"@vercel/build-utils": "3.1.1-canary.2",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",
"async-sema": "3.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/go",
"version": "1.4.4-canary.1",
"version": "1.4.4-canary.2",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
@@ -25,7 +25,7 @@
"@types/fs-extra": "^5.0.5",
"@types/node-fetch": "^2.3.0",
"@types/tar": "^4.0.0",
"@vercel/build-utils": "3.1.1-canary.1",
"@vercel/build-utils": "3.1.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/next",
"version": "2.8.67-canary.1",
"version": "2.8.67-canary.2",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
@@ -45,8 +45,8 @@
"@types/semver": "6.0.0",
"@types/text-table": "0.2.1",
"@types/webpack-sources": "3.2.0",
"@vercel/build-utils": "3.1.1-canary.1",
"@vercel/nft": "0.19.0",
"@vercel/build-utils": "3.1.1-canary.2",
"@vercel/nft": "0.19.1",
"@vercel/routing-utils": "1.13.3",
"async-sema": "3.0.1",
"buffer-crc32": "0.2.13",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/node",
"version": "1.15.4-canary.1",
"version": "1.15.4-canary.2",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -45,9 +45,9 @@
"@types/etag": "1.8.0",
"@types/jest": "27.4.1",
"@types/test-listen": "1.1.0",
"@vercel/build-utils": "3.1.1-canary.1",
"@vercel/build-utils": "3.1.1-canary.2",
"@vercel/ncc": "0.24.0",
"@vercel/nft": "0.19.0",
"@vercel/nft": "0.19.1",
"content-type": "1.0.4",
"cookie": "0.4.0",
"etag": "1.8.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/python",
"version": "2.3.4-canary.1",
"version": "2.3.4-canary.2",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -23,7 +23,7 @@
"devDependencies": {
"@types/execa": "^0.9.0",
"@types/jest": "27.4.1",
"@vercel/build-utils": "3.1.1-canary.1",
"@vercel/build-utils": "3.1.1-canary.2",
"@vercel/ncc": "0.24.0",
"execa": "^1.0.0",
"typescript": "4.3.4"

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/redwood",
"version": "0.8.4-canary.1",
"version": "0.8.4-canary.2",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://vercel.com/docs",
@@ -20,7 +20,7 @@
"prepublishOnly": "node build.js"
},
"dependencies": {
"@vercel/nft": "0.19.0",
"@vercel/nft": "0.19.1",
"@vercel/routing-utils": "1.13.3",
"semver": "6.1.1"
},
@@ -28,6 +28,6 @@
"@types/aws-lambda": "8.10.19",
"@types/node": "*",
"@types/semver": "6.0.0",
"@vercel/build-utils": "3.1.1-canary.1"
"@vercel/build-utils": "3.1.1-canary.2"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/remix",
"version": "0.0.2-canary.1",
"version": "0.0.2-canary.2",
"license": "MIT",
"main": "./dist/index.js",
"homepage": "https://vercel.com/docs",
@@ -22,12 +22,12 @@
],
"dependencies": {
"@remix-run/vercel": "1.4.3",
"@vercel/nft": "0.19.0"
"@vercel/nft": "0.19.1"
},
"devDependencies": {
"@types/jest": "27.5.1",
"@types/node": "*",
"@vercel/build-utils": "3.1.1-canary.1",
"@vercel/build-utils": "3.1.1-canary.2",
"typescript": "4.6.4"
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "@vercel/ruby",
"author": "Nathan Cahill <nathan@nathancahill.com>",
"version": "1.3.7-canary.1",
"version": "1.3.7-canary.2",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
@@ -23,7 +23,7 @@
"devDependencies": {
"@types/fs-extra": "8.0.0",
"@types/semver": "6.0.0",
"@vercel/build-utils": "3.1.1-canary.1",
"@vercel/build-utils": "3.1.1-canary.2",
"@vercel/ncc": "0.24.0",
"execa": "2.0.4",
"fs-extra": "^7.0.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/static-build",
"version": "0.25.3-canary.1",
"version": "0.25.3-canary.2",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/build-step",
@@ -37,7 +37,7 @@
"@types/ms": "0.7.31",
"@types/node-fetch": "2.5.4",
"@types/promise-timeout": "1.3.0",
"@vercel/build-utils": "3.1.1-canary.1",
"@vercel/build-utils": "3.1.1-canary.2",
"@vercel/frameworks": "0.9.2-canary.0",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "1.13.3",

View File

@@ -88,7 +88,9 @@ async function nowDeploy(bodies, randomness, uploadNowJson) {
const { readyState } = deployment;
if (readyState === 'ERROR') {
logWithinTest('state is ERROR, throwing');
const error = new Error(`State of https://${deploymentUrl} is ERROR`);
const error = new Error(
`State of https://${deploymentUrl} is ERROR: ${deployment.errorMessage}`
);
error.deployment = deployment;
throw error;
}

View File

@@ -2925,10 +2925,10 @@
resolved "https://registry.yarnpkg.com/@vercel/ncc/-/ncc-0.24.0.tgz#a2e8783a185caa99b5d8961a57dfc9665de16296"
integrity sha512-crqItMcIwCkvdXY/V3/TzrHJQx6nbIaRqE1cOopJhgGX6izvNov40SmD//nS5flfEvdK54YGjwVVq+zG6crjOg==
"@vercel/nft@0.19.0":
version "0.19.0"
resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.19.0.tgz#56f2a17e0685b5c052158aafec3a9f31552de5b4"
integrity sha512-diHIP3/OHq/4wSkS4JJ7LqY6NAQOpCr83vpNZuKqx2PV7zrhwUH8CZ9QCu0SHkZh3LYaV0zcbGusVxTNw3DMvA==
"@vercel/nft@0.19.1":
version "0.19.1"
resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.19.1.tgz#dcd3c20d7ef14d2050244e7d5edcfecc7303dd46"
integrity sha512-klR5oN7S3WJsZz0r6Xsq7o8YlFEyU3/00VmlpZzIPVFzKfbcEjXo/sVR5lQBUqNKuOzhcbxaFtzW9aOyHjmPYA==
dependencies:
"@mapbox/node-pre-gyp" "^1.0.5"
acorn "^8.6.0"