Compare commits

..

21 Commits

Author SHA1 Message Date
Steven
ad436313e1 Publish Stable
- @vercel/build-utils@3.1.1
 - vercel@24.2.5
 - @vercel/client@11.0.4
 - @vercel/frameworks@1.0.0
 - @vercel/go@1.4.4
 - @vercel/next@2.9.0
 - @vercel/node@1.15.4
 - @vercel/python@2.3.4
 - @vercel/redwood@0.8.4
 - @vercel/remix@0.0.2
 - @vercel/ruby@1.3.7
 - @vercel/static-build@0.26.0
2022-05-31 10:44:36 -04:00
Lee Robinson
c414288b2f [examples] Update SvelteKit example with latest boilerplate (#7892)
Replaces https://github.com/vercel/vercel/pull/7674 with the latest SvelteKit boilerplate.
2022-05-30 07:57:46 +00:00
Steven
b07ff7431f [tests] Bump turbo to 1.2.14 (#7887)
This should fix the caching problem

https://github.com/vercel/turborepo/releases/tag/v1.2.14
2022-05-27 23:46:38 +00:00
Steven
79fde4475c [build-utils][go][next][redwood][static-build] Fix path delimiter on windows (#7881)
This PR fixes an issue where the path delimiter was incorrect for windows and caused yarn (and other global CLIs) to no longer be found.

https://github.com/vercel/vercel/runs/6602572000?check_suite_focus=true#step:13:1357
2022-05-27 22:59:28 +00:00
Nathan Rajlich
855197c699 [node] Remove mkdirp-promise dependency (#7878)
There's no need to use this module since Node.js has `recursive: true` since Node v10.

Co-authored-by: Steven <steven@ceriously.com>
2022-05-26 15:07:09 -04:00
Nathan Rajlich
fbd9080859 [tests] Add vc build integration test (#7876)
[cli] Add `vc build` integration test

Adds a `vc build` integration test to ensure the ncc'd CLI works as
expected to supplement the unit tests from #7869.

Co-authored-by: Steven <steven@ceriously.com>
2022-05-26 15:05:31 -04:00
Steven
b5c5b7b82c [tests] Fix tests with promises (#7880)
These tests are asynchronous and would sometimes fail if the second call started before the first call finished. This PR fixes the usage since the function returns a promise.
2022-05-26 10:28:49 -04:00
Gal Schlezinger
0a072ee850 Publish Canary
- vercel@24.2.5-canary.3
 - @vercel/next@2.8.67-canary.3
2022-05-26 09:20:12 +03:00
Dalpat Rathore
0b56caba45 [docs] Fix CONTRIBUTING.md grammar (#7575)
Fix: CONTRIBUTING.md grammar

Co-authored-by: Steven <steven@ceriously.com>
2022-05-25 17:44:41 -04:00
Steven
ab3db60824 [tests] Conditionally set env vars (#7874)
This PR is a follow up to #7873 which didn't work as intended
2022-05-25 17:36:20 -04:00
Steven
f2f2ff2c67 [tests] Skip env vars for forks (#7873)
This fixes an issue when an external contributor submits a PR and the remote cache is not accessible 

```
$ turbo run build
 ERROR No caches are enabled. You can try "turbo login", "turbo link", or ensuring you are not passing --remote-only to enable caching:  <nil>
• Packages in scope: @vercel/build-utils, @vercel/client, @vercel/frameworks, @vercel/go, @vercel/next, @vercel/node, @vercel/node-bridge, @vercel/python, @vercel/redwood, @vercel/remix, @vercel/routing-utils, @vercel/ruby, @vercel/static-build, @vercel/static-config, vercel
```
2022-05-25 16:54:35 -04:00
Gal Schlezinger
ba7dafff71 [next] Allow edge api endpoints in Next.js (#7855)
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2022-05-25 15:19:14 +03:00
JJ Kasper
987fb4d4f7 [next] Ensure test-next-local is run in CI (#7868)
* Ensure test-next-local is run in CI

* update turbo config

* update timeout

* update flakey test
2022-05-24 14:22:40 -05:00
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
136 changed files with 3870 additions and 2558 deletions

View File

@@ -23,7 +23,7 @@ Make sure all the tests pass before making changes.
## Verifying your change ## Verifying your change
Once you are done with your changes (we even suggest doing it along the way), make sure all the test still pass by running: Once you are done with your changes (we even suggest doing it along the way), make sure all the tests still pass by running:
``` ```
yarn test-unit yarn test-unit
@@ -64,7 +64,7 @@ Integration tests create deployments to your Vercel account using the `test` pro
x-now-trace=iad1] x-now-trace=iad1]
``` ```
In such cases you can visit the URL of the failed deployment and append `/_logs` so see the build error. In the case above, that would be https://test-8ashcdlew.vercel.app/_logs In such cases, you can visit the URL of the failed deployment and append `/_logs` to see the build error. In the case above, that would be https://test-8ashcdlew.vercel.app/_logs
The logs of this deployment will contain the actual error which may help you to understand what went wrong. The logs of this deployment will contain the actual error which may help you to understand what went wrong.
@@ -82,11 +82,11 @@ nodeFileTrace(['path/to/entrypoint.js'], {
.then(e => console.error(e)); .then(e => console.error(e));
``` ```
When you run this script, you'll see all imported files. If anything file is missing, the bug is in [@vercel/nft](https://github.com/vercel/nft) and not the Builder. When you run this script, you'll see all the imported files. If anything file is missing, the bug is in [@vercel/nft](https://github.com/vercel/nft) and not the Builder.
## Deploy a Builder with existing project ## Deploy a Builder with existing project
Sometimes you want to test changes to a Builder against an existing project, maybe with `vercel dev` or an actual deployment. You can avoid publishing every Builder change to npm by uploading the Builder as a tarball. Sometimes you want to test changes to a Builder against an existing project, maybe with `vercel dev` or actual deployment. You can avoid publishing every Builder change to npm by uploading the Builder as a tarball.
1. Change directory to the desired Builder `cd ./packages/node` 1. Change directory to the desired Builder `cd ./packages/node`
2. Run `yarn build` to compile typescript and other build steps 2. Run `yarn build` to compile typescript and other build steps

View File

@@ -16,13 +16,15 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest] os: [ubuntu-latest]
node: [12] node: [14]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env:
TURBO_REMOTE_ONLY: true
TURBO_TEAM: vercel
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
steps: steps:
- name: Conditionally set remote env
if: github.event.pull_request.head.repo.full_name == github.repository
run: |
echo "TURBO_REMOTE_ONLY=true" >> $GITHUB_ENV
echo "TURBO_TEAM=vercel" >> $GITHUB_ENV
echo "TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}" >> $GITHUB_ENV
- uses: actions/setup-go@v2 - uses: actions/setup-go@v2
with: with:
go-version: '1.13.15' go-version: '1.13.15'

View File

@@ -18,11 +18,13 @@ jobs:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
node: [14] node: [14]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env:
TURBO_REMOTE_ONLY: true
TURBO_TEAM: vercel
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
steps: steps:
- name: Conditionally set remote env
if: github.event.pull_request.head.repo.full_name == github.repository
run: |
echo "TURBO_REMOTE_ONLY=true" >> $GITHUB_ENV
echo "TURBO_TEAM=vercel" >> $GITHUB_ENV
echo "TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}" >> $GITHUB_ENV
- uses: actions/setup-go@v2 - uses: actions/setup-go@v2
with: with:
go-version: '1.13.15' go-version: '1.13.15'

View File

@@ -38,10 +38,6 @@ jobs:
runs-on: ${{ matrix.runner }} runs-on: ${{ matrix.runner }}
name: ${{matrix.scriptName}} (${{matrix.packageName}}, ${{matrix.chunkNumber}}, ${{ matrix.runner }}) name: ${{matrix.scriptName}} (${{matrix.packageName}}, ${{matrix.chunkNumber}}, ${{ matrix.runner }})
if: ${{ needs.setup.outputs['tests'] != '[]' }} if: ${{ needs.setup.outputs['tests'] != '[]' }}
env:
TURBO_REMOTE_ONLY: true
TURBO_TEAM: vercel
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
needs: needs:
- setup - setup
strategy: strategy:
@@ -49,6 +45,12 @@ jobs:
matrix: matrix:
include: ${{ fromJson(needs.setup.outputs['tests']) }} include: ${{ fromJson(needs.setup.outputs['tests']) }}
steps: steps:
- name: Conditionally set remote env
if: github.event.pull_request.head.repo.full_name == github.repository
run: |
echo "TURBO_REMOTE_ONLY=true" >> $GITHUB_ENV
echo "TURBO_TEAM=vercel" >> $GITHUB_ENV
echo "TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}" >> $GITHUB_ENV
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
fetch-depth: 2 fetch-depth: 2

View File

@@ -7,3 +7,4 @@ node_modules
.env.* .env.*
!.env.example !.env.example
.vercel .vercel
.output

View File

@@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

View File

@@ -0,0 +1,6 @@
{
"useTabs": false,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100
}

View File

@@ -14,31 +14,29 @@ If you're seeing this, you've probably already done this step. Congrats!
```bash ```bash
# create a new project in the current directory # create a new project in the current directory
npm init svelte@next npm init svelte
# create a new project in my-app # create a new project in my-app
npm init svelte@next my-app npm init svelte my-app
``` ```
> Note: the `@next` is temporary
## Developing ## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: Once you've created a project and installed dependencies with `pnpm install`, start a development server:
```bash ```bash
npm run dev pnpm run dev
# or start the server and open the app in a new browser tab # or start the server and open the app in a new browser tab
npm run dev -- --open pnpm run dev -- --open
``` ```
## Building ## Building
This uses the adapter-auto for SvelteKit, which detects Vercel and runs adapter-vercel on your behalf. To create a production version of your app:
```bash ```bash
npm run build pnpm run build
``` ```
> You can preview the built app with `npm run preview`, regardless of whether you installed an adapter. This should _not_ be used to serve your app in production. You can preview the production build with `npm run preview`.

View File

@@ -1,10 +1,13 @@
{ {
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "allowJs": true,
"paths": { "checkJs": true,
"$lib": ["src/lib"], "esModuleInterop": true,
"$lib/*": ["src/lib/*"] "forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true
} }
},
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,29 @@
{ {
"private": true, "private": true,
"name": "sveltekit",
"version": "0.0.1",
"scripts": { "scripts": {
"dev": "svelte-kit dev", "dev": "svelte-kit dev",
"build": "svelte-kit build", "build": "svelte-kit build",
"package": "svelte-kit package", "package": "svelte-kit package",
"preview": "svelte-kit preview" "preview": "svelte-kit preview",
"prepare": "svelte-kit sync",
"check": "svelte-check --tsconfig ./jsconfig.json",
"check:watch": "svelte-check --tsconfig ./jsconfig.json --watch",
"lint": "prettier --check --plugin-search-dir=. .",
"format": "prettier --write --plugin-search-dir=. ."
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-auto": "next", "@sveltejs/adapter-auto": "next",
"@sveltejs/kit": "next", "@sveltejs/kit": "next",
"svelte": "^3.46.0" "@types/cookie": "^0.4.1",
"prettier": "^2.5.1",
"prettier-plugin-svelte": "^2.5.0",
"svelte": "^3.46.0",
"svelte-check": "^2.2.6",
"typescript": "~4.6.2"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@fontsource/fira-mono": "^4.5.0", "@fontsource/fira-mono": "^4.5.0",
"@lukeed/uuid": "^2.0.0",
"cookie": "^0.4.1" "cookie": "^0.4.1"
} }
} }

1633
examples/sveltekit/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

15
examples/sveltekit/src/app.d.ts vendored Normal file
View File

@@ -0,0 +1,15 @@
/// <reference types="@sveltejs/kit" />
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare namespace App {
interface Locals {
userid: string;
}
// interface Platform {}
// interface Session {}
// interface Stuff {}
}

View File

@@ -2,12 +2,11 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="description" content="Svelte demo app" /> <link rel="icon" href="%sveltekit.assets%/favicon.png" />
<link rel="icon" href="%svelte.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
%svelte.head% %sveltekit.head%
</head> </head>
<body> <body>
<div>%svelte.body%</div> <div>%sveltekit.body%</div>
</body> </body>
</html> </html>

View File

@@ -1,13 +1,13 @@
import cookie from 'cookie'; import * as cookie from 'cookie';
import { v4 as uuid } from '@lukeed/uuid';
/** @type {import('@sveltejs/kit').Handle} */
export const handle = async ({ event, resolve }) => { export const handle = async ({ event, resolve }) => {
const cookies = cookie.parse(event.request.headers.get('cookie') || ''); const cookies = cookie.parse(event.request.headers.get('cookie') || '');
event.locals.userid = cookies.userid || uuid(); event.locals.userid = cookies['userid'] || crypto.randomUUID();
const response = await resolve(event); const response = await resolve(event);
if (!cookies.userid) { if (!cookies['userid']) {
// if this is the first time the user has visited this app, // if this is the first time the user has visited this app,
// set a cookie so that we recognise them when they return // set a cookie so that we recognise them when they return
response.headers.set( response.headers.set(

View File

@@ -7,6 +7,10 @@
$: displayed_count.set(count); $: displayed_count.set(count);
$: offset = modulo($displayed_count, 1); $: offset = modulo($displayed_count, 1);
/**
* @param {number} n
* @param {number} m
*/
function modulo(n, m) { function modulo(n, m) {
// handle negative numbers // handle negative numbers
return ((n % m) + m) % m; return ((n % m) + m) % m;
@@ -50,6 +54,7 @@
justify-content: center; justify-content: center;
border: 0; border: 0;
background-color: transparent; background-color: transparent;
touch-action: manipulation;
color: var(--text-color); color: var(--text-color);
font-size: 2rem; font-size: 2rem;
} }

View File

@@ -2,9 +2,36 @@ import { invalidate } from '$app/navigation';
// this action (https://svelte.dev/tutorial/actions) allows us to // this action (https://svelte.dev/tutorial/actions) allows us to
// progressively enhance a <form> that already works without JS // progressively enhance a <form> that already works without JS
/**
* @param {HTMLFormElement} form
* @param {{
* pending?: ({ data, form }: { data: FormData; form: HTMLFormElement }) => void;
* error?: ({
* data,
* form,
* response,
* error
* }: {
* data: FormData;
* form: HTMLFormElement;
* response: Response | null;
* error: Error | null;
* }) => void;
* result?: ({
* data,
* form,
* response
* }: {
* data: FormData;
* response: Response;
* form: HTMLFormElement;
* }) => void;
* }} [opts]
*/
export function enhance(form, { pending, error, result } = {}) { export function enhance(form, { pending, error, result } = {}) {
let current_token; let current_token;
/** @param {SubmitEvent} e */
async function handle_submit(e) { async function handle_submit(e) {
const token = (current_token = {}); const token = (current_token = {});
@@ -37,7 +64,7 @@ export function enhance(form, { pending, error, result } = {}) {
console.error(await response.text()); console.error(await response.text());
} }
} catch (e) { } catch (e) {
if (error) { if (error && e instanceof Error) {
error({ data, form, error: e, response: null }); error({ data, form, error: e, response: null });
} else { } else {
throw e; throw e;

View File

@@ -16,6 +16,7 @@
<svelte:head> <svelte:head>
<title>About</title> <title>About</title>
<meta name="description" content="About this app" />
</svelte:head> </svelte:head>
<div class="content"> <div class="content">
@@ -26,8 +27,7 @@
following into your command line and following the prompts: following into your command line and following the prompts:
</p> </p>
<!-- TODO lose the @next! --> <pre>npm init svelte</pre>
<pre>npm init svelte@next</pre>
<p> <p>
The page you're looking at is purely static HTML, with no client-side interactivity needed. The page you're looking at is purely static HTML, with no client-side interactivity needed.

View File

@@ -8,6 +8,7 @@
<svelte:head> <svelte:head>
<title>Home</title> <title>Home</title>
<meta name="description" content="Svelte demo app" />
</svelte:head> </svelte:head>
<section> <section>

View File

@@ -11,6 +11,11 @@
const base = 'https://api.svelte.dev'; const base = 'https://api.svelte.dev';
/**
* @param {string} method
* @param {string} resource
* @param {Record<string, unknown>} [data]
*/
export function api(method, resource, data) { export function api(method, resource, data) {
return fetch(`${base}/${resource}`, { return fetch(`${base}/${resource}`, {
method, method,

View File

@@ -1,5 +1,6 @@
import { api } from './_api'; import { api } from './_api';
/** @type {import('./__types').RequestHandler} */
export const get = async ({ locals }) => { export const get = async ({ locals }) => {
// locals.userid comes from src/hooks.js // locals.userid comes from src/hooks.js
const response = await api('get', `todos/${locals.userid}`); const response = await api('get', `todos/${locals.userid}`);
@@ -27,6 +28,7 @@ export const get = async ({ locals }) => {
}; };
}; };
/** @type {import('./index').RequestHandler} */
export const post = async ({ request, locals }) => { export const post = async ({ request, locals }) => {
const form = await request.formData(); const form = await request.formData();
@@ -46,6 +48,7 @@ const redirect = {
} }
}; };
/** @type {import('./index').RequestHandler} */
export const patch = async ({ request, locals }) => { export const patch = async ({ request, locals }) => {
const form = await request.formData(); const form = await request.formData();
@@ -57,6 +60,7 @@ export const patch = async ({ request, locals }) => {
return redirect; return redirect;
}; };
/** @type {import('./index').RequestHandler} */
export const del = async ({ request, locals }) => { export const del = async ({ request, locals }) => {
const form = await request.formData(); const form = await request.formData();

View File

@@ -3,11 +3,23 @@
import { scale } from 'svelte/transition'; import { scale } from 'svelte/transition';
import { flip } from 'svelte/animate'; import { flip } from 'svelte/animate';
/**
* @typedef {{
* uid: string;
* created_at: Date;
* text: string;
* done: boolean;
* pending_delete: boolean;
* }} Todo
*/
/** @type {Todo[]} */
export let todos; export let todos;
</script> </script>
<svelte:head> <svelte:head>
<title>Todos</title> <title>Todos</title>
<meta name="description" content="A todo list app" />
</svelte:head> </svelte:head>
<div class="todos"> <div class="todos">

View File

@@ -31,7 +31,7 @@
"prettier": "2.6.2", "prettier": "2.6.2",
"ts-eager": "2.0.2", "ts-eager": "2.0.2",
"ts-jest": "28.0.0-next.1", "ts-jest": "28.0.0-next.1",
"turbo": "1.2.9" "turbo": "1.2.14"
}, },
"scripts": { "scripts": {
"lerna": "lerna", "lerna": "lerna",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/build-utils", "name": "@vercel/build-utils",
"version": "3.1.1-canary.1", "version": "3.1.1",
"license": "MIT", "license": "MIT",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.js", "types": "./dist/index.d.js",
@@ -31,7 +31,7 @@
"@types/node-fetch": "^2.1.6", "@types/node-fetch": "^2.1.6",
"@types/semver": "6.0.0", "@types/semver": "6.0.0",
"@types/yazl": "2.4.2", "@types/yazl": "2.4.2",
"@vercel/frameworks": "0.9.2-canary.0", "@vercel/frameworks": "1.0.0",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"aggregate-error": "3.0.1", "aggregate-error": "3.0.1",
"async-retry": "1.2.3", "async-retry": "1.2.3",

View File

@@ -205,7 +205,9 @@ export function getSpawnOptions(
if (!meta.isDev) { if (!meta.isDev) {
// Ensure that the selected Node version is at the beginning of the `$PATH` // Ensure that the selected Node version is at the beginning of the `$PATH`
opts.env.PATH = `/node${nodeVersion.major}/bin:${opts.env.PATH}`; opts.env.PATH = `/node${nodeVersion.major}/bin${path.delimiter}${
opts.env.PATH || process.env.PATH
}`;
} }
return opts; return opts;
@@ -433,13 +435,13 @@ export function getEnvForPackageManager({
(nodeVersion?.major || 0) < 16 (nodeVersion?.major || 0) < 16
) { ) {
// Ensure that npm 7 is at the beginning of the `$PATH` // Ensure that npm 7 is at the beginning of the `$PATH`
newEnv.PATH = `/node16/bin-npm7:${env.PATH}`; newEnv.PATH = `/node16/bin-npm7${path.delimiter}${env.PATH}`;
console.log('Detected `package-lock.json` generated by npm 7...'); console.log('Detected `package-lock.json` generated by npm 7...');
} }
} else if (cliType === 'pnpm') { } else if (cliType === 'pnpm') {
if (typeof lockfileVersion === 'number' && lockfileVersion === 5.4) { if (typeof lockfileVersion === 'number' && lockfileVersion === 5.4) {
// Ensure that pnpm 7 is at the beginning of the `$PATH` // Ensure that pnpm 7 is at the beginning of the `$PATH`
newEnv.PATH = `/pnpm7/node_modules/.bin:${env.PATH}`; newEnv.PATH = `/pnpm7/node_modules/.bin${path.delimiter}${env.PATH}`;
console.log('Detected `pnpm-lock.yaml` generated by pnpm 7...'); console.log('Detected `pnpm-lock.yaml` generated by pnpm 7...');
} }
} else { } else {

View File

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

@@ -17,5 +17,13 @@ checkPkgOrThrow('exeggcute');
// This is to satisfy `@vercel/static-build` which needs a `dist` directory. // This is to satisfy `@vercel/static-build` which needs a `dist` directory.
const { exec } = require('exeggcute'); const { exec } = require('exeggcute');
exec('mkdir dist', __dirname); exec('mkdir dist', __dirname)
exec('echo "node-env:RANDOMNESS_PLACEHOLDER" > dist/index.html', __dirname); .then(() => {
exec(
'echo "node-env:RANDOMNESS_PLACEHOLDER" > dist/index.html',
__dirname
).then(() => {
console.log('Success');
});
})
.catch(console.error);

View File

@@ -6,5 +6,10 @@ const b = require('./b');
a(); a();
b(); b();
exec('mkdir public', __dirname); exec('mkdir public', __dirname)
exec('echo "Hello, World!" > public/index.html', __dirname); .then(() => {
exec('echo "Hello, World!" > public/index.html', __dirname).then(() => {
console.log('Success');
});
})
.catch(console.error);

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', '23-pnpm-workspaces',
'27-yarn-workspaces', '27-yarn-workspaces',
'28-turborepo-with-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 // eslint-disable-next-line no-restricted-syntax

View File

@@ -5,10 +5,10 @@ import { FixtureFilesystem } from './utils/fixture-filesystem';
describe('monorepo-managers', () => { describe('monorepo-managers', () => {
describe.each([ describe.each([
['turbo', '28-turborepo-with-yarn-workspaces'], ['28-turborepo-with-yarn-workspaces', 'turbo'],
['turbo', '29-turborepo-in-package-json'], ['31-turborepo-in-package-json', 'turbo'],
[null, '22-pnpm'], ['22-pnpm', null],
])('with detectFramework', (frameworkSlug, fixturePath) => { ])('with detectFramework', (fixturePath, frameworkSlug) => {
const testName = frameworkSlug const testName = frameworkSlug
? `should detect a ${frameworkSlug} workspace for ${fixturePath}` ? `should detect a ${frameworkSlug} workspace for ${fixturePath}`
: `should not detect a monorepo manager 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('workspace-managers', () => {
describe.each([ describe.each([
['npm', '21-npm-workspaces'], ['21-npm-workspaces', 'npm'],
['pnpm', '23-pnpm-workspaces'], ['23-pnpm-workspaces', 'pnpm'],
['yarn', '27-yarn-workspaces'], ['27-yarn-workspaces', 'yarn'],
['yarn', '25-multiple-lock-files-yarn'], ['25-multiple-lock-files-yarn', 'yarn'],
['pnpm', '26-multiple-lock-files-pnpm'], ['26-multiple-lock-files-pnpm', 'pnpm'],
[null, '22-pnpm'], ['22-pnpm', null],
])('with detectFramework', (frameworkSlug, fixturePath) => { ])('with detectFramework', (fixturePath, frameworkSlug) => {
const testName = frameworkSlug const testName = frameworkSlug
? `should detect a ${frameworkSlug} workspace for ${fixturePath}` ? `should detect a ${frameworkSlug} workspace for ${fixturePath}`
: `should not detect framework for ${fixturePath}`; : `should not detect framework for ${fixturePath}`;

View File

@@ -1,4 +1,5 @@
import assert from 'assert'; import assert from 'assert';
import { delimiter } from 'path';
import { getEnvForPackageManager } from '../src'; import { getEnvForPackageManager } from '../src';
describe('Test `getEnvForPackageManager()`', () => { describe('Test `getEnvForPackageManager()`', () => {
@@ -34,7 +35,7 @@ describe('Test `getEnvForPackageManager()`', () => {
}, },
want: { want: {
FOO: 'bar', FOO: 'bar',
PATH: `/node16/bin-npm7:foo`, PATH: `/node16/bin-npm7${delimiter}foo`,
}, },
}, },
{ {
@@ -97,7 +98,7 @@ describe('Test `getEnvForPackageManager()`', () => {
}, },
want: { want: {
FOO: 'bar', FOO: 'bar',
PATH: '/pnpm7/node_modules/.bin:foo', PATH: `/pnpm7/node_modules/.bin${delimiter}foo`,
}, },
}, },
{ {

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", "name": "vercel",
"version": "24.2.5-canary.1", "version": "24.2.5",
"preferGlobal": true, "preferGlobal": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "The command-line interface for Vercel", "description": "The command-line interface for Vercel",
@@ -43,15 +43,15 @@
"node": ">= 12" "node": ">= 12"
}, },
"dependencies": { "dependencies": {
"@vercel/build-utils": "3.1.1-canary.1", "@vercel/build-utils": "3.1.1",
"@vercel/go": "1.4.4-canary.1", "@vercel/go": "1.4.4",
"@vercel/next": "2.8.67-canary.1", "@vercel/next": "2.9.0",
"@vercel/node": "1.15.4-canary.1", "@vercel/node": "1.15.4",
"@vercel/python": "2.3.4-canary.1", "@vercel/python": "2.3.4",
"@vercel/redwood": "0.8.4-canary.1", "@vercel/redwood": "0.8.4",
"@vercel/remix": "0.0.2-canary.1", "@vercel/remix": "0.0.2",
"@vercel/ruby": "1.3.7-canary.1", "@vercel/ruby": "1.3.7",
"@vercel/static-build": "0.25.3-canary.1", "@vercel/static-build": "0.26.0",
"update-notifier": "4.1.0" "update-notifier": "4.1.0"
}, },
"devDependencies": { "devDependencies": {
@@ -95,8 +95,8 @@
"@types/which": "1.3.2", "@types/which": "1.3.2",
"@types/write-json-file": "2.2.1", "@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0", "@types/yauzl-promise": "2.1.0",
"@vercel/client": "11.0.4-canary.1", "@vercel/client": "11.0.4",
"@vercel/frameworks": "0.9.2-canary.0", "@vercel/frameworks": "1.0.0",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"@zeit/fun": "0.11.2", "@zeit/fun": "0.11.2",
"@zeit/source-map-support": "0.6.2", "@zeit/source-map-support": "0.6.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, { const devServer = new DevServer(cwd, {
output, output,
devCommand, devCommand,

View File

@@ -1,7 +1,7 @@
import npa from 'npm-package-arg'; import npa from 'npm-package-arg';
import { satisfies } from 'semver'; import { satisfies } from 'semver';
import { dirname, join } from 'path'; import { dirname, join } from 'path';
import { outputJSON, readJSON } from 'fs-extra'; import { mkdirp, outputJSON, readJSON, symlink } from 'fs-extra';
import { import {
BuilderV2, BuilderV2,
BuilderV3, BuilderV3,
@@ -203,6 +203,11 @@ async function installBuilders(
cwd: buildersDir, 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, // Cross-reference any builderSpecs from the saved `package.json` file,
// in case they were installed from a URL // in case they were installed from a URL
const buildersPkg = await readJSONFile<PackageJson>(buildersPkgPath); 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, "version": 2,
"name": "now-dev-headers",
"builds": [ "builds": [
{ "src": "index.js", "use": "@vercel/node" }, { "src": "index.js", "use": "@vercel/node" },
{ "src": "foo.txt", "use": "@vercel/static" } { "src": "foo.txt", "use": "@vercel/static" }

View File

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

View File

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

View File

@@ -1,6 +1,9 @@
import os from 'os'; import os from 'os';
import url from 'url';
import fs from 'fs-extra'; import fs from 'fs-extra';
import { join } from 'path'; import { join } from 'path';
import listen from 'async-listen';
import { createServer } from 'http';
const { const {
exec, exec,
@@ -8,8 +11,374 @@ const {
fixture, fixture,
testFixture, testFixture,
testFixtureStdio, testFixtureStdio,
validateResponseHeaders,
} = require('./utils.js'); } = 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 () => { test('[vercel dev] prints `npm install` errors', async () => {
const dir = fixture('runtime-not-installed'); const dir = fixture('runtime-not-installed');
const result = await exec(dir); const result = await exec(dir);

View File

@@ -96,10 +96,16 @@ function shouldSkip(name, versions) {
return false; return false;
} }
function validateResponseHeaders(res) { function validateResponseHeaders(res, podId) {
if (res.status < 500) { 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('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>

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