Compare commits

..

11 Commits

Author SHA1 Message Date
Sean Massa
95a4dcfb33 Publish Stable
- @vercel/build-utils@6.2.2
 - vercel@28.15.4
 - @vercel/client@12.3.8
 - @vercel/fs-detectors@3.7.11
 - @vercel/gatsby-plugin-vercel-builder@1.1.4
 - @vercel/go@2.3.4
 - @vercel/hydrogen@0.0.50
 - @vercel/next@3.4.3
 - @vercel/node-bridge@3.1.11
 - @vercel/node@2.9.3
 - @vercel/python@3.1.46
 - @vercel/redwood@1.1.2
 - @vercel/remix@1.2.13
 - @vercel/ruby@1.3.62
 - @vercel/static-build@1.3.6
2023-02-13 12:47:15 -06:00
Tim Neutkens
124b747b0e Read rscContentType from routes manifest (#9408) 2023-02-13 14:04:54 +01:00
Nathan Rajlich
a735527d8f [build-utils] Filter out files outside of cwd when glob is in "follow" mode (#9412)
Enables `glob()` to operate in "follow" mode, but filters out any values where a symlink points to a target that lives outside of the `cwd.
2023-02-11 20:42:39 +00:00
Sean Massa
d628880942 [cli][node][node-bridge] improve edge/serverless function error messages (#9410)
The error messages shown during `vc dev` when an Edge or Serverless functions returns a promise that ends up being rejected could be better.

Main changes:

- removed "socket hang up" error messages because they were not helpful to the user
- changed serverless function error handling to log explicitly and exit
- changed serverless function error message to include the request path
- changed edge function error message to include request path and a faked (but useful) stack trace

## Current

### In Production

**Serverless Function:** In production, for a serverless function rejected promise, you'll see this in the logs:

```
Unhandled Promise Rejection 	{
  "errorType": "Runtime.UnhandledPromiseRejection",
  "errorMessage": "Error: intentional break!",
  "reason": {
    "errorType": "Error",
    "errorMessage": "intentional break!",
    "stack": [
      "Error: intentional break!",
      "    at handler (/var/task/api/node.js:3:9)",
      "    at Server.<anonymous> (/var/task/___vc/__helpers.js:813:19)",
      "    at Server.emit (node:events:527:28)",
      "    at parserOnIncoming (node:_http_server:956:12)",
      "    at HTTPParser.parserOnHeadersComplete (node:_http_common:128:17)"
    ]
  },
  "promise": {},
  "stack": [
    "Runtime.UnhandledPromiseRejection: Error: intentional break!",
    "    at process.<anonymous> (file:///var/runtime/index.mjs:1194:17)",
    "    at process.emit (node:events:539:35)",
    "    at process.emit (/var/task/___vc/__sourcemap_support.js:559:21)",
    "    at emit (node:internal/process/promises:140:20)",
    "    at processPromiseRejections (node:internal/process/promises:274:27)",
    "    at processTicksAndRejections (node:internal/process/task_queues:97:32)"
  ]
}
Unknown application error occurred
Runtime.Unknown
```

**Edge Function:** In production, for an edge function rejected promise, you'll see this in the logs:

```
Error: intentional break!
    at (api/edge.js:10:10)
```

In both cases, in the browser, you see the "This Serverless/Edge Function has crashed." template with no error message or stack trace.


### In `vc dev`


**Serverless Function:** In `vc dev`, for a serverless function rejected promise, you'll see this in the output:

```
Unhandled rejection: Error: intentional break!
    at handler (/Users/smassa/source/demo/edge-errors/api/node.js:3:9)
    at Server.<anonymous> (/Users/smassa/source/vercel/vercel/packages/node-bridge/helpers.js:813:19)
    at Server.emit (node:events:513:28)
    at Server.emit (node:domain:489:12)
    at parserOnIncoming (node:_http_server:998:12)
    at HTTPParser.parserOnHeadersComplete (node:_http_common:128:17)
Error: Failed to complete request to /api/node: Error: socket hang up
```

**Edge Function:** In `vc dev`, for an edge function rejected promise, you'll see this in the output:

```
Unhandled rejection: intentional break!
Error: Failed to complete request to /api/edge: Error: socket hang up
```

## After Changes

### In `vc dev`


**Serverless Function:** In `vc dev`, for a serverless function rejected promise, you'll see this in the output:

```
Rejected Promise returned from /api/node: Error: intentional break!
    at handler (/Users/smassa/source/demo/edge-errors/api/node.js:3:9)
    at Server.<anonymous> (/Users/smassa/source/vercel/vercel/packages/node-bridge/helpers.js:824:19)
    at Server.emit (node:events:513:28)
    at Server.emit (node:domain:489:12)
    at parserOnIncoming (node:_http_server:998:12)
    at HTTPParser.parserOnHeadersComplete (node:_http_common:128:17)
```

**Edge Function:** In `vc dev`, for an edge function rejected promise, you'll see this in the output:

```
Rejected Promise returned from api/edge: intentional break!
    at (api/edge.ts)
```

We can't show the real stack trace for Edge Functions because the bundling + VM execution mangles it. What's rendered here is a fake one, but it's still useful to the user.

If we currently showed the real stack trace for edge functions, it would look like:

```
Rejected Promise returned from api/edge: intentional break!
    at edge (evalmachine.<anonymous>:35:9)
    at FetchEvent.<anonymous> (evalmachine.<anonymous>:87:26)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async EdgeRuntime.dispatchFetch (evalmachine.<anonymous>:29:7)
    at async Server.handler (/Users/smassa/source/vercel/vercel/node_modules/.pnpm/edge-runtime@2.0.0/node_modules/edge-runtime/src/server/create-handler.ts:46:26)
```

## Follow Up

We'll look into improving the Edge Function error stack traces later.
2023-02-11 00:14:58 +00:00
chloetedder
084125d90f [fs-detectors] Add main to test package.json files (#9416)
Fix flakey tests
2023-02-10 22:56:01 +00:00
Steven
2b483b0fd0 [tests] Fix examples tests with shared util (#9415)
Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2023-02-10 16:32:45 -06:00
Sean Massa
fdcd86d37c [tests] add missing dep that used to be hoisted (#9413)
It looks like `find-up` was working before because of yarn hoisting, but not anymore. I don't know why this works sometimes and not others, though.

This module is imported from: https://github.com/vercel/vercel/blob/main/packages/cli/test/helpers/setup-fixture.ts#L1

I stuck with version `4.1.0` because that looks like the version being used before.

---

Trying to fix: https://github.com/vercel/vercel/actions/runs/4147054878/jobs/7174327469#step:9:2622
2023-02-10 21:33:38 +00:00
Steven
f5280cb375 [node] Fix incorrect stack trace of TS error (#9409)
### Description

TS has an option, `noEmitOnError`, that is used to either fail the build by throwing when `true` or just print the error and continuing to build when `false`.

This PR fixes the case when `noEmitOnError: false`. Previously showing an error stack trace but now it correctly shows the error message without the stack.

### Before

<img width="1186" alt="image" src="https://user-images.githubusercontent.com/229881/218186616-9e6f04ea-0256-4ed6-8705-50e8dd5090f5.png">

### After

<img width="1195" alt="image" src="https://user-images.githubusercontent.com/229881/218186818-526b2b6a-599e-43e1-aa7b-7f536887730a.png">
2023-02-10 20:29:45 +00:00
Steven
26773daf05 [tests] Add tests for examples (#9403)
These tests will run if one of the conditions is met:

- any change to `./examples`
- any changes to `./packages/frameworks`

The test ensures that examples can build and deploy without errors.

In a future PR, we could improve these tests to make requests to the
test deployment (like we do for probes).
2023-02-09 09:58:37 -05:00
Steven
8087b7804e [tests] Fix app-dir test assertion (#9404)
This was changed in `next@canary`
2023-02-09 09:56:17 -05:00
Chris Barber
c8690190f6 [cli] Update notification redesign (#9392)
This PR does a couple fantastic things:

1. Increases latest version check from once a week to once a day
2. Increases latest version notification from once a week to once every 3 days unless an even newer version is available
3. Update changelog link to all releases and make text an actual link
4. Redesign of the update notification to be more visible by wrapping in a box
5. Replace `boxen` (500KB) (used by `vc bisect`) with slimmed down built-in version (3.4KB)
6. Update notification appears at the end of the command regardless if an error occurred

Regular notification: displayed once every 3 days or sooner if a new release first day:

<img width="438" alt="image" src="https://user-images.githubusercontent.com/97262/217167938-40baa1fe-2ab7-4092-a8a0-c4a968d5fc17.png">

Same as above, but formats the notification to adapt to narrow terminals:

<img width="389" alt="image" src="https://user-images.githubusercontent.com/97262/217170374-b3f23ffc-47cc-45ca-aa0b-1dde4fb1c66c.png">

Whenever any command (aside from `help`) returns a non-zero exit code, show an additional message to encourage updating:

<img width="436" alt="image" src="https://user-images.githubusercontent.com/97262/217172208-b9e24e67-669f-4403-8bf2-29f896b27f06.png">

Linear: https://linear.app/vercel/issue/VCCLI-504/investigate-old-update-notifier-behavior
2023-02-09 05:03:52 +00:00
98 changed files with 969 additions and 284 deletions

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,5 @@
import { deployExample } from '../test-utils';
it('should deploy', async () => {
await deployExample(__filename);
});

View File

@@ -0,0 +1,27 @@
import { basename, join } from 'path';
import { lstatSync, readdirSync } from 'fs';
export async function deployExample(filename: string) {
const { testDeployment } = require('../../test/lib/deployment/test-deployment.js');
const example = basename(filename).replace(/\.test\.ts$/, '');
await testDeployment(join(filename, '..', '..', '..', example));
}
export function getExamples() {
const dirname = join(__dirname, '..');
const examples = readdirSync(dirname)
.map(example =>
({
exampleName: example,
examplePath: join(dirname, example),
testPath: join(dirname, '__tests__', 'integration', `${example}.test.ts`),
})
)
.filter(o =>
!o.exampleName.startsWith('.') &&
!o.exampleName.startsWith('_') &&
o.exampleName !== 'node_modules' &&
lstatSync(o.examplePath).isDirectory()
);
return examples;
}

View File

@@ -0,0 +1,8 @@
import { existsSync } from 'fs';
import { getExamples } from '../test-utils';
describe('should have test for each example', () => {
it.each(getExamples())('should exist $exampleName', async ({testPath}) => {
expect(existsSync(testPath)).toBeTruthy();
});
});

View File

@@ -8,6 +8,9 @@
"lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .",
"test": "echo \"No tests yet\""
},
"engines": {
"node": "16.x"
},
"browserslist": [
"defaults"
],

View File

@@ -10,6 +10,9 @@
"test:functional": "dojo build --mode functional && dojo test --functional --config local",
"test:all": "dojo build --mode unit && dojo build --mode functional && dojo test --all --config local"
},
"engines": {
"node": "16.x"
},
"dependencies": {
"@dojo/framework": "^6.0.0",
"@dojo/themes": "^6.0.0",

View File

@@ -18,6 +18,9 @@
"url": "https://github.com/ionic-team/ionic-conference-app.git"
},
"private": true,
"engines": {
"node": "16.x"
},
"dependencies": {
"@angular/common": "^8.2.14",
"@angular/core": "^8.2.14",

View File

@@ -2,6 +2,9 @@
"name": "ionic-react",
"version": "0.0.1",
"private": true,
"engines": {
"node": "16.x"
},
"dependencies": {
"@ionic/react": "^4.11.0",
"@ionic/react-router": "^4.11.0",

7
examples/jest.config.js vendored Normal file
View File

@@ -0,0 +1,7 @@
/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['<rootDir>/__tests__/**/*.test.ts'],
testTimeout: 5 * 60 * 1000,
};

14
examples/package.json vendored Normal file
View File

@@ -0,0 +1,14 @@
{
"private": true,
"name": "examples",
"description": "Each subdirectory is an example boilerplate for a framework. This package.json only exists for testing purposes.",
"scripts": {
"test-unit": "pnpm test __tests__/unit/",
"test-integration-once": "pnpm test __tests__/integration/",
"test": "jest --env node --verbose --runInBand --bail"
},
"devDependencies": {
"@types/jest": "27.4.1",
"@vercel/frameworks": "1.3.0"
}
}

View File

@@ -7,6 +7,9 @@
"lint": "eslint src",
"test": "jest"
},
"engines": {
"node": "16.x"
},
"eslintConfig": {
"extends": "preact",
"ignorePatterns": [
@@ -35,4 +38,4 @@
"<rootDir>/tests/__mocks__/setupTests.js"
]
}
}
}

View File

@@ -4,6 +4,9 @@
"dev": "saber",
"build": "saber build"
},
"engines": {
"node": "16.x"
},
"devDependencies": {
"saber": "latest",
"saber-theme-minima": "latest",

View File

@@ -1,7 +1,6 @@
{
"name": "TODO",
"description": "TODO",
"version": "0.0.1",
"name": "sapper",
"private": true,
"scripts": {
"start": "sapper dev",
"dev": "sapper dev --port $PORT",
@@ -10,6 +9,9 @@
"cy:open": "cypress open",
"test": "run-p --race dev cy:run"
},
"engines": {
"node": "16.x"
},
"dependencies": {
"compression": "^1.7.1",
"polka": "next",

View File

@@ -10,6 +10,9 @@
"scully": "scully"
},
"private": true,
"engines": {
"node": "16.x"
},
"dependencies": {
"@angular/animations": "~9.0.0-rc.7",
"@angular/common": "~9.0.0-rc.7",

View File

@@ -11,8 +11,8 @@
"format": "prettier --write ."
},
"devDependencies": {
"@sveltejs/adapter-auto": "next",
"@sveltejs/kit": "1.0.0-next.589",
"@sveltejs/adapter-auto": "1.0.0-next.65",
"@sveltejs/kit": "1.0.0-next.428",
"@types/cookie": "^0.5.1",
"prettier": "^2.6.2",
"prettier-plugin-svelte": "^2.7.0",

View File

@@ -2,8 +2,8 @@ lockfileVersion: 5.4
specifiers:
'@fontsource/fira-mono': ^4.5.0
'@sveltejs/adapter-auto': next
'@sveltejs/kit': next
'@sveltejs/adapter-auto': 1.0.0-next.65
'@sveltejs/kit': 1.0.0-next.428
'@types/cookie': ^0.5.1
cookie: ^0.4.1
prettier: ^2.6.2
@@ -32,8 +32,8 @@ devDependencies:
packages:
/@cloudflare/workers-types/3.14.1:
resolution: {integrity: sha512-B1/plF62pt+H2IJHvApK8fdOJAVsvojvacuac8x8s+JIyqbropMyqNqHTKLm3YD8ZFLGwYeFTudU+PQ7vGvBdA==}
/@cloudflare/workers-types/3.19.0:
resolution: {integrity: sha512-0FRcsz7Ea3jT+gc5gKPIYciykm1bbAaTpygdzpCwGt0RL+V83zWnYN30NWDW4rIHj/FHtz+MIuBKS61C8l7AzQ==}
dev: true
/@esbuild/linux-loong64/0.14.54:
@@ -69,19 +69,19 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.14
dev: true
/@mapbox/node-pre-gyp/1.0.9:
resolution: {integrity: sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==}
/@mapbox/node-pre-gyp/1.0.10:
resolution: {integrity: sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==}
hasBin: true
dependencies:
detect-libc: 2.0.1
https-proxy-agent: 5.0.1
make-dir: 3.1.0
node-fetch: 2.6.7
node-fetch: 2.6.9
nopt: 5.0.0
npmlog: 5.0.1
rimraf: 3.0.2
semver: 7.3.7
tar: 6.1.11
semver: 7.3.8
tar: 6.1.13
transitivePeerDependencies:
- encoding
- supports-color
@@ -134,7 +134,7 @@ packages:
/@sveltejs/adapter-cloudflare/1.0.0-next.31:
resolution: {integrity: sha512-HhEFZP72GJ8AZGgFECKIiayDcLaAWi65pI0AnBfiNhCifYSlH/mPNWNVD4AWRDnXnH6XU+FLwhGDnIDwytTyYg==}
dependencies:
'@cloudflare/workers-types': 3.14.1
'@cloudflare/workers-types': 3.19.0
esbuild: 0.14.54
worktop: 0.8.0-next.14
dev: true
@@ -231,15 +231,15 @@ packages:
resolution: {integrity: sha512-hFCAETfI5cG8l5iAiLhMC2bReC5K7SIybzrxGorv+eGspIbIFsVw7Vg85GovXm/LxA08pIDrAlrhR6GN36XB/Q==}
hasBin: true
dependencies:
'@mapbox/node-pre-gyp': 1.0.9
acorn: 8.8.0
'@mapbox/node-pre-gyp': 1.0.10
acorn: 8.8.2
async-sema: 3.1.1
bindings: 1.5.0
estree-walker: 2.0.2
glob: 7.2.3
graceful-fs: 4.2.10
micromatch: 4.0.5
node-gyp-build: 4.5.0
node-gyp-build: 4.6.0
resolve-from: 5.0.0
rollup-pluginutils: 2.8.2
transitivePeerDependencies:
@@ -251,8 +251,8 @@ packages:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
dev: true
/acorn/8.8.0:
resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==}
/acorn/8.8.2:
resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==}
engines: {node: '>=0.4.0'}
hasBin: true
dev: true
@@ -688,7 +688,7 @@ packages:
resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
engines: {node: '>= 8'}
dependencies:
minipass: 3.3.4
minipass: 3.3.6
dev: true
/fs.realpath/1.0.0:
@@ -893,18 +893,23 @@ packages:
resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==}
dev: true
/minipass/3.3.4:
resolution: {integrity: sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==}
/minipass/3.3.6:
resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
engines: {node: '>=8'}
dependencies:
yallist: 4.0.0
dev: true
/minipass/4.0.3:
resolution: {integrity: sha512-OW2r4sQ0sI+z5ckEt5c1Tri4xTgZwYDxpE54eqWlQloQRoWtXjqt9udJ5Z4dSv7wK+nfFI7FRXyCpBSft+gpFw==}
engines: {node: '>=8'}
dev: true
/minizlib/2.1.2:
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
engines: {node: '>= 8'}
dependencies:
minipass: 3.3.4
minipass: 3.3.6
yallist: 4.0.0
dev: true
@@ -946,8 +951,8 @@ packages:
engines: {node: '>=10.5.0'}
dev: true
/node-fetch/2.6.7:
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
/node-fetch/2.6.9:
resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
@@ -967,8 +972,8 @@ packages:
formdata-polyfill: 4.0.10
dev: true
/node-gyp-build/4.5.0:
resolution: {integrity: sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==}
/node-gyp-build/4.6.0:
resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
hasBin: true
dev: true
@@ -1163,8 +1168,8 @@ packages:
hasBin: true
dev: true
/semver/7.3.7:
resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==}
/semver/7.3.8:
resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
engines: {node: '>=10'}
hasBin: true
dependencies:
@@ -1338,13 +1343,13 @@ packages:
engines: {node: '>= 8'}
dev: true
/tar/6.1.11:
resolution: {integrity: sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==}
engines: {node: '>= 10'}
/tar/6.1.13:
resolution: {integrity: sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==}
engines: {node: '>=10'}
dependencies:
chownr: 2.0.0
fs-minipass: 2.1.0
minipass: 3.3.4
minipass: 4.0.3
minizlib: 2.1.2
mkdirp: 1.0.4
yallist: 4.0.0

7
examples/tsconfig.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"compilerOptions": {
"esModuleInterop": true,
"target": "esnext",
"module": "commonjs"
}
}

View File

@@ -5,6 +5,9 @@
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"engines": {
"node": "16.x"
},
"dependencies": {
"core-js": "^3.6.5",
"vue": "^3.0.0"

View File

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

View File

@@ -2,7 +2,7 @@ import path from 'path';
import assert from 'assert';
import vanillaGlob_ from 'glob';
import { promisify } from 'util';
import { lstat, Stats } from 'fs-extra';
import { lstat, readlink, Stats } from 'fs-extra';
import { normalizePath } from './normalize-path';
import FileFsRef from '../file-fs-ref';
@@ -45,16 +45,33 @@ export default async function glob(
const dirsWithEntries = new Set<string>();
for (const relativePath of files) {
const fsPath = normalizePath(path.join(options.cwd, relativePath));
const absPath = path.join(options.cwd, relativePath);
const fsPath = normalizePath(absPath);
let stat = statCache[fsPath];
assert(
stat,
`statCache does not contain value for ${relativePath} (resolved to ${fsPath})`
);
const isSymlink = symlinks[fsPath];
// When `follow` mode is enabled, ensure that the entry is not a symlink
// that points to outside of `cwd`
if (
options.follow &&
(isSymlink || (await lstat(fsPath)).isSymbolicLink())
) {
const target = await readlink(absPath);
const absTarget = path.resolve(path.dirname(absPath), target);
if (path.relative(options.cwd, absTarget).startsWith(`..${path.sep}`)) {
continue;
}
}
if (isSymlink || stat.isFile() || stat.isDirectory()) {
if (isSymlink) {
stat = await lstat(fsPath);
stat = await lstat(absPath);
}
// Some bookkeeping to track which directories already have entries within

View File

@@ -1,7 +1,7 @@
import fs from 'fs-extra';
import { join } from 'path';
import { tmpdir } from 'os';
import { glob, isDirectory } from '../src';
import { glob, isDirectory, isSymbolicLink } from '../src';
describe('glob()', () => {
it('should not return entries for empty directories by default', async () => {
@@ -62,4 +62,43 @@ describe('glob()', () => {
await fs.remove(dir);
}
});
it('should allow for following symlinks', async () => {
const rootDir = await fs.mkdtemp(join(tmpdir(), 'build-utils-test'));
const dir = await fs.mkdtemp(join(rootDir, 'build-utils-test'));
try {
await Promise.all([
fs.writeFile(join(rootDir, 'root.txt'), 'file outside of "dir"'),
fs.writeFile(join(dir, 'root.txt'), 'file at the root'),
fs.mkdirp(join(dir, 'empty-dir')),
fs
.mkdirp(join(dir, 'dir-with-file'))
.then(() =>
fs.writeFile(join(dir, 'dir-with-file/data.json'), '{"a":"b"}')
),
fs.mkdirp(join(dir, 'another/subdir')),
fs.symlink('root.txt', join(dir, 'root-link')),
fs.symlink(join(dir, 'root.txt'), join(dir, 'abs-root-link')),
fs.symlink('dir-with-file', join(dir, 'dir-link')),
fs.symlink('empty-dir', join(dir, 'empty-dir-link')),
fs.symlink('../root.txt', join(dir, 'outside-cwd-link')),
fs.symlink(join(dir, '../root.txt'), join(dir, 'abs-outside-cwd-link')),
]);
const files = await glob('**', { cwd: dir, follow: true });
const fileNames = Object.keys(files).sort();
expect(fileNames).toHaveLength(5);
expect(fileNames).toEqual([
'abs-root-link',
'dir-link/data.json',
'dir-with-file/data.json',
'root-link',
'root.txt',
]);
for (const file of Object.values(files)) {
expect(isSymbolicLink(file.mode)).toEqual(false);
}
} finally {
await fs.remove(dir);
}
});
});

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "28.15.3",
"version": "28.15.4",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -41,16 +41,16 @@
"node": ">= 14"
},
"dependencies": {
"@vercel/build-utils": "6.2.1",
"@vercel/go": "2.3.3",
"@vercel/hydrogen": "0.0.49",
"@vercel/next": "3.4.2",
"@vercel/node": "2.9.2",
"@vercel/python": "3.1.45",
"@vercel/redwood": "1.1.1",
"@vercel/remix": "1.2.12",
"@vercel/ruby": "1.3.61",
"@vercel/static-build": "1.3.5"
"@vercel/build-utils": "6.2.2",
"@vercel/go": "2.3.4",
"@vercel/hydrogen": "0.0.50",
"@vercel/next": "3.4.3",
"@vercel/node": "2.9.3",
"@vercel/python": "3.1.46",
"@vercel/redwood": "1.1.2",
"@vercel/remix": "1.2.13",
"@vercel/ruby": "1.3.62",
"@vercel/static-build": "1.3.6"
},
"devDependencies": {
"@alex_neo/jest-expect-message": "1.0.5",
@@ -93,10 +93,10 @@
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel/client": "12.3.7",
"@vercel/client": "12.3.8",
"@vercel/error-utils": "1.0.8",
"@vercel/frameworks": "1.3.0",
"@vercel/fs-detectors": "3.7.10",
"@vercel/fs-detectors": "3.7.11",
"@vercel/fun": "1.0.4",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.1.8",
@@ -110,7 +110,6 @@
"async-retry": "1.1.3",
"async-sema": "2.1.4",
"ava": "2.2.0",
"boxen": "4.2.0",
"bytes": "3.0.0",
"chalk": "4.1.0",
"chance": "1.1.7",
@@ -128,6 +127,7 @@
"execa": "3.2.0",
"express": "4.17.1",
"fast-deep-equal": "3.1.3",
"find-up": "4.1.0",
"fs-extra": "10.0.0",
"get-port": "5.1.1",
"git-last-commit": "1.0.1",

View File

@@ -1,11 +1,11 @@
import open from 'open';
import boxen from 'boxen';
import execa from 'execa';
import plural from 'pluralize';
import { resolve } from 'path';
import chalk, { Chalk } from 'chalk';
import { URLSearchParams, parse } from 'url';
import box from '../../util/output/box';
import sleep from '../../util/sleep';
import formatDate from '../../util/format-date';
import link from '../../util/output/link';
@@ -363,7 +363,7 @@ export default async function main(client: Client): Promise<number> {
result.push(`${chalk.bold('Inspect:')} ${link(lastBad.inspectorUrl)}`);
output.print(boxen(result.join('\n'), { padding: 1 }));
output.print(box(result.join('\n')));
output.print('\n');
return 0;

View File

@@ -52,6 +52,7 @@ import { getCommandName, getTitleName } from './util/pkg-name';
import doLoginPrompt from './util/login/prompt';
import { AuthConfig, GlobalConfig } from './types';
import { VercelConfig } from '@vercel/client';
import box from './util/output/box';
const isCanary = pkg.version.includes('canary');
@@ -73,11 +74,12 @@ Sentry.init({
});
let client: Client;
let output: Output;
let { isTTY } = process.stdout;
let debug: (s: string) => void = () => {};
let apiUrl = 'https://api.vercel.com';
const main = async () => {
let { isTTY } = process.stdout;
if (process.env.FORCE_TTY === '1') {
isTTY = true;
process.stdout.isTTY = true;
@@ -105,7 +107,7 @@ const main = async () => {
const isDebugging = argv['--debug'];
const isNoColor = argv['--no-color'];
const output = new Output(process.stderr, {
output = new Output(process.stderr, {
debug: isDebugging,
noColor: isNoColor,
});
@@ -146,29 +148,6 @@ const main = async () => {
process.chdir(cwd);
}
// Print update information, if available
if (isTTY && !process.env.NO_UPDATE_NOTIFIER) {
// Check if an update is available. If so, `latest` will contain a string
// of the latest version, otherwise `undefined`.
const latest = getLatestVersion({
distTag: isCanary ? 'canary' : 'latest',
output,
pkg,
});
if (latest) {
output.log(
`${chalk.black.bgCyan('UPDATE AVAILABLE')} ` +
`Run ${cmd(
await getUpdateCommand()
)} to install ${getTitleName()} CLI ${latest}`
);
output.log(
`Changelog: https://github.com/vercel/vercel/releases/tag/vercel@${latest}\n`
);
}
}
// The second argument to the command can be:
//
// * a path to deploy (as in: `vercel path/`)
@@ -716,7 +695,39 @@ process.on('unhandledRejection', handleRejection);
process.on('uncaughtException', handleUnexpected);
main()
.then(exitCode => {
.then(async exitCode => {
// Print update information, if available
if (isTTY && !process.env.NO_UPDATE_NOTIFIER) {
// Check if an update is available. If so, `latest` will contain a string
// of the latest version, otherwise `undefined`.
const latest = getLatestVersion({
distTag: isCanary ? 'canary' : 'latest',
output,
pkg,
});
if (latest) {
const changelog = 'https://github.com/vercel/vercel/releases';
const errorMsg =
exitCode && exitCode !== 2
? chalk.magenta(
`\n\nThe latest update ${chalk.italic(
'may'
)} fix any errors that occurred.`
)
: '';
output.print(
box(
`Update available! ${chalk.gray(`v${pkg.version}`)}${chalk.green(
`v${latest}`
)}
Changelog: ${output.link(changelog, changelog, { fallback: false })}
Run ${chalk.cyan(cmd(await getUpdateCommand()))} to update.${errorMsg}`
)
);
output.print('\n\n');
}
}
process.exitCode = exitCode;
})
.catch(handleUnexpected);

View File

@@ -2307,7 +2307,10 @@ function proxyPass(
res,
{ target: dest, ignorePath },
(error: NodeJS.ErrnoException) => {
devServer.output.error(
// only debug output this error because it's always something generic like
// "Error: socket hang up"
// and the original error should have already been logged
devServer.output.debug(
`Failed to complete request to ${req.url}: ${error}`
);
if (!res.headersSent) {

View File

@@ -116,45 +116,10 @@ process.once('message', async msg => {
output.debug(`Initializing lock file with pid ${process.pid}`);
await writeFile(lockFile, String(process.pid), 'utf-8');
// fetch the latest version from npm
const agent = new https.Agent({
keepAlive: true,
maxSockets: 15, // See: `npm config get maxsockets`
});
const headers = {
accept:
'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*',
};
const url = `https://registry.npmjs.org/-/package/${name}/dist-tags`;
output.debug(`Fetching ${url}`);
const tags = await new Promise((resolve, reject) => {
const req = https.get(
url,
{
agent,
headers,
},
res => {
let buf = '';
res.on('data', chunk => {
buf += chunk;
});
res.on('end', () => {
try {
resolve(JSON.parse(buf));
} catch (err) {
reject(err);
}
});
}
);
req.on('error', reject);
req.end();
});
const tags = await fetchDistTags(name);
const version = tags[distTag];
const expireAt = Date.now() + updateCheckInterval;
const notifyAt = await getNotifyAt(cacheFile, version);
if (version) {
output.debug(`Found dist tag "${distTag}" with version "${version}"`);
@@ -167,8 +132,8 @@ process.once('message', async msg => {
await writeFile(
cacheFile,
JSON.stringify({
expireAt: Date.now() + updateCheckInterval,
notified: false,
expireAt,
notifyAt,
version,
})
);
@@ -223,3 +188,74 @@ async function isRunning(lockFile) {
return false;
}
}
/**
* Attempts to load and return the previous `notifyAt` value.
*
* If the latest version is newer than the previous latest version, then
* return `undefined` to invalidate `notifyAt` which forces the notification
* to be displayed, otherwise keep the existing `notifyAt`.
*
* @param {string} cacheFile The path to the cache file
* @param {string} version The latest version
* @returns {number | undefined} The previous notifyAt
*/
async function getNotifyAt(cacheFile, version) {
try {
const old = JSON.parse(await readFile(cacheFile, 'utf-8'));
if (old?.version && old.version === version) {
return old.notifyAt;
}
} catch (err) {
// cache does not exist or malformed
if (err.code !== 'ENOENT') {
output.debug(`Error reading latest package cache file: ${err}`);
}
}
}
/**
* Fetches the dist tags from npm for a given package.
*
* @param {string} name The package name
* @returns A map of dist tags to versions
*/
async function fetchDistTags(name) {
// fetch the latest version from npm
const agent = new https.Agent({
keepAlive: true,
maxSockets: 15, // See: `npm config get maxsockets`
});
const headers = {
accept:
'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*',
};
const url = `https://registry.npmjs.org/-/package/${name}/dist-tags`;
output.debug(`Fetching ${url}`);
return new Promise((resolve, reject) => {
const req = https.get(
url,
{
agent,
headers,
},
res => {
let buf = '';
res.on('data', chunk => {
buf += chunk;
});
res.on('end', () => {
try {
resolve(JSON.parse(buf));
} catch (err) {
reject(err);
}
});
}
);
req.on('error', reject);
req.end();
});
}

View File

@@ -9,22 +9,23 @@ import { spawn } from 'child_process';
interface GetLatestVersionOptions {
cacheDir?: string;
distTag?: string;
notifyInterval?: number;
output?: Output;
pkg: PackageJson;
updateCheckInterval?: number;
}
interface PackageInfoCache {
version: string;
expireAt: number;
notified: boolean;
notifyAt: number;
version: string;
}
interface GetLatestWorkerPayload {
cacheFile?: string;
distTag?: string;
updateCheckInterval?: number;
name?: string;
updateCheckInterval?: number;
}
/**
@@ -38,9 +39,10 @@ interface GetLatestWorkerPayload {
export default function getLatestVersion({
cacheDir = XDGAppPaths('com.vercel.cli').cache(),
distTag = 'latest',
notifyInterval = 1000 * 60 * 60 * 24 * 3, // 3 days
output,
pkg,
updateCheckInterval = 1000 * 60 * 60 * 24 * 7, // 1 week
updateCheckInterval = 1000 * 60 * 60 * 24, // 1 day
}: GetLatestVersionOptions): string | undefined {
if (
!pkg ||
@@ -67,27 +69,31 @@ export default function getLatestVersion({
}
}
if (!cache || cache.expireAt < Date.now()) {
if (!cache || !cache.expireAt || cache.expireAt < Date.now()) {
spawnWorker(
{
cacheFile,
distTag,
updateCheckInterval,
name: pkg.name,
updateCheckInterval,
},
output
);
}
if (
cache &&
!cache.notified &&
pkg.version &&
semver.lt(pkg.version, cache.version)
) {
cache.notified = true;
outputJSONSync(cacheFile, cache);
return cache.version;
if (cache) {
const shouldNotify = !cache.notifyAt || cache.notifyAt < Date.now();
let updateAvailable = false;
if (cache.version && pkg.version) {
updateAvailable = semver.lt(pkg.version, cache.version);
}
if (shouldNotify && updateAvailable) {
cache.notifyAt = Date.now() + notifyInterval;
outputJSONSync(cacheFile, cache);
return cache.version;
}
}
}

View File

@@ -0,0 +1,112 @@
import chalk from 'chalk';
import stripAnsi from 'strip-ansi';
const border = ['─', '╭', '╮', '│', '│', '╰', '╯'];
const nothing = ['─', '', '', '', '', '', ''];
export type BoxOptions = {
borderColor?:
| 'black'
| 'red'
| 'green'
| 'yellow'
| 'blue'
| 'magenta'
| 'cyan'
| 'white';
padding?: number;
textAlignment?: 'left' | 'center' | 'right';
terminalColumns?: number;
};
/**
* Renders text centered inside a yellow box. If terminal is too narrow to fit
* the text without wrapping, the box will only consist of a top and bottom
* horizontal rule with the text left justified.
*
* @param message The multiline message to display
* @param options Various formatting options
* @returns The rendered string
*
* @example Simple box
*
* # Usage
* ```
* console.log(box('Hello world!\nThe quick brown fox jumps over the lazy dog'));
* ```
*
* # Result
* ```
* ╭─────────────────────────────────────────────────╮
* │ │
* │ Hello world! │
* │ The quick brown fox jumps over the lazy dog │
* │ │
* ╰─────────────────────────────────────────────────╯
* ```
*/
export default function box(
message: string,
{
borderColor,
padding = 1,
textAlignment = 'center',
terminalColumns: cols = process.stdout.columns ||
(process.env.COLUMNS && parseInt(process.env.COLUMNS, 10)) ||
80,
}: BoxOptions = {}
): string {
const lines: [string, number][] = message
.split(/\r?\n/)
.map(line => [line, stripAnsi(line).length]);
const maxLine = lines.reduce((p, [, len]) => Math.max(p, len), 0);
const borderColorFn = (borderColor && chalk[borderColor]) || chalk.yellow;
const clampedSidePadding = Math.max(1, padding * 3);
const narrowMode = maxLine + 2 + clampedSidePadding * 2 > cols;
const sidePadding = narrowMode ? 0 : clampedSidePadding;
const innerWidth = Math.min(maxLine + sidePadding * 2, cols);
const [hr, topLeft, topRight, left, right, bottomLeft, bottomRight] =
narrowMode ? nothing : border;
const spacerRow = narrowMode
? '\n'.repeat(padding)
: `${borderColorFn(`${left}${' '.repeat(innerWidth)}${right}`)}\n`.repeat(
padding
);
const renderLine = ([line, len]: [string, number]) => {
let leftPadding = 0;
let rightPadding = 0;
if (!narrowMode) {
leftPadding = sidePadding;
rightPadding = sidePadding;
if (textAlignment === 'center') {
leftPadding += Math.floor((maxLine - len) / 2);
rightPadding += maxLine - len - leftPadding + sidePadding;
} else if (textAlignment === 'right') {
leftPadding += maxLine - len;
} else if (textAlignment === 'left') {
rightPadding += maxLine - len;
}
}
return (
borderColorFn(left) +
' '.repeat(leftPadding) +
line +
' '.repeat(rightPadding) +
borderColorFn(right)
);
};
return (
borderColorFn(`${topLeft}${hr.repeat(innerWidth)}${topRight}`) +
'\n' +
spacerRow +
lines.map(renderLine).join('\n') +
'\n' +
spacerRow +
borderColorFn(`${bottomLeft}${hr.repeat(innerWidth)}${bottomRight}`)
);
}

View File

@@ -100,15 +100,12 @@ test('[vercel dev] throws an error when an edge function has no response', async
let res = await fetch(`http://localhost:${port}/api/edge-no-response`);
validateResponseHeaders(res);
const { stdout, stderr } = await dev.kill();
const { stdout } = await dev.kill();
expect(await res.status).toBe(500);
expect(await res.text()).toMatch('FUNCTION_INVOCATION_FAILED');
expect(stdout).toMatch(
/Unhandled rejection: Edge Function "api\/edge-no-response.js" did not return a response./g
);
expect(stderr).toMatch(
/Failed to complete request to \/api\/edge-no-response: Error: socket hang up/g
/Error from API Route api\/edge-no-response: Edge Function "api\/edge-no-response.js" did not return a response./g
);
} finally {
await dev.kill();
@@ -158,14 +155,13 @@ test('[vercel dev] should handle runtime errors thrown in edge functions', async
});
validateResponseHeaders(res);
const { stdout, stderr } = await dev.kill();
const { stdout } = await dev.kill();
expect(await res.text()).toMatch(
/<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g
);
expect(stdout).toMatch(/Unhandled rejection: intentional runtime error/g);
expect(stderr).toMatch(
/Failed to complete request to \/api\/edge-error-runtime: Error: socket hang up/g
expect(stdout).toMatch(
/Error from API Route api\/edge-error-runtime: intentional runtime error/g
);
} finally {
await dev.kill();
@@ -196,9 +192,6 @@ test('[vercel dev] should handle config errors thrown in edge functions', async
expect(stderr).toMatch(
/Invalid function runtime "invalid-runtime-value" for "api\/edge-error-config.js". Valid runtimes are: \["edge","experimental-edge"\]/g
);
expect(stderr).toMatch(
/Failed to complete request to \/api\/edge-error-config: Error: socket hang up/g
);
} finally {
await dev.kill();
}
@@ -227,9 +220,6 @@ test('[vercel dev] should handle startup errors thrown in edge functions', async
);
expect(stderr).toMatch(/Failed to instantiate edge runtime./g);
expect(stderr).toMatch(/intentional startup error/g);
expect(stderr).toMatch(
/Failed to complete request to \/api\/edge-error-startup: Error: socket hang up/g
);
} finally {
await dev.kill();
}
@@ -258,9 +248,6 @@ test('[vercel dev] should handle syntax errors thrown in edge functions', async
);
expect(stderr).toMatch(/Failed to compile user code for edge runtime./g);
expect(stderr).toMatch(/Unexpected end of file/g);
expect(stderr).toMatch(
/Failed to complete request to \/api\/edge-error-syntax: Error: socket hang up/g
);
} finally {
await dev.kill();
}
@@ -293,9 +280,6 @@ test('[vercel dev] should handle import errors thrown in edge functions', async
expect(stderr).toMatch(
/Could not resolve "unknown-module-893427589372458934795843"/g
);
expect(stderr).toMatch(
/Failed to complete request to \/api\/edge-error-unknown-import: Error: socket hang up/g
);
} finally {
await dev.kill();
}
@@ -320,7 +304,7 @@ test('[vercel dev] should handle missing handler errors thrown in edge functions
);
validateResponseHeaders(res);
const { stdout, stderr } = await dev.kill();
const { stdout } = await dev.kill();
expect(await res.text()).toMatch(
/<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g
@@ -328,9 +312,6 @@ test('[vercel dev] should handle missing handler errors thrown in edge functions
expect(stdout).toMatch(
/No default export was found. Add a default export to handle requests./g
);
expect(stderr).toMatch(
/Failed to complete request to \/api\/edge-error-no-handler: Error: socket hang up/g
);
} finally {
await dev.kill();
}

View File

@@ -40,7 +40,7 @@ describe('get latest version', () => {
expect(cache.expireAt).toBeGreaterThan(Date.now());
expect(typeof cache.version).toEqual('string');
expect(cache.version).toEqual(expect.stringMatching(versionRE));
expect(cache.notified).toEqual(false);
expect(cache.notifyAt).toEqual(undefined);
// 2. call again and this time it'll return the version from the cache
latest = getLatestVersion({
@@ -52,7 +52,7 @@ describe('get latest version', () => {
cache = await fs.readJSON(cacheFile);
expect(cache.version).toEqual(expect.stringMatching(versionRE));
expect(cache.notified).toEqual(true);
expect(cache.notifyAt).not.toEqual(undefined);
// 3. notification already done, should skip
latest = getLatestVersion({
@@ -130,6 +130,45 @@ describe('get latest version', () => {
);
expect(() => getLatestVersion({ pkg: { name: '' } })).toThrow(TypeError);
});
it('should reset notify if newer version is available', async () => {
// 1. seed the cache file with both a expireAt and notifyAt in the future
// with an out-of-date latest version
await fs.mkdirs(join(cacheDir, 'package-updates'));
await fs.writeJSON(cacheFile, {
expireAt: Date.now(),
notifyAt: Date.now() - 60000,
version: '28.0.0',
});
// 2. get the latest version
let latest = getLatestVersion({
cacheDir,
pkg,
});
expect(latest).toEqual('28.0.0');
// we need to wait up to 20 seconds for the cacheFile to be updated
for (let i = 0; i < 80; i++) {
await sleep(250);
try {
const cache = await fs.readJSON(cacheFile);
if (cache.version !== '28.0.0') {
break;
}
} catch {
// cacheFile has not been updated yet
}
if (i + 1 === 80) {
throw new Error(`Timed out waiting for worker to fetch latest version`);
}
}
let cache = await fs.readJSON(cacheFile);
expect(cache.version).toEqual(expect.stringMatching(versionRE));
expect(cache.version).not.toEqual('28.0.0');
expect(cache.notifyAt).toEqual(undefined);
});
});
async function waitForCacheFile() {

View File

@@ -0,0 +1,124 @@
import box from '../../../../src/util/output/box';
import chalk from 'chalk';
import stripAnsi from 'strip-ansi';
describe('box()', () => {
it('should show single line box with default padding', () => {
const result = box('Hello world!');
expect(stripAnsi(result)).toEqual(
`
╭──────────────────╮
│ │
│ Hello world! │
│ │
╰──────────────────╯
`.trim()
);
});
it('should show single line box without padding', () => {
const result = box('Hello world!', { padding: 0 });
expect(stripAnsi(result)).toEqual(
`
╭──────────────╮
│ Hello world! │
╰──────────────╯
`.trim()
);
});
it('should show single line box with padding 2', () => {
const result = box('Hello world!', { padding: 2 });
expect(stripAnsi(result)).toEqual(
`
╭────────────────────────╮
│ │
│ │
│ Hello world! │
│ │
│ │
╰────────────────────────╯
`.trim()
);
});
it('should show multiple lines with default padding', () => {
const result = box(
'Hello world!\nThis is a really, really long line of text\n\nWow!'
);
expect(stripAnsi(result)).toEqual(
`
╭────────────────────────────────────────────────╮
│ │
│ Hello world! │
│ This is a really, really long line of text │
│ │
│ Wow! │
│ │
╰────────────────────────────────────────────────╯
`.trim()
);
});
it('should ignore ansi color escape sequences', () => {
const result = box(chalk.red('This text is red'));
expect(stripAnsi(result)).toEqual(
`
╭──────────────────────╮
│ │
│ This text is red │
│ │
╰──────────────────────╯
`.trim()
);
});
it('should left align contents', () => {
const result = box(
'This is left aligned\nThis is a really, really long line of text',
{ textAlignment: 'left' }
);
expect(stripAnsi(result)).toEqual(
`
╭────────────────────────────────────────────────╮
│ │
│ This is left aligned │
│ This is a really, really long line of text │
│ │
╰────────────────────────────────────────────────╯
`.trim()
);
});
it('should right align contents', () => {
const result = box(
'This is right aligned\nThis is a really, really long line of text',
{ textAlignment: 'right' }
);
expect(stripAnsi(result)).toEqual(
`
╭────────────────────────────────────────────────╮
│ │
│ This is right aligned │
│ This is a really, really long line of text │
│ │
╰────────────────────────────────────────────────╯
`.trim()
);
});
it('should slim if terminal width too small', () => {
const result = box('This is a really, really long line of text', {
terminalColumns: 30,
});
expect(stripAnsi(result)).toEqual(
`
──────────────────────────────
This is a really, really long line of text
──────────────────────────────
`.trim()
);
});
});

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "12.3.7",
"version": "12.3.8",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -43,7 +43,7 @@
]
},
"dependencies": {
"@vercel/build-utils": "6.2.1",
"@vercel/build-utils": "6.2.2",
"@vercel/routing-utils": "2.1.8",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/fs-detectors",
"version": "3.7.10",
"version": "3.7.11",
"description": "Vercel filesystem detectors",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -35,7 +35,7 @@
"@types/minimatch": "3.0.5",
"@types/node": "14.18.33",
"@types/semver": "7.3.10",
"@vercel/build-utils": "6.2.1",
"@vercel/build-utils": "6.2.2",
"typescript": "4.3.4"
}
}

View File

@@ -1,6 +1,7 @@
{
"name": "app-11",
"version": "0.0.1",
"main": "index.js",
"nx": {
"targets": {
"build": {

View File

@@ -1,4 +1,5 @@
{
"name": "app-8",
"version": "0.0.1"
"version": "0.0.1",
"main": "index.js"
}

View File

@@ -1,4 +1,5 @@
{
"name": "app-12",
"version": "0.0.1"
"version": "0.0.1",
"main": "index.js"
}

View File

@@ -1,4 +1,5 @@
{
"name": "app-13",
"version": "0.0.1"
"version": "0.0.1",
"main": "index.js"
}

View File

@@ -1,4 +1,5 @@
{
"name": "app-14",
"version": "0.0.1"
"version": "0.0.1",
"main": "index.js"
}

View File

@@ -1,35 +1,19 @@
import frameworkList from '@vercel/frameworks';
import { detectFramework } from '../src';
import { FixtureFilesystem } from './utils/fixture-filesystem';
import { readdirSync, lstatSync } from 'fs';
import { join } from 'path';
function getExamples() {
const root = join(__dirname, '..', '..', '..');
const examplesPath = join(root, 'examples');
const examples = readdirSync(examplesPath);
const exampleDirs = examples.filter(example => {
const examplePath = join(examplesPath, example);
const stat = lstatSync(examplePath);
return stat.isDirectory();
});
return exampleDirs.map(exampleDirName => {
return [exampleDirName, join(examplesPath, exampleDirName)];
});
}
import { getExamples } from '../../../examples/__tests__/test-utils';
describe('examples should be detected', () => {
const examples = getExamples();
it.each(getExamples())(
'should detect $exampleName',
async ({ exampleName, examplePath }) => {
const fs = new FixtureFilesystem(examplePath);
const framework = await detectFramework({ fs, frameworkList });
if (!framework) {
throw new Error(`Framework not detected for example "${exampleName}".`);
}
it.each(examples)('%s', async (example, examplePath) => {
const fs = new FixtureFilesystem(examplePath);
const framework = await detectFramework({ fs, frameworkList });
if (!framework) {
throw new Error(`Framework not detected for example "${example}".`);
expect(framework).toBe(exampleName);
}
expect(framework).toBe(example);
});
);
});

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/gatsby-plugin-vercel-builder",
"version": "1.1.3",
"version": "1.1.4",
"main": "dist/index.js",
"files": [
"dist",
@@ -14,8 +14,8 @@
"build:src": "tsc -p tsconfig.src.json"
},
"dependencies": {
"@vercel/build-utils": "6.2.1",
"@vercel/node": "2.9.2",
"@vercel/build-utils": "6.2.2",
"@vercel/node": "2.9.3",
"@vercel/routing-utils": "2.1.8",
"ajv": "8.12.0",
"esbuild": "0.14.47",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/go",
"version": "2.3.3",
"version": "2.3.4",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
@@ -36,7 +36,7 @@
"@types/node": "14.18.33",
"@types/node-fetch": "^2.3.0",
"@types/tar": "^4.0.0",
"@vercel/build-utils": "6.2.1",
"@vercel/build-utils": "6.2.2",
"@vercel/ncc": "0.24.0",
"async-retry": "1.3.1",
"execa": "^1.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/hydrogen",
"version": "0.0.49",
"version": "0.0.50",
"license": "MIT",
"main": "./dist/index.js",
"homepage": "https://vercel.com/docs",
@@ -21,7 +21,7 @@
"devDependencies": {
"@types/jest": "27.5.1",
"@types/node": "14.18.33",
"@vercel/build-utils": "6.2.1",
"@vercel/build-utils": "6.2.2",
"@vercel/static-config": "2.0.12",
"execa": "3.2.0",
"fs-extra": "11.1.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/next",
"version": "3.4.2",
"version": "3.4.3",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
@@ -45,7 +45,7 @@
"@types/semver": "6.0.0",
"@types/text-table": "0.2.1",
"@types/webpack-sources": "3.2.0",
"@vercel/build-utils": "6.2.1",
"@vercel/build-utils": "6.2.2",
"@vercel/nft": "0.22.5",
"@vercel/routing-utils": "2.1.8",
"async-sema": "3.0.1",

View File

@@ -236,6 +236,7 @@ type RoutesManifestOld = {
rsc?: {
header: string;
varyHeader: string;
contentTypeHeader: string;
};
skipMiddlewareUrlNormalize?: boolean;
};
@@ -2030,6 +2031,7 @@ export const onPrerenderRoute =
const rscVaryHeader =
routesManifest?.rsc?.varyHeader ||
'__rsc__, __next_router_state_tree__, __next_router_prefetch__';
const rscContentTypeHeader = routesManifest?.rsc?.contentTypeHeader || 'application/octet-stream';
prerenders[outputPathPage] = new Prerender({
expiration: initialRevalidate,
@@ -2069,7 +2071,7 @@ export const onPrerenderRoute =
...(isAppPathRoute
? {
initialHeaders: {
'content-type': 'application/octet-stream',
'content-type': rscContentTypeHeader,
vary: rscVaryHeader,
},
}

View File

@@ -26,7 +26,7 @@ if (parseInt(process.versions.node.split('.')[0], 10) >= 16) {
}
}
expect(lambdas.size).toBe(2);
expect(lambdas.size).toBe(1);
expect(buildResult.output['dashboard']).toBeDefined();
expect(buildResult.output['dashboard/another']).toBeDefined();
expect(buildResult.output['dashboard/changelog']).toBeDefined();

View File

@@ -295,13 +295,15 @@ export function createServerWithHelpers(
res.send = body => send(req, res, body);
res.json = jsonBody => json(req, res, jsonBody);
await handler(req, res);
} catch (err) {
if (err instanceof ApiError) {
sendError(res, err.statusCode, err.message);
} else {
throw err;
try {
await handler(req, res);
} catch (err) {
console.log(`Error from API Route ${req.url}: ${err.stack}`);
process.exit(1);
}
} catch (err) {
console.log(`Error while handling ${req.url}: ${err.message}`);
process.exit(1);
}
});

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/node",
"version": "2.9.2",
"version": "2.9.3",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -31,8 +31,8 @@
"dependencies": {
"@edge-runtime/vm": "2.0.0",
"@types/node": "14.18.33",
"@vercel/build-utils": "6.2.1",
"@vercel/node-bridge": "3.1.10",
"@vercel/build-utils": "6.2.2",
"@vercel/node-bridge": "3.1.11",
"@vercel/static-config": "2.0.12",
"edge-runtime": "2.0.0",
"esbuild": "0.14.47",

View File

@@ -34,8 +34,8 @@ async function serializeRequest(message: IncomingMessage) {
}
async function compileUserCode(
entrypointPath: string,
entrypointLabel: string,
entrypointFullPath: string,
entrypointRelativePath: string,
isMiddleware: boolean
): Promise<undefined | { userCode: string; wasmAssets: WasmAssets }> {
const { wasmAssets, plugin: edgeWasmPlugin } = createEdgeWasmPlugin();
@@ -51,7 +51,7 @@ async function compileUserCode(
legalComments: 'none',
bundle: true,
plugins: [edgeWasmPlugin],
entryPoints: [entrypointPath],
entryPoints: [entrypointFullPath],
write: false, // operate in memory
format: 'cjs',
});
@@ -59,7 +59,7 @@ async function compileUserCode(
const compiledFile = result.outputFiles?.[0];
if (!compiledFile) {
throw new Error(
`Compilation of ${entrypointLabel} produced no output files.`
`Compilation of ${entrypointRelativePath} produced no output files.`
);
}
@@ -72,7 +72,7 @@ async function compileUserCode(
// request metadata
const IS_MIDDLEWARE = ${isMiddleware};
const ENTRYPOINT_LABEL = '${entrypointLabel}';
const ENTRYPOINT_LABEL = '${entrypointRelativePath}';
// edge handler
${edgeHandlerTemplate}
@@ -134,13 +134,13 @@ async function createEdgeRuntime(params?: {
}
export async function createEdgeEventHandler(
entrypointPath: string,
entrypointLabel: string,
entrypointFullPath: string,
entrypointRelativePath: string,
isMiddleware: boolean
): Promise<(request: IncomingMessage) => Promise<VercelProxyResponse>> {
const userCode = await compileUserCode(
entrypointPath,
entrypointLabel,
entrypointFullPath,
entrypointRelativePath,
isMiddleware
);
const server = await createEdgeRuntime(userCode);
@@ -164,8 +164,13 @@ export async function createEdgeEventHandler(
const isUserError =
response.headers.get('x-vercel-failed') === 'edge-wrapper';
if (isUserError && response.status >= 500) {
// this error was "unhandled" from the user code's perspective
console.log(`Unhandled rejection: ${body}`);
// We can't currently get a real stack trace from the Edge Function error,
// but we can fake a basic one that is still usefult to the user.
const fakeStackTrace = ` at (${entrypointRelativePath})`;
const urlPath = extractUrlPath(entrypointRelativePath);
console.log(
`Error from API Route ${urlPath}: ${body}\n${fakeStackTrace}`
);
// this matches the serverless function bridge launcher's behavior when
// an error is thrown in the function
@@ -180,3 +185,13 @@ export async function createEdgeEventHandler(
};
};
}
// turns "api/some.func.js" into "api/some.func"
function extractUrlPath(entrypointRelativePath: string) {
const parts = entrypointRelativePath.split('.');
if (parts.length === 1) {
return entrypointRelativePath;
}
parts.pop();
return parts.join('.');
}

View File

@@ -166,11 +166,6 @@ export function register(opts: Options = {}): Register {
getCanonicalFileName: path => path,
};
function createTSError(diagnostics: ReadonlyArray<_ts.Diagnostic>) {
const message = formatDiagnostics(diagnostics, diagnosticHost);
return new NowBuildError({ code: 'NODE_TYPESCRIPT_ERROR', message });
}
function reportTSError(
diagnostics: _ts.Diagnostic[],
shouldExit: boolean | undefined
@@ -178,13 +173,13 @@ export function register(opts: Options = {}): Register {
if (!diagnostics || diagnostics.length === 0) {
return;
}
const error = createTSError(diagnostics);
const message = formatDiagnostics(diagnostics, diagnosticHost);
if (shouldExit) {
throw error;
throw new NowBuildError({ code: 'NODE_TYPESCRIPT_ERROR', message });
} else {
// Print error in red color and continue execution.
console.error('\x1b[31m%s\x1b[0m', error);
console.error(message);
}
}

View File

@@ -4,7 +4,8 @@
"probes": [
{
"path": "/",
"mustContain": "no-emit-on-error-false:RANDOMNESS_PLACEHOLDER"
"mustContain": "no-emit-on-error-false:RANDOMNESS_PLACEHOLDER",
"logMustContain": "error TS2339: Property 'thisDoesNotExist' does not exist on type 'IncomingMessage'"
}
]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/python",
"version": "3.1.45",
"version": "3.1.46",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -23,7 +23,7 @@
"@types/execa": "^0.9.0",
"@types/jest": "27.4.1",
"@types/node": "14.18.33",
"@vercel/build-utils": "6.2.1",
"@vercel/build-utils": "6.2.2",
"@vercel/ncc": "0.24.0",
"execa": "^1.0.0",
"typescript": "4.3.4"

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/redwood",
"version": "1.1.1",
"version": "1.1.2",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://vercel.com/docs",
@@ -27,7 +27,7 @@
"@types/aws-lambda": "8.10.19",
"@types/node": "14.18.33",
"@types/semver": "6.0.0",
"@vercel/build-utils": "6.2.1",
"@vercel/build-utils": "6.2.2",
"execa": "3.2.0",
"fs-extra": "11.1.0",
"typescript": "4.3.4"

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/remix",
"version": "1.2.12",
"version": "1.2.13",
"license": "MIT",
"main": "./dist/index.js",
"homepage": "https://vercel.com/docs",
@@ -24,7 +24,7 @@
"devDependencies": {
"@types/jest": "27.5.1",
"@types/node": "14.18.33",
"@vercel/build-utils": "6.2.1",
"@vercel/build-utils": "6.2.2",
"typescript": "4.9.4"
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "@vercel/ruby",
"author": "Nathan Cahill <nathan@nathancahill.com>",
"version": "1.3.61",
"version": "1.3.62",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
@@ -22,7 +22,7 @@
"devDependencies": {
"@types/fs-extra": "8.0.0",
"@types/semver": "6.0.0",
"@vercel/build-utils": "6.2.1",
"@vercel/build-utils": "6.2.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": "1.3.5",
"version": "1.3.6",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/build-step",
@@ -30,7 +30,7 @@
},
"dependencies": {
"@vercel/gatsby-plugin-vercel-analytics": "1.0.7",
"@vercel/gatsby-plugin-vercel-builder": "1.1.3"
"@vercel/gatsby-plugin-vercel-builder": "1.1.4"
},
"devDependencies": {
"@types/aws-lambda": "8.10.64",
@@ -42,9 +42,9 @@
"@types/node-fetch": "2.5.4",
"@types/promise-timeout": "1.3.0",
"@types/semver": "7.3.13",
"@vercel/build-utils": "6.2.1",
"@vercel/build-utils": "6.2.2",
"@vercel/frameworks": "1.3.0",
"@vercel/fs-detectors": "3.7.10",
"@vercel/fs-detectors": "3.7.11",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.1.8",
"@vercel/static-config": "2.0.12",

70
pnpm-lock.yaml generated
View File

@@ -84,6 +84,14 @@ importers:
'@vercel/node': link:../packages/node
typescript: 4.3.4
examples:
specifiers:
'@types/jest': 27.4.1
'@vercel/frameworks': 1.3.0
devDependencies:
'@types/jest': 27.4.1
'@vercel/frameworks': link:../packages/frameworks
packages/build-utils:
specifiers:
'@iarna/toml': 2.2.3
@@ -196,23 +204,23 @@ importers:
'@types/which': 1.3.2
'@types/write-json-file': 2.2.1
'@types/yauzl-promise': 2.1.0
'@vercel/build-utils': 6.2.1
'@vercel/client': 12.3.7
'@vercel/build-utils': 6.2.2
'@vercel/client': 12.3.8
'@vercel/error-utils': 1.0.8
'@vercel/frameworks': 1.3.0
'@vercel/fs-detectors': 3.7.10
'@vercel/fs-detectors': 3.7.11
'@vercel/fun': 1.0.4
'@vercel/go': 2.3.3
'@vercel/hydrogen': 0.0.49
'@vercel/go': 2.3.4
'@vercel/hydrogen': 0.0.50
'@vercel/ncc': 0.24.0
'@vercel/next': 3.4.2
'@vercel/node': 2.9.2
'@vercel/python': 3.1.45
'@vercel/redwood': 1.1.1
'@vercel/remix': 1.2.12
'@vercel/next': 3.4.3
'@vercel/node': 2.9.3
'@vercel/python': 3.1.46
'@vercel/redwood': 1.1.2
'@vercel/remix': 1.2.13
'@vercel/routing-utils': 2.1.8
'@vercel/ruby': 1.3.61
'@vercel/static-build': 1.3.5
'@vercel/ruby': 1.3.62
'@vercel/static-build': 1.3.6
'@zeit/source-map-support': 0.6.2
ajv: 6.12.2
alpha-sort: 2.0.1
@@ -223,7 +231,6 @@ importers:
async-retry: 1.1.3
async-sema: 2.1.4
ava: 2.2.0
boxen: 4.2.0
bytes: 3.0.0
chalk: 4.1.0
chance: 1.1.7
@@ -241,6 +248,7 @@ importers:
execa: 3.2.0
express: 4.17.1
fast-deep-equal: 3.1.3
find-up: 4.1.0
fs-extra: 10.0.0
get-port: 5.1.1
git-last-commit: 1.0.1
@@ -359,7 +367,6 @@ importers:
async-retry: 1.1.3
async-sema: 2.1.4
ava: 2.2.0
boxen: 4.2.0
bytes: 3.0.0
chalk: 4.1.0
chance: 1.1.7
@@ -377,6 +384,7 @@ importers:
execa: 3.2.0
express: 4.17.1
fast-deep-equal: 3.1.3
find-up: 4.1.0
fs-extra: 10.0.0
get-port: 5.1.1
git-last-commit: 1.0.1
@@ -438,7 +446,7 @@ importers:
'@types/node-fetch': 2.5.4
'@types/recursive-readdir': 2.2.0
'@types/tar-fs': 1.16.1
'@vercel/build-utils': 6.2.1
'@vercel/build-utils': 6.2.2
'@vercel/routing-utils': 2.1.8
'@zeit/fetch': 5.2.0
async-retry: 1.2.3
@@ -543,7 +551,7 @@ importers:
'@types/minimatch': 3.0.5
'@types/node': 14.18.33
'@types/semver': 7.3.10
'@vercel/build-utils': 6.2.1
'@vercel/build-utils': 6.2.2
'@vercel/error-utils': 1.0.8
'@vercel/frameworks': 1.3.0
'@vercel/routing-utils': 2.1.8
@@ -591,8 +599,8 @@ importers:
'@types/fs-extra': 11.0.1
'@types/node': 14.18.33
'@types/react': 18.0.26
'@vercel/build-utils': 6.2.1
'@vercel/node': 2.9.2
'@vercel/build-utils': 6.2.2
'@vercel/node': 2.9.3
'@vercel/routing-utils': 2.1.8
ajv: 8.12.0
esbuild: 0.14.47
@@ -626,7 +634,7 @@ importers:
'@types/node': 14.18.33
'@types/node-fetch': ^2.3.0
'@types/tar': ^4.0.0
'@vercel/build-utils': 6.2.1
'@vercel/build-utils': 6.2.2
'@vercel/ncc': 0.24.0
async-retry: 1.3.1
execa: ^1.0.0
@@ -658,7 +666,7 @@ importers:
specifiers:
'@types/jest': 27.5.1
'@types/node': 14.18.33
'@vercel/build-utils': 6.2.1
'@vercel/build-utils': 6.2.2
'@vercel/static-config': 2.0.12
execa: 3.2.0
fs-extra: 11.1.0
@@ -689,7 +697,7 @@ importers:
'@types/semver': 6.0.0
'@types/text-table': 0.2.1
'@types/webpack-sources': 3.2.0
'@vercel/build-utils': 6.2.1
'@vercel/build-utils': 6.2.2
'@vercel/nft': 0.22.5
'@vercel/routing-utils': 2.1.8
async-sema: 3.0.1
@@ -768,10 +776,10 @@ importers:
'@types/node': 14.18.33
'@types/node-fetch': ^2.6.1
'@types/test-listen': 1.1.0
'@vercel/build-utils': 6.2.1
'@vercel/build-utils': 6.2.2
'@vercel/ncc': 0.24.0
'@vercel/nft': 0.22.5
'@vercel/node-bridge': 3.1.10
'@vercel/node-bridge': 3.1.11
'@vercel/static-config': 2.0.12
content-type: 1.0.4
cookie: 0.4.0
@@ -852,7 +860,7 @@ importers:
'@types/execa': ^0.9.0
'@types/jest': 27.4.1
'@types/node': 14.18.33
'@vercel/build-utils': 6.2.1
'@vercel/build-utils': 6.2.2
'@vercel/ncc': 0.24.0
execa: ^1.0.0
typescript: 4.3.4
@@ -870,7 +878,7 @@ importers:
'@types/aws-lambda': 8.10.19
'@types/node': 14.18.33
'@types/semver': 6.0.0
'@vercel/build-utils': 6.2.1
'@vercel/build-utils': 6.2.2
'@vercel/nft': 0.22.5
'@vercel/routing-utils': 2.1.8
execa: 3.2.0
@@ -894,7 +902,7 @@ importers:
specifiers:
'@types/jest': 27.5.1
'@types/node': 14.18.33
'@vercel/build-utils': 6.2.1
'@vercel/build-utils': 6.2.2
'@vercel/nft': 0.22.5
typescript: 4.9.4
dependencies:
@@ -925,7 +933,7 @@ importers:
specifiers:
'@types/fs-extra': 8.0.0
'@types/semver': 6.0.0
'@vercel/build-utils': 6.2.1
'@vercel/build-utils': 6.2.2
'@vercel/ncc': 0.24.0
execa: 2.0.4
fs-extra: ^7.0.1
@@ -952,11 +960,11 @@ importers:
'@types/node-fetch': 2.5.4
'@types/promise-timeout': 1.3.0
'@types/semver': 7.3.13
'@vercel/build-utils': 6.2.1
'@vercel/build-utils': 6.2.2
'@vercel/frameworks': 1.3.0
'@vercel/fs-detectors': 3.7.10
'@vercel/fs-detectors': 3.7.11
'@vercel/gatsby-plugin-vercel-analytics': 1.0.7
'@vercel/gatsby-plugin-vercel-builder': 1.1.3
'@vercel/gatsby-plugin-vercel-builder': 1.1.4
'@vercel/ncc': 0.24.0
'@vercel/routing-utils': 2.1.8
'@vercel/static-config': 2.0.12
@@ -6055,7 +6063,7 @@ packages:
/@types/jest-expect-message/1.0.3:
resolution: {integrity: sha512-sp70Lc8POkOcXHEcLERpX/7B/BtQiqIYz3AvC9ZMNKSaiDttr8hKvz9DljIn7N6WJi3ioVoTtB1utDAX46oPlg==}
dependencies:
'@types/jest': 27.4.1
'@types/jest': 29.2.1
dev: true
/@types/jest/27.4.1:

View File

@@ -1,3 +1,4 @@
packages:
- 'packages/*'
- 'api'
- 'examples'