Compare commits

...

44 Commits

Author SHA1 Message Date
Steven
d5537500d8 Publish Stable
- @vercel/build-utils@5.4.2
 - vercel@28.2.3
 - @vercel/client@12.2.4
 - @vercel/frameworks@1.1.4
 - @vercel/fs-detectors@3.0.0
 - @vercel/go@2.2.5
 - @vercel/hydrogen@0.0.18
 - @vercel/next@3.1.25
 - @vercel/node@2.5.14
 - @vercel/python@3.1.14
 - @vercel/redwood@1.0.23
 - @vercel/remix@1.0.24
 - @vercel/ruby@1.3.31
 - @vercel/static-build@1.0.22
2022-09-07 15:39:47 -04:00
Andrew Gadzik
4b1736b2f2 [fs-detectors] optimize readdir for framework detection (#8497)
Adding the ability to explicitly set the `hasPath` and `isFile` caches via `readdir`

**Why is this needed?**

For framework detection, this allows us to fetch the contents of a directory (the root directory) of a git repo, and make assumptions about the existence of files,

```ts
import frameworks from "@vercel/frameworks";

// we can make the set of all file paths needed for framework detection like this
const setOfPaths = new Set(
  frameworks.frameworks
    .flatMap((f) => [
      ...(f.detectors?.every ?? []),
      ...(f.detectors?.some ?? []),
    ])
    .map((d) => d.path)
);

// then we can read the contents of the root directory of a git repo via the `DetectorFilesystem`
fs.readdir('./', { potentialFiles: [...setOfPaths] });

// The filesystem has been fully primed - any network calls from here will only happen if needed.
// This works because the logic of `detectFramework` calls `hasPath` and then `isFile` before 
// fetching file contents.  Since the filesystem knows this information, it doesn't have to make 
// any additional network calls 🙌 
const framework = detectFramework(fs);
```

### Related Issues

Related to [HIT-57](https://linear.app/vercel/issue/HIT-57/monorepo-detection-api-prevent-rate-limits)

### 📋 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-09-07 15:35:56 -04:00
Sean Massa
9da1c6fa66 [tests] set retry count to 0 when running tests locally (#8529)
When running tests locally that fail to make a fetch request, the retries add a lot of noise to debugging. This PR sets those retry counts to `0` locally, but keeps them at their current value for CI.
2022-09-07 18:47:09 +00:00
Jiachi Liu
a6c320846e [@vercel/next] Handle nextjs edge routes from app dir (#8524)
### Related Issues

Add handling for edge SSR in app dir, normalize the edge page routes
from app dir, like what we did for ones in `pages/`.

### 📋 Checklist

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR

Co-authored-by: JJ Kasper <jj@jjsweb.site>
2022-09-06 16:19:28 -07:00
Shu Uesugi
4973814978 [frameworks] Update Next.js Logos (#8505)
Update them to match the new logos. See: https://vercel.com/design/brands#next-js

<img width="869" alt="Screen Shot 2022-09-02 at 10 43 52 AM" src="https://user-images.githubusercontent.com/992008/188208956-aae10437-77ef-43c8-9bc1-c2499e509a7c.png">
2022-09-02 18:06:31 +00:00
Sean Massa
f1289ff263 [node] update middleware-no-response behavior to match production changes (#8500)
Production was updated to allow middleware without responses to pass through. This PR makes `vc dev` behave the same way. Tests were updated.
2022-09-01 22:19:10 +00:00
Steven
3ff93279cd Publish Stable
- vercel@28.2.2
 - @vercel/next@3.1.24
 - @vercel/node@2.5.13
 - @vercel/redwood@1.0.22
 - @vercel/remix@1.0.23
2022-09-01 16:42:27 -04:00
Steven
58f9d649a8 [next][node][remix][redwood] Bump @vercel/nft@0.22.1 (#8502)
Bump `@vercel/nft` to version [0.22.1](https://github.com/vercel/nft/releases/tag/0.22.1)
2022-09-01 16:41:30 -04:00
JJ Kasper
cadc082ad1 [next] Fix index app dir case (#8501)
### Related Issues

This ensures we properly normalize the `/` case from the `app-path-routes-manifest` as the outputs expect this to be normalized to `index`. 

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-09-01 12:06:18 -07:00
Steven
2906d83eae Publish Stable
- @vercel/build-utils@5.4.1
 - vercel@28.2.1
 - @vercel/client@12.2.3
 - @vercel/edge@0.0.4
 - @vercel/fs-detectors@2.1.0
 - @vercel/go@2.2.4
 - @vercel/hydrogen@0.0.17
 - @vercel/next@3.1.23
 - @vercel/node@2.5.12
 - @vercel/python@3.1.13
 - @vercel/redwood@1.0.21
 - @vercel/remix@1.0.22
 - @vercel/ruby@1.3.30
 - @vercel/static-build@1.0.21
2022-09-01 13:16:42 -04:00
Steven
675c3e2915 [cli] Change error message from Error! to Error: (#8498)
We have code that tries to detect and highlight errors in the build logs, however it doesn't look for `Error!`, only `Error:`.

We could update that highlight code or we could update Vercel CLI to make it consistent.

This PR is the latter.
2022-09-01 13:12:41 -04:00
Steven
13a8a7dbf6 [next] Revert nft to 0.21.0 (#8499)
In Next.js 12, we introduce `outputFileTracing` as the default behavior. This moved `@vercel/nft` from `@vercel/next` into `next` itself so users can upgrade or downgrade Next.js if there is a bug in `@vercel/nft`.

Unfortunately, some users are setting `outputFileTracing: false` which deopts tracing back from `next` to `@vercel/next`. So we should pin `@vercel/nft` here and never upgrade in case of any bugs.
2022-09-01 09:13:35 -07:00
Andrew Gadzik
b480e632a3 Add writeFile to DetectorFilesystem (#8493)
Adds a `writeFile` function to `DetectorFilesystem` that will be used to update the various file cache maps.

When detecting npm7+ monorepos, we identified a performance improvement where the service can inspect the `package-lock.json` file for workspaces, and reuse the package information for each workspace in framework-detection.

The pseudo code in `vercel/api` will look something like this

For a given lockfile
```json
{
  ...,
  "packages": {
    "": {
      "name": "npm-workspaces",
      "version": "1.0.0",
      "license": "ISC",
      "workspaces": {
        "packages": [
          "apps/*"
        ]
      }
    },
    "apps/admin": {
      "version": "0.1.0",
      "dependencies": {
        "next": "12.2.5",
        "react": "18.2.0",
        "react-dom": "18.2.0"
      },
      "devDependencies": {
        "eslint": "8.23.0",
        "eslint-config-next": "12.2.5"
      }
    },
    ...,
}
```

```ts
// for each projectPath we detect in package-lock.json
// switch the cwd of the fs to the project directory
const projectFs = fs.chdir(projectPath);
// gets the package info from the lockfile
const projectPackageInfo = lockFile.packages[projectPath];
// insert this content into fs cache
projectFs.writeFile('package.json', projectPackageInfo)
// call detectFramework, which should now have a cached "package.json" file 
const projectFramework = await detectFramework(projectFs);
```

### Related Issues

Related to [HIT-57](https://linear.app/vercel/issue/HIT-57/monorepo-detection-api-prevent-rate-limits)

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-31 18:51:03 +00:00
Chris Barber
bc6c364888 [node] Added 'cause' to error message (#8467)
When running `vc dev` for a project using `experimental-edge` runtime and a fetch-related error occurs, the message is a little vague:

```
Unhandled rejection: fetch failed
```

In this case, the fetch promise (defined in @edge-runtime/primitives) is being rejected with a `TypeError: fetch failed` that has a `cause` property containing the actual `response.error` message. The `response.error` is the actual error and is never reported to the user.

This PR simply appends the `cause` message, if exists, to the fetch failure message.

```
Unhandled rejection: fetch failed: Error: getaddrinfo ENOTFOUND undefined
```

### 📋 Checklist

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-30 02:32:38 +00:00
Steven
6cad10c899 [build-utils] Replace npm bin with custom implementation (#8483)
This PR removes `npm bin` in favor of a custom implementation since the command will be removed in npm 9.

- Related to https://github.com/npm/statusboard/issues/537
2022-08-30 01:23:34 +00:00
Gal Schlezinger
0d78ec4666 [edge] Add markdown documentation using TypeDoc (#8446) 2022-08-29 12:36:44 +03:00
Nathan Rajlich
b05d653cf1 [cli] Use unique "name" in package.json test fixtures (#8475)
This fixes a few warnings when running Jest that look something like:

```
jest-haste-map: Haste module naming collision: nextjs
  The following files share their name; please adjust your hasteImpl:
    * <rootDir>/test/dev/fixtures/10-nextjs-node/package.json
    * <rootDir>/test/dev/fixtures/25-nextjs-src-dir/package.json
```
2022-08-26 21:43:25 +00:00
Nathan Rajlich
5a38b94de2 [cli] Replace "progress" module with custom implementation (#8397)
The `progress` module is overkill for what we are using it for and it's inconsistent with our styling for the rest of the deployment process.

So drop the npm module in favor of an in-house progress bar implementation that can be used inside of the Ora `output.spinner()` that we already use for the other steps of the deployment process.

**Before:**

<img width="307" alt="Screen Shot 2022-08-25 at 5 07 28 PM" src="https://user-images.githubusercontent.com/71256/186790052-18431ac0-b05b-434e-b885-9eb76d880b9d.png">

**After:**

<img width="347" alt="Screen Shot 2022-08-25 at 5 09 35 PM" src="https://user-images.githubusercontent.com/71256/186790168-8dd3e9a2-863a-45cc-bd21-d98308d34e22.png">
2022-08-26 21:03:38 +00:00
Nathan Rajlich
6668d6cf21 [client] Add "tar-fs" dependency (#8473)
Fixes #8471.
2022-08-26 18:02:01 +00:00
Andrew Gadzik
b63f444abd [fs-detectors] Add a default workspace manager when there's no lock file (#8469)
This fixes an issue with this example,

https://vercel.com/api/v1/integrations/detect-eligible-projects?url=https%3A%2F%2Fgithub.com%2Fvercel%2Fturborepo%2Ftree%2Fmain%2Fexamples%2Fkitchen-sink

Where the repo has no lockfile, and hence our workspace detection fails.  This fix is to add a fallback of "npm" in this scenario

### Related Issues

Fixes https://github.com/vercel/turbo-internal/issues/232

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-26 16:24:13 +00:00
Steven
9d71d4332f [tests] Update codeowners for fs-detectors (#8470)
Update codeowners for `@vercel/fs-detectors` to add @agadzik and @chloetedder
2022-08-26 15:43:14 +00:00
Steven
151db21a2f [tests] Add required labels (#8464)
This action will fail if the required labels are not applied to a PR.

We allow multiple `area` labels and only a single `semver` label.

- Example failure: https://github.com/vercel/vercel/runs/8024949804?check_suite_focus=true#step:2:19
- Example success: https://github.com/vercel/vercel/runs/8026212031?check_suite_focus=true#step:2:19
2022-08-25 22:20:37 +00:00
Steven
aba9c95ea2 [tests] Change PR automerge label (#8465)
We decided to change this to an explicit label and rely on a separate action to check for correct labels (see #8464).

This action likely won't work for PRs from forks, but that might be okay because our other actions don't work for forks either.
2022-08-25 22:05:55 +00:00
Sean Massa
e7e0a55b72 Publish Stable
- @vercel/build-utils@5.4.0
 - vercel@28.2.0
 - @vercel/client@12.2.2
 - @vercel/go@2.2.3
 - @vercel/hydrogen@0.0.16
 - @vercel/next@3.1.22
 - @vercel/node@2.5.11
 - @vercel/python@3.1.12
 - @vercel/redwood@1.0.20
 - @vercel/remix@1.0.21
 - @vercel/ruby@1.3.29
 - @vercel/static-build@1.0.20
2022-08-25 15:50:50 -05:00
Sean Massa
c2163e3e4f [tests] Update CODEOWNERS (#8463)
Changes:

- added @cb1kenobi @Ethan-Arrowood to sections owned by the Vercel CLI team
- removed outdated code owners: if this was you and you believe this was in error, let us know!
- removed no-op lines
2022-08-25 20:06:49 +00:00
Nathan Rajlich
873f549637 [cli] Normalize Builder output paths in vc dev (#8461)
Fixes https://github.com/vercel/og-image/issues/180.
2022-08-25 19:09:12 +00:00
Naoyuki Kanezawa
ead1e411ee [next] support middleware-manifest v2 (#8319)
### Related Issues

As the title, support a new version of middleware-manifest of next.js that is going to be added in https://github.com/vercel/next.js/pull/39257

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-25 18:57:12 +00:00
Sean Massa
7c1c089a70 [tests] rename go test function (#8458)
This function name can be anything, but the current one is confusing. This is what the function gets renamed to during compilation. Let's change it back to avoid confusion.
2022-08-25 15:11:05 +00:00
Ethan Arrowood
a7dbd9649b [cli] add override support for vc build and vc dev (#8451)
### Related Issues

Adds configuration override support to `vc build` and `vc dev` commands.

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-25 03:00:52 +00:00
Chris Barber
2e439045c9 [cli] Improved 'vc git' help description for --yes flag (#8460)
When adding the `--yes` flag to the `vc git` command's help screen, the description was copy/pasted and not accurate, so this PR improves the description to be more accurate.

### 📋 Checklist

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-25 01:25:30 +00:00
Steven
520e0d01f4 [frameworks] Fix link to framework logo (#8459)
This link should be using our custom domain
2022-08-24 16:07:17 -04:00
Steven
3856623785 [build-utils] Prefix debug message (#8378)
This PR adds `VERCEL_DEBUG_PREFIX` environment variable which lets you set a prefix for the debug logs.
2022-08-24 18:50:20 +00:00
Steven
1c14e945f9 [tests] Reduce test logs (#8455)
This PR reduces the noisy test logs that currently look like this:

<img width="1013" alt="image" src="https://user-images.githubusercontent.com/229881/186479890-e954ca9c-782e-49b8-89e7-6409185bb490.png">
2022-08-24 14:00:10 -04:00
Steven
32357fc06f [node][next][redwood][remix] Bump @vercel/nft@0.22.0 (#8454)
This PR bumps `@vercel/nft` to version [0.22.0](https://github.com/vercel/nft/releases/tag/0.22.0)
2022-08-24 12:13:34 -04:00
Lee Robinson
f7c57dc539 [examples] Update SvelteKit example for breaking changes. (#8439)
There were recently a few breaking changes introduced into SvelteKit. This PR clones that latest changes from a demo SvelteKit application, while retaining support for Vercel Analytics.

Deployed latest to https://sveltekit-template.vercel.app/ and confirmed analytics are sending.
2022-08-24 15:15:50 +00:00
github-actions[bot]
34f7c35c13 [examples] Upgrade Next.js to version 12.2.5 (#8453)
This auto-generated PR updates Next.js to version 12.2.5

Co-authored-by: Vercel Team Bot <team@zeit.co>
2022-08-24 10:41:54 -04:00
Sean Massa
6700630feb Publish Stable
- @vercel/build-utils@5.3.2
 - vercel@28.1.4
 - @vercel/client@12.2.1
 - @vercel/go@2.2.2
 - @vercel/hydrogen@0.0.15
 - @vercel/next@3.1.21
 - @vercel/node@2.5.10
 - @vercel/python@3.1.11
 - @vercel/redwood@1.0.19
 - @vercel/remix@1.0.20
 - @vercel/ruby@1.3.28
 - @vercel/static-build@1.0.19
2022-08-24 09:09:02 -05:00
Sean Massa
34cd8b4144 [cli] revert git connect inside vc link (#8450) 2022-08-24 09:07:04 -05:00
Sean Massa
ad0ed6d852 Publish Stable
- vercel@28.1.3
 - @vercel/node@2.5.9
2022-08-23 16:58:19 -05:00
Gal Schlezinger
0bad09b47a [node] fix WebAssembly bindings evaluating to undefined (#8417)
This was broken since https://github.com/vercel/vercel/pull/8242, which accidentally pushed all WebAssembly modules to be nested under `global.wasmBindings` instead of being directly under the global scope.

### Related Issues

- This was broken since https://github.com/vercel/vercel/pull/8242

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-23 21:56:13 +00:00
Steven
5120689bf2 [tests] Fix update-next script (#8452)
Follow up to #8447 since exec returns a buffer, not a string.
2022-08-23 17:28:38 -04:00
Steven
5a39fd9242 [tests] Automatically update Next.js example periodically (#8447)
Automatically update Next.js example every 4 hours using `create-next-app`
2022-08-23 17:19:55 +00:00
Sean Massa
352cd00ef0 Publish Stable
- vercel@28.1.2
 - @vercel/go@2.2.1
2022-08-23 10:15:26 -05:00
Sean Massa
abfe817f86 [go] ignore bad go handlers and rename functions with more precision (#8422)
Co-authored-by: Steven <steven@ceriously.com>
2022-08-23 10:14:21 -05:00
163 changed files with 6353 additions and 8313 deletions

28
.github/CODEOWNERS vendored
View File

@@ -1,23 +1,17 @@
# Documentation
# https://help.github.com/en/articles/about-code-owners
* @TooTallNate @EndangeredMassa @styfle
/.github/workflows @TooTallNate @EndangeredMassa @styfle @ijjk
/packages/frameworks @TooTallNate @EndangeredMassa @styfle @AndyBitz
/packages/cli/src/commands/domains @javivelasco @mglagola @anatrajkovska
/packages/cli/src/commands/certs @javivelasco @mglagola @anatrajkovska
/packages/cli/src/commands/env @styfle @lucleray
/packages/client @TooTallNate @EndangeredMassa @styfle
/packages/build-utils @TooTallNate @EndangeredMassa @styfle @AndyBitz
/packages/middleware @gdborton @javivelasco
/packages/node @TooTallNate @EndangeredMassa @styfle
/packages/node-bridge @TooTallNate @EndangeredMassa @styfle @ijjk
/packages/next @TooTallNate @ijjk
/packages/go @TooTallNate @EndangeredMassa @styfle
/packages/python @TooTallNate @EndangeredMassa @styfle
/packages/ruby @TooTallNate @EndangeredMassa @styfle
/packages/static-build @TooTallNate @EndangeredMassa @styfle @AndyBitz
/packages/routing-utils @TooTallNate @EndangeredMassa @styfle @ijjk
* @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood
/.github/workflows @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk
/packages/cli/src/commands/domains @mglagola @anatrajkovska
/packages/cli/src/commands/certs @mglagola @anatrajkovska
/packages/cli/src/commands/env @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood
/packages/fs-detectors @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @agadzik @chloetedder
/packages/middleware @gdborton @vercel/edge-function
/packages/node-bridge @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk
/packages/next @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk
/packages/routing-utils @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk
/packages/edge @vercel/edge-function
/examples @leerob
/examples/create-react-app @Timer
/examples/nextjs @timneutkens @ijjk @styfle

25
.github/workflows/cron-update-next.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Cron Update Next
on:
# Run every 4 hours https://crontab.guru/every-4-hours
schedule:
- cron: '0 */4 * * *'
jobs:
create-pull-request:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
# 0 means fetch all commits so we can commit and push in the script below
with:
fetch-depth: 0
- name: Create Pull Request
uses: actions/github-script@v6
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
# See https://github.com/actions/github-script#run-a-separate-file-with-an-async-function
with:
script: |
const script = require('./utils/update-next.js')
await script({ github, context })

22
.github/workflows/required-pr-label.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: Required PR Label
on:
pull_request:
types: [opened, labeled, unlabeled, synchronize]
jobs:
label:
runs-on: ubuntu-latest
steps:
- name: Check PR Labels
uses: actions/github-script@v6
with:
script: |
const labels = context.payload.pull_request.labels.map(l => l.name);
if (labels.filter(l => l.startsWith('area:')).length === 0) {
console.error('\u001b[31mMissing label: Please add at least one "area" label.');
process.exit(1);
}
if (labels.filter(l => l.startsWith('semver:')).length !== 1) {
console.error('\u001b[31mMissing label: Please add exactly one "semver" label.');
process.exit(1);
}
console.log('\u001b[32mSuccess: This pull request has correct labels, thanks!');

View File

@@ -1,7 +1,7 @@
version = 1
[merge]
automerge_label = ["semver-major","semver-minor","semver-patch"]
automerge_label = ["pr: automerge"]
blacklist_title_regex = "^WIP.*"
blacklist_labels = ["work in progress"]
method = "squash"

View File

@@ -17,7 +17,7 @@ const frameworks = (_frameworks as Framework[])
};
if (framework.logo) {
framework.logo = `https://res.cloudinary.com/zeit-inc/image/fetch/${framework.logo}`;
framework.logo = `https://assets.vercel.com/zeit-inc/image/fetch/${framework.logo}`;
}
return framework;

View File

@@ -26,10 +26,11 @@ yarn-error.log*
.pnpm-debug.log*
# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@@ -1,6 +1,7 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
}
module.exports = nextConfig

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,6 @@
{
"name": "nextjs",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
@@ -7,12 +9,12 @@
"lint": "next lint"
},
"dependencies": {
"next": "12.1.4",
"react": "18.0.0",
"react-dom": "18.0.0"
"next": "12.2.5",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"eslint": "8.12.0",
"eslint-config-next": "12.1.4"
"eslint": "8.22.0",
"eslint-config-next": "12.2.5"
}
}

View File

@@ -114,3 +114,16 @@
flex-direction: column;
}
}
@media (prefers-color-scheme: dark) {
.card,
.footer {
border-color: #222;
}
.code {
background: #111;
}
.logo img {
filter: invert(1);
}
}

View File

@@ -14,3 +14,13 @@ a {
* {
box-sizing: border-box;
}
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
body {
color: white;
background: black;
}
}

1703
examples/nextjs/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -8,21 +8,9 @@ Everything you need to build a Svelte project, powered by [`create-svelte`](http
_Live Example: https://sveltekit-template.vercel.app_
## Creating a project
If you're seeing this, you've probably already done this step. Congrats!
```bash
# create a new project in the current directory
npm init svelte
# create a new project in my-app
npm init svelte my-app
```
## Developing
Once you've created a project and installed dependencies with `pnpm install`, start a development server:
Once you've installed dependencies with `pnpm install`, start a development server:
```bash
pnpm run dev

View File

@@ -10,4 +10,8 @@
"sourceMap": true,
"strict": true
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias and https://kit.svelte.dev/docs/configuration#files
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

View File

@@ -1,25 +1,25 @@
{
"private": true,
"scripts": {
"dev": "svelte-kit dev",
"build": "svelte-kit build",
"dev": "vite dev",
"build": "vite build",
"package": "svelte-kit package",
"preview": "svelte-kit preview",
"prepare": "svelte-kit sync",
"preview": "vite preview",
"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=. ."
"lint": "prettier --check .",
"format": "prettier --write ."
},
"devDependencies": {
"@sveltejs/adapter-auto": "1.0.0-next.50",
"@sveltejs/kit": "1.0.0-next.347",
"@types/cookie": "^0.4.1",
"prettier": "^2.5.1",
"prettier-plugin-svelte": "^2.5.0",
"@sveltejs/adapter-auto": "next",
"@sveltejs/kit": "next",
"@types/cookie": "^0.5.1",
"prettier": "^2.6.2",
"prettier-plugin-svelte": "^2.7.0",
"svelte": "^3.46.0",
"svelte-check": "^2.2.6",
"typescript": "~4.6.2"
"svelte-check": "^2.7.1",
"typescript": "^4.7.4",
"vite": "^3.0.8"
},
"type": "module",
"dependencies": {

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
/// <reference types="@sveltejs/kit" />
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
// and what to do when importing types
declare namespace App {
interface Locals {
userid: string;
@@ -9,7 +8,7 @@ declare namespace App {
// interface Platform {}
// interface Session {}
// interface PrivateEnv {}
// interface Stuff {}
// interface PublicEnv {}
}

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width" />
%sveltekit.head%
</head>
<body>

View File

@@ -31,11 +31,11 @@ import { invalidate } from '$app/navigation';
export function enhance(form, { pending, error, result } = {}) {
let current_token;
/** @param {SubmitEvent} e */
async function handle_submit(e) {
/** @param {SubmitEvent} event */
async function handle_submit(event) {
const token = (current_token = {});
e.preventDefault();
event.preventDefault();
const data = new FormData(form);
@@ -63,11 +63,11 @@ export function enhance(form, { pending, error, result } = {}) {
} else {
console.error(await response.text());
}
} catch (e) {
if (error && e instanceof Error) {
error({ data, form, error: e, response: null });
} catch (err) {
if (error && err instanceof Error) {
error({ data, form, error: err, response: null });
} else {
throw e;
throw err;
}
}
}

View File

@@ -0,0 +1,58 @@
<script>
import Header from '$lib/header/Header.svelte';
import { webVitals } from '$lib/vitals';
import { browser } from '$app/env';
import { page } from '$app/stores';
import '../app.css';
let analyticsId = import.meta.env.VERCEL_ANALYTICS_ID;
$: if (browser && analyticsId) {
webVitals({
path: $page.url.pathname,
params: $page.params,
analyticsId
})
}
</script>
<Header />
<main>
<slot />
</main>
<footer>
<p>visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to learn SvelteKit</p>
</footer>
<style>
main {
flex: 1;
display: flex;
flex-direction: column;
padding: 1rem;
width: 100%;
max-width: 1024px;
margin: 0 auto;
box-sizing: border-box;
}
footer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 40px;
}
footer a {
font-weight: bold;
}
@media (min-width: 480px) {
footer {
padding: 40px 0;
}
}
</style>

View File

@@ -0,0 +1 @@
export const prerender = true;

View File

@@ -0,0 +1,57 @@
<script>
import Counter from '$lib/Counter.svelte';
</script>
<svelte:head>
<title>Home</title>
<meta name="description" content="Svelte demo app" />
</svelte:head>
<section>
<h1>
<span class="welcome">
<picture>
<source srcset="svelte-welcome.webp" type="image/webp" />
<img src="svelte-welcome.png" alt="Welcome" />
</picture>
</span>
to your new<br />SvelteKit app
</h1>
<h2>
try editing <strong>src/routes/index.svelte</strong>
</h2>
<Counter />
</section>
<style>
section {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex: 1;
}
h1 {
width: 100%;
}
.welcome {
display: block;
position: relative;
width: 100%;
height: 0;
padding: 0 0 calc(100% * 495 / 2048) 0;
}
.welcome img {
position: absolute;
width: 100%;
height: 100%;
top: 0;
display: block;
}
</style>

View File

@@ -1,58 +0,0 @@
<script>
import Header from '$lib/header/Header.svelte';
import { webVitals } from '$lib/vitals';
import { browser } from '$app/env';
import { page } from '$app/stores';
import '../app.css';
let analyticsId = import.meta.env.VERCEL_ANALYTICS_ID;
$: if (browser && analyticsId) {
webVitals({
path: $page.url.pathname,
params: $page.params,
analyticsId
})
}
</script>
<Header />
<main>
<slot />
</main>
<footer>
<p>visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to learn SvelteKit</p>
</footer>
<style>
main {
flex: 1;
display: flex;
flex-direction: column;
padding: 1rem;
width: 100%;
max-width: 1024px;
margin: 0 auto;
box-sizing: border-box;
}
footer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 40px;
}
footer a {
font-weight: bold;
}
@media (min-width: 480px) {
footer {
padding: 40px 0;
}
}
</style>

View File

@@ -1,50 +0,0 @@
<script context="module">
import { browser, dev } from '$app/env';
// we don't need any JS on this page, though we'll load
// it in dev so that we get hot module replacement...
export const hydrate = dev;
// ...but if the client-side router is already loaded
// (i.e. we came here from elsewhere in the app), use it
export const router = browser;
// since there's no dynamic data here, we can prerender
// it so that it gets served as a static asset in prod
export const prerender = true;
</script>
<svelte:head>
<title>About</title>
<meta name="description" content="About this app" />
</svelte:head>
<div class="content">
<h1>About this app</h1>
<p>
This is a <a href="https://kit.svelte.dev">SvelteKit</a> app. You can make your own by typing the
following into your command line and following the prompts:
</p>
<pre>npm init svelte</pre>
<p>
The page you're looking at is purely static HTML, with no client-side interactivity needed.
Because of that, we don't need to load any JavaScript. Try viewing the page's source, or opening
the devtools network panel and reloading.
</p>
<p>
The <a href="/todos">TODOs</a> page illustrates SvelteKit's data loading and form handling. Try using
it with JavaScript disabled!
</p>
</div>
<style>
.content {
width: 100%;
max-width: var(--column-width);
margin: var(--column-margin-top) auto 0 auto;
}
</style>

View File

@@ -0,0 +1,13 @@
import { browser, dev } from '$app/env';
// we don't need any JS on this page, though we'll load
// it in dev so that we get hot module replacement...
export const hydrate = dev;
// ...but if the client-side router is already loaded
// (i.e. we came here from elsewhere in the app), use it
export const router = browser;
// since there's no dynamic data here, we can prerender
// it so that it gets served as a static asset in prod
export const prerender = true;

View File

@@ -0,0 +1,34 @@
<svelte:head>
<title>About</title>
<meta name="description" content="About this app" />
</svelte:head>
<div class="content">
<h1>About this app</h1>
<p>
This is a <a href="https://kit.svelte.dev">SvelteKit</a> app. You can make your own by typing the
following into your command line and following the prompts:
</p>
<pre>npm create svelte@latest</pre>
<p>
The page you're looking at is purely static HTML, with no client-side interactivity needed.
Because of that, we don't need to load any JavaScript. Try viewing the page's source, or opening
the devtools network panel and reloading.
</p>
<p>
The <a href="/todos">TODOs</a> page illustrates SvelteKit's data loading and form handling. Try using
it with JavaScript disabled!
</p>
</div>
<style>
.content {
width: 100%;
max-width: var(--column-width);
margin: var(--column-margin-top) auto 0 auto;
}
</style>

View File

@@ -1,60 +0,0 @@
<script context="module">
export const prerender = true;
</script>
<script>
import Counter from '$lib/Counter.svelte';
</script>
<svelte:head>
<title>Home</title>
<meta name="description" content="Svelte demo app" />
</svelte:head>
<section>
<h1>
<div class="welcome">
<picture>
<source srcset="svelte-welcome.webp" type="image/webp" />
<img src="svelte-welcome.png" alt="Welcome" />
</picture>
</div>
to your new<br />SvelteKit app
</h1>
<h2>
try editing <strong>src/routes/index.svelte</strong>
</h2>
<Counter />
</section>
<style>
section {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex: 1;
}
h1 {
width: 100%;
}
.welcome {
position: relative;
width: 100%;
height: 0;
padding: 0 0 calc(100% * 495 / 2048) 0;
}
.welcome img {
position: absolute;
width: 100%;
height: 100%;
top: 0;
display: block;
}
</style>

View File

@@ -0,0 +1,62 @@
import { error } from '@sveltejs/kit';
import { api } from './api';
/**
* @typedef {{
* uid: string;
* created_at: Date;
* text: string;
* done: boolean;
* pending_delete: boolean;
* }} Todo
*/
/** @type {import('./$types').PageServerLoad} */
export const load = async ({ locals }) => {
// locals.userid comes from src/hooks.js
const response = await api('GET', `todos/${locals.userid}`);
if (response.status === 404) {
// user hasn't created a todo list.
// start with an empty array
return {
/** @type {Todo[]} */
todos: []
};
}
if (response.status === 200) {
return {
/** @type {Todo[]} */
todos: await response.json()
};
}
throw error(response.status);
};
/** @type {import('./$types').Action} */
export const POST = async ({ request, locals }) => {
const form = await request.formData();
await api('POST', `todos/${locals.userid}`, {
text: form.get('text')
});
};
/** @type {import('./$types').Action} */
export const PATCH = async ({ request, locals }) => {
const form = await request.formData();
await api('PATCH', `todos/${locals.userid}/${form.get('uid')}`, {
text: form.has('text') ? form.get('text') : undefined,
done: form.has('done') ? !!form.get('done') : undefined
});
};
/** @type {import('./$types').Action} */
export const DELETE = async ({ request, locals }) => {
const form = await request.formData();
await api('DELETE', `todos/${locals.userid}/${form.get('uid')}`);
};

View File

@@ -0,0 +1,180 @@
<script>
import { enhance } from '$lib/form';
import { scale } from 'svelte/transition';
import { flip } from 'svelte/animate';
/** @type {import('./$types').PageData} */
export let data;
</script>
<svelte:head>
<title>Todos</title>
<meta name="description" content="A todo list app" />
</svelte:head>
<div class="todos">
<h1>Todos</h1>
<form
class="new"
action="/todos"
method="post"
use:enhance={{
result: async ({ form }) => {
form.reset();
}
}}
>
<input name="text" aria-label="Add todo" placeholder="+ tap to add a todo" />
</form>
{#each data.todos as todo (todo.uid)}
<div
class="todo"
class:done={todo.done}
transition:scale|local={{ start: 0.7 }}
animate:flip={{ duration: 200 }}
>
<form
action="/todos?_method=PATCH"
method="post"
use:enhance={{
pending: ({ data }) => {
todo.done = !!data.get('done');
}
}}
>
<input type="hidden" name="uid" value={todo.uid} />
<input type="hidden" name="done" value={todo.done ? '' : 'true'} />
<button class="toggle" aria-label="Mark todo as {todo.done ? 'not done' : 'done'}" />
</form>
<form class="text" action="/todos?_method=PATCH" method="post" use:enhance>
<input type="hidden" name="uid" value={todo.uid} />
<input aria-label="Edit todo" type="text" name="text" value={todo.text} />
<button class="save" aria-label="Save todo" />
</form>
<form
action="/todos?_method=DELETE"
method="post"
use:enhance={{
pending: () => (todo.pending_delete = true)
}}
>
<input type="hidden" name="uid" value={todo.uid} />
<button class="delete" aria-label="Delete todo" disabled={todo.pending_delete} />
</form>
</div>
{/each}
</div>
<style>
.todos {
width: 100%;
max-width: var(--column-width);
margin: var(--column-margin-top) auto 0 auto;
line-height: 1;
}
.new {
margin: 0 0 0.5rem 0;
}
input {
border: 1px solid transparent;
}
input:focus-visible {
box-shadow: inset 1px 1px 6px rgba(0, 0, 0, 0.1);
border: 1px solid #ff3e00 !important;
outline: none;
}
.new input {
font-size: 28px;
width: 100%;
padding: 0.5em 1em 0.3em 1em;
box-sizing: border-box;
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
text-align: center;
}
.todo {
display: grid;
grid-template-columns: 2rem 1fr 2rem;
grid-gap: 0.5rem;
align-items: center;
margin: 0 0 0.5rem 0;
padding: 0.5rem;
background-color: white;
border-radius: 8px;
filter: drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.1));
transform: translate(-1px, -1px);
transition: filter 0.2s, transform 0.2s;
}
.done {
transform: none;
opacity: 0.4;
filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.1));
}
form.text {
position: relative;
display: flex;
align-items: center;
flex: 1;
}
.todo input {
flex: 1;
padding: 0.5em 2em 0.5em 0.8em;
border-radius: 3px;
}
.todo button {
width: 2em;
height: 2em;
border: none;
background-color: transparent;
background-position: 50% 50%;
background-repeat: no-repeat;
}
button.toggle {
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 50%;
box-sizing: border-box;
background-size: 1em auto;
}
.done .toggle {
background-image: url("data:image/svg+xml,%3Csvg width='22' height='16' viewBox='0 0 22 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20.5 1.5L7.4375 14.5L1.5 8.5909' stroke='%23676778' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.delete {
background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4.5 5V22H19.5V5H4.5Z' fill='%23676778' stroke='%23676778' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cpath d='M10 10V16.5' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M14 10V16.5' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M2 5H22' stroke='%23676778' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M8 5L9.6445 2H14.3885L16 5H8Z' fill='%23676778' stroke='%23676778' stroke-width='1.5' stroke-linejoin='round'/%3E%3C/svg%3E%0A");
opacity: 0.2;
}
.delete:hover,
.delete:focus {
transition: opacity 0.2s;
opacity: 1;
}
.save {
position: absolute;
right: 0;
opacity: 0;
background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20.5 2H3.5C2.67158 2 2 2.67157 2 3.5V20.5C2 21.3284 2.67158 22 3.5 22H20.5C21.3284 22 22 21.3284 22 20.5V3.5C22 2.67157 21.3284 2 20.5 2Z' fill='%23676778' stroke='%23676778' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cpath d='M17 2V11H7.5V2H17Z' fill='white' stroke='white' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cpath d='M13.5 5.5V7.5' stroke='%23676778' stroke-width='1.5' stroke-linecap='round'/%3E%3Cpath d='M5.99844 2H18.4992' stroke='%23676778' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E%0A");
}
.todo input:focus + .save,
.save:focus {
transition: opacity 0.2s;
opacity: 1;
}
</style>

View File

@@ -1,9 +1,7 @@
/*
This module is used by the /todos endpoint to
make calls to api.svelte.dev, which stores todos
for each user. The leading underscore indicates that this is
a private module, _not_ an endpoint visiting /todos/_api
will net you a 404 response.
for each user.
(The data on the todo app will expire periodically; no
guarantees are made. Don't use it to organise your life.)

View File

@@ -1,70 +0,0 @@
import { api } from './_api';
/** @type {import('./__types').RequestHandler} */
export const get = async ({ locals }) => {
// locals.userid comes from src/hooks.js
const response = await api('get', `todos/${locals.userid}`);
if (response.status === 404) {
// user hasn't created a todo list.
// start with an empty array
return {
body: {
todos: []
}
};
}
if (response.status === 200) {
return {
body: {
todos: await response.json()
}
};
}
return {
status: response.status
};
};
/** @type {import('./index').RequestHandler} */
export const post = async ({ request, locals }) => {
const form = await request.formData();
await api('post', `todos/${locals.userid}`, {
text: form.get('text')
});
return {};
};
// If the user has JavaScript disabled, the URL will change to
// include the method override unless we redirect back to /todos
const redirect = {
status: 303,
headers: {
location: '/todos'
}
};
/** @type {import('./index').RequestHandler} */
export const patch = async ({ request, locals }) => {
const form = await request.formData();
await api('patch', `todos/${locals.userid}/${form.get('uid')}`, {
text: form.has('text') ? form.get('text') : undefined,
done: form.has('done') ? !!form.get('done') : undefined
});
return redirect;
};
/** @type {import('./index').RequestHandler} */
export const del = async ({ request, locals }) => {
const form = await request.formData();
await api('delete', `todos/${locals.userid}/${form.get('uid')}`);
return redirect;
};

View File

@@ -1,190 +0,0 @@
<script>
import { enhance } from '$lib/form';
import { scale } from 'svelte/transition';
import { flip } from 'svelte/animate';
/**
* @typedef {{
* uid: string;
* created_at: Date;
* text: string;
* done: boolean;
* pending_delete: boolean;
* }} Todo
*/
/** @type {Todo[]} */
export let todos;
</script>
<svelte:head>
<title>Todos</title>
<meta name="description" content="A todo list app" />
</svelte:head>
<div class="todos">
<h1>Todos</h1>
<form
class="new"
action="/todos"
method="post"
use:enhance={{
result: async ({ form }) => {
form.reset();
}
}}
>
<input name="text" aria-label="Add todo" placeholder="+ tap to add a todo" />
</form>
{#each todos as todo (todo.uid)}
<div
class="todo"
class:done={todo.done}
transition:scale|local={{ start: 0.7 }}
animate:flip={{ duration: 200 }}
>
<form
action="/todos?_method=PATCH"
method="post"
use:enhance={{
pending: ({ data }) => {
todo.done = !!data.get('done');
}
}}
>
<input type="hidden" name="uid" value={todo.uid} />
<input type="hidden" name="done" value={todo.done ? '' : 'true'} />
<button class="toggle" aria-label="Mark todo as {todo.done ? 'not done' : 'done'}" />
</form>
<form class="text" action="/todos?_method=PATCH" method="post" use:enhance>
<input type="hidden" name="uid" value={todo.uid} />
<input aria-label="Edit todo" type="text" name="text" value={todo.text} />
<button class="save" aria-label="Save todo" />
</form>
<form
action="/todos?_method=DELETE"
method="post"
use:enhance={{
pending: () => (todo.pending_delete = true)
}}
>
<input type="hidden" name="uid" value={todo.uid} />
<button class="delete" aria-label="Delete todo" disabled={todo.pending_delete} />
</form>
</div>
{/each}
</div>
<style>
.todos {
width: 100%;
max-width: var(--column-width);
margin: var(--column-margin-top) auto 0 auto;
line-height: 1;
}
.new {
margin: 0 0 0.5rem 0;
}
input {
border: 1px solid transparent;
}
input:focus-visible {
box-shadow: inset 1px 1px 6px rgba(0, 0, 0, 0.1);
border: 1px solid #ff3e00 !important;
outline: none;
}
.new input {
font-size: 28px;
width: 100%;
padding: 0.5em 1em 0.3em 1em;
box-sizing: border-box;
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
text-align: center;
}
.todo {
display: grid;
grid-template-columns: 2rem 1fr 2rem;
grid-gap: 0.5rem;
align-items: center;
margin: 0 0 0.5rem 0;
padding: 0.5rem;
background-color: white;
border-radius: 8px;
filter: drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.1));
transform: translate(-1px, -1px);
transition: filter 0.2s, transform 0.2s;
}
.done {
transform: none;
opacity: 0.4;
filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.1));
}
form.text {
position: relative;
display: flex;
align-items: center;
flex: 1;
}
.todo input {
flex: 1;
padding: 0.5em 2em 0.5em 0.8em;
border-radius: 3px;
}
.todo button {
width: 2em;
height: 2em;
border: none;
background-color: transparent;
background-position: 50% 50%;
background-repeat: no-repeat;
}
button.toggle {
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 50%;
box-sizing: border-box;
background-size: 1em auto;
}
.done .toggle {
background-image: url("data:image/svg+xml,%3Csvg width='22' height='16' viewBox='0 0 22 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20.5 1.5L7.4375 14.5L1.5 8.5909' stroke='%23676778' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.delete {
background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4.5 5V22H19.5V5H4.5Z' fill='%23676778' stroke='%23676778' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cpath d='M10 10V16.5' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M14 10V16.5' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M2 5H22' stroke='%23676778' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M8 5L9.6445 2H14.3885L16 5H8Z' fill='%23676778' stroke='%23676778' stroke-width='1.5' stroke-linejoin='round'/%3E%3C/svg%3E%0A");
opacity: 0.2;
}
.delete:hover,
.delete:focus {
transition: opacity 0.2s;
opacity: 1;
}
.save {
position: absolute;
right: 0;
opacity: 0;
background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20.5 2H3.5C2.67158 2 2 2.67157 2 3.5V20.5C2 21.3284 2.67158 22 3.5 22H20.5C21.3284 22 22 21.3284 22 20.5V3.5C22 2.67157 21.3284 2 20.5 2Z' fill='%23676778' stroke='%23676778' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cpath d='M17 2V11H7.5V2H17Z' fill='white' stroke='white' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cpath d='M13.5 5.5V7.5' stroke='%23676778' stroke-width='1.5' stroke-linecap='round'/%3E%3Cpath d='M5.99844 2H18.4992' stroke='%23676778' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E%0A");
}
.todo input:focus + .save,
.save:focus {
transition: opacity 0.2s;
opacity: 1;
}
</style>

View File

@@ -8,11 +8,6 @@ const config = {
// Override http methods in the Todo forms
methodOverride: {
allowed: ['PATCH', 'DELETE']
},
vite: {
define: {
'import.meta.env.VERCEL_ANALYTICS_ID': JSON.stringify(process.env.VERCEL_ANALYTICS_ID)
}
}
}
};

View File

@@ -0,0 +1,11 @@
import { sveltekit } from '@sveltejs/kit/vite';
/** @type {import('vite').UserConfig} */
const config = {
plugins: [sveltekit()],
define: {
'import.meta.env.VERCEL_ANALYTICS_ID': JSON.stringify(process.env.VERCEL_ANALYTICS_ID)
}
};
export default config;

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,10 @@
"packages/*"
],
"nohoist": [
"**/@types/**"
"**/@types/**",
"**/typedoc",
"**/typedoc-plugin-markdown",
"**/typedoc-plugin-mdn-links"
]
},
"dependencies": {

View File

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

View File

@@ -3,5 +3,7 @@ import { getPlatformEnv } from './get-platform-env';
export default function debug(message: string, ...additional: any[]) {
if (getPlatformEnv('BUILDER_DEBUG')) {
console.log(message, ...additional);
} else if (process.env.VERCEL_DEBUG_PREFIX) {
console.log(`${process.env.VERCEL_DEBUG_PREFIX}${message}`, ...additional);
}
}

View File

@@ -23,8 +23,7 @@ export interface ScanParentDirsResult {
*/
cliType: CliType;
/**
* The file path of found `package.json` file, or `undefined` if none was
* found.
* The file path of found `package.json` file, or `undefined` if not found.
*/
packageJsonPath?: string;
/**
@@ -33,8 +32,13 @@ export interface ScanParentDirsResult {
*/
packageJson?: PackageJson;
/**
* The `lockfileVersion` number from the `package-lock.json` file,
* when present.
* The file path of the lockfile (`yarn.lock`, `package-lock.json`, or `pnpm-lock.yaml`)
* or `undefined` if not found.
*/
lockfilePath?: string;
/**
* The `lockfileVersion` number from lockfile (`package-lock.json` or `pnpm-lock.yaml`),
* or `undefined` if not found.
*/
lockfileVersion?: number;
}
@@ -178,25 +182,9 @@ export async function getNodeBinPath({
}: {
cwd: string;
}): Promise<string> {
const { code, stdout, stderr } = await execAsync('npm', ['bin'], {
cwd,
prettyCommand: 'npm bin',
// in some rare cases, we saw `npm bin` exit with a non-0 code, but still
// output the right bin path, so we ignore the exit code
ignoreNon0Exit: true,
});
const nodeBinPath = stdout.trim();
if (path.isAbsolute(nodeBinPath)) {
return nodeBinPath;
}
throw new NowBuildError({
code: `BUILD_UTILS_GET_NODE_BIN_PATH`,
message: `Running \`npm bin\` failed to return a valid bin path (code=${code}, stdout=${stdout}, stderr=${stderr})`,
});
const { lockfilePath } = await scanParentDirs(cwd);
const dir = path.dirname(lockfilePath || cwd);
return path.join(dir, 'node_modules', '.bin');
}
async function chmodPlusX(fsPath: string) {
@@ -319,6 +307,7 @@ export async function scanParentDirs(
start: destPath,
filenames: ['yarn.lock', 'package-lock.json', 'pnpm-lock.yaml'],
});
let lockfilePath: string | undefined;
let lockfileVersion: number | undefined;
let cliType: CliType = 'yarn';
@@ -335,17 +324,25 @@ export async function scanParentDirs(
// Priority order is Yarn > pnpm > npm
if (hasYarnLock) {
cliType = 'yarn';
lockfilePath = yarnLockPath;
} else if (pnpmLockYaml) {
cliType = 'pnpm';
// just ensure that it is read as a number and not a string
lockfilePath = pnpmLockPath;
lockfileVersion = Number(pnpmLockYaml.lockfileVersion);
} else if (packageLockJson) {
cliType = 'npm';
lockfilePath = npmLockPath;
lockfileVersion = packageLockJson.lockfileVersion;
}
const packageJsonPath = pkgJsonPath || undefined;
return { cliType, packageJson, lockfileVersion, packageJsonPath };
return {
cliType,
packageJson,
lockfilePath,
lockfileVersion,
packageJsonPath,
};
}
export async function walkParentDirs({

View File

@@ -41,6 +41,7 @@ export interface Config {
devCommand?: string;
framework?: string | null;
nodeVersion?: string;
middleware?: boolean;
[key: string]: unknown;
}
@@ -335,7 +336,6 @@ export interface ProjectSettings {
directoryListing?: boolean;
gitForkProtection?: boolean;
commandForIgnoringBuildStep?: string | null;
skipGitConnectDuringLink?: boolean;
}
export interface BuilderV2 {

View File

@@ -0,0 +1,46 @@
import { join, parse } from 'path';
import { getNodeBinPath } from '../src';
describe('Test `getNodeBinPath()`', () => {
it('should work with npm7', async () => {
const cwd = join(__dirname, 'fixtures', '20-npm-7');
const result = await getNodeBinPath({ cwd });
expect(result).toBe(join(cwd, 'node_modules', '.bin'));
});
it('should work with yarn', async () => {
const cwd = join(__dirname, 'fixtures', '19-yarn-v2');
const result = await getNodeBinPath({ cwd });
expect(result).toBe(join(cwd, 'node_modules', '.bin'));
});
it('should work with npm 6', async () => {
const cwd = join(__dirname, 'fixtures', '08-yarn-npm/with-npm');
const result = await getNodeBinPath({ cwd });
expect(result).toBe(join(cwd, 'node_modules', '.bin'));
});
it('should work with npm workspaces', async () => {
const cwd = join(__dirname, 'fixtures', '21-npm-workspaces/a');
const result = await getNodeBinPath({ cwd });
expect(result).toBe(join(cwd, '..', 'node_modules', '.bin'));
});
it('should work with pnpm', async () => {
const cwd = join(__dirname, 'fixtures', '22-pnpm');
const result = await getNodeBinPath({ cwd });
expect(result).toBe(join(cwd, 'node_modules', '.bin'));
});
it('should work with pnpm workspaces', async () => {
const cwd = join(__dirname, 'fixtures', '23-pnpm-workspaces/c');
const result = await getNodeBinPath({ cwd });
expect(result).toBe(join(cwd, '..', 'node_modules', '.bin'));
});
it('should fallback to cwd if no lockfile found', async () => {
const cwd = parse(process.cwd()).root;
const result = await getNodeBinPath({ cwd });
expect(result).toBe(join(cwd, 'node_modules', '.bin'));
});
});

View File

@@ -501,6 +501,7 @@ it('should return lockfileVersion 2 with npm7', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('npm');
expect(result.lockfileVersion).toEqual(2);
expect(result.lockfilePath).toEqual(path.join(fixture, 'package-lock.json'));
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});
@@ -509,6 +510,7 @@ it('should not return lockfileVersion with yarn', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('yarn');
expect(result.lockfileVersion).toEqual(undefined);
expect(result.lockfilePath).toEqual(path.join(fixture, 'yarn.lock'));
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});
@@ -517,6 +519,7 @@ it('should return lockfileVersion 1 with older versions of npm', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('npm');
expect(result.lockfileVersion).toEqual(1);
expect(result.lockfilePath).toEqual(path.join(fixture, 'package-lock.json'));
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});
@@ -525,6 +528,9 @@ it('should detect npm Workspaces', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('npm');
expect(result.lockfileVersion).toEqual(2);
expect(result.lockfilePath).toEqual(
path.join(fixture, '..', 'package-lock.json')
);
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});
@@ -533,6 +539,7 @@ it('should detect pnpm without workspace', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('pnpm');
expect(result.lockfileVersion).toEqual(5.3);
expect(result.lockfilePath).toEqual(path.join(fixture, 'pnpm-lock.yaml'));
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});
@@ -541,6 +548,9 @@ it('should detect pnpm with workspaces', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('pnpm');
expect(result.lockfileVersion).toEqual(5.3);
expect(result.lockfilePath).toEqual(
path.join(fixture, '..', 'pnpm-lock.yaml')
);
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});
@@ -552,6 +562,7 @@ it('should detect package.json in nested backend', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('yarn');
expect(result.lockfileVersion).toEqual(undefined);
// There is no lockfile but this test will pick up vercel/vercel/yarn.lock
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});
@@ -563,6 +574,7 @@ it('should detect package.json in nested frontend', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('yarn');
expect(result.lockfileVersion).toEqual(undefined);
// There is no lockfile but this test will pick up vercel/vercel/yarn.lock
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "28.1.1",
"version": "28.2.3",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -41,16 +41,16 @@
"node": ">= 14"
},
"dependencies": {
"@vercel/build-utils": "5.3.1",
"@vercel/go": "2.2.0",
"@vercel/hydrogen": "0.0.14",
"@vercel/next": "3.1.20",
"@vercel/node": "2.5.8",
"@vercel/python": "3.1.10",
"@vercel/redwood": "1.0.18",
"@vercel/remix": "1.0.19",
"@vercel/ruby": "1.3.27",
"@vercel/static-build": "1.0.18",
"@vercel/build-utils": "5.4.2",
"@vercel/go": "2.2.5",
"@vercel/hydrogen": "0.0.18",
"@vercel/next": "3.1.25",
"@vercel/node": "2.5.14",
"@vercel/python": "3.1.14",
"@vercel/redwood": "1.0.23",
"@vercel/remix": "1.0.24",
"@vercel/ruby": "1.3.31",
"@vercel/static-build": "1.0.22",
"update-notifier": "5.1.0"
},
"devDependencies": {
@@ -85,7 +85,6 @@
"@types/node-fetch": "2.5.10",
"@types/npm-package-arg": "6.1.0",
"@types/pluralize": "0.0.29",
"@types/progress": "2.0.3",
"@types/psl": "1.1.0",
"@types/semver": "6.0.1",
"@types/tar-fs": "1.16.1",
@@ -96,9 +95,9 @@
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel/client": "12.2.0",
"@vercel/frameworks": "1.1.3",
"@vercel/fs-detectors": "2.0.5",
"@vercel/client": "12.2.4",
"@vercel/frameworks": "1.1.4",
"@vercel/fs-detectors": "3.0.0",
"@vercel/fun": "1.0.4",
"@vercel/ncc": "0.24.0",
"@zeit/source-map-support": "0.6.2",
@@ -153,7 +152,6 @@
"ora": "3.4.0",
"pcre-to-regexp": "1.0.0",
"pluralize": "7.0.0",
"progress": "2.0.3",
"promisepipe": "3.0.0",
"psl": "1.1.31",
"qr-image": "3.2.0",

View File

@@ -4,7 +4,7 @@ const { statSync } = require('fs');
const pkg = require('../package');
function error(command) {
console.error('> Error!', command);
console.error('> Error:', command);
}
function debug(str) {

View File

@@ -37,6 +37,7 @@ import cliPkg from '../util/pkg';
import readJSONFile from '../util/read-json-file';
import { CantParseJSONFile } from '../util/errors-ts';
import {
pickOverrides,
ProjectLinkAndSettings,
readProjectSettings,
} from '../util/projects/project-settings';
@@ -278,6 +279,11 @@ async function doBuild(
if (pkg instanceof CantParseJSONFile) throw pkg;
if (vercelConfig instanceof CantParseJSONFile) throw vercelConfig;
const projectSettings = {
...project.settings,
...pickOverrides(vercelConfig || {}),
};
// Get a list of source files
const files = (await getFiles(workPath, client)).map(f =>
normalizePath(relative(workPath, f))
@@ -313,7 +319,7 @@ async function doBuild(
// Detect the Vercel Builders that will need to be invoked
const detectedBuilders = await detectBuilders(files, pkg, {
...vercelConfig,
projectSettings: project.settings,
projectSettings,
ignoreBuildScript: true,
featHandleMiss: true,
});
@@ -425,14 +431,14 @@ async function doBuild(
const buildConfig: Config = isZeroConfig
? {
outputDirectory: project.settings.outputDirectory ?? undefined,
outputDirectory: projectSettings.outputDirectory ?? undefined,
...build.config,
projectSettings: project.settings,
installCommand: project.settings.installCommand ?? undefined,
devCommand: project.settings.devCommand ?? undefined,
buildCommand: project.settings.buildCommand ?? undefined,
framework: project.settings.framework,
nodeVersion: project.settings.nodeVersion,
projectSettings,
installCommand: projectSettings.installCommand ?? undefined,
devCommand: projectSettings.devCommand ?? undefined,
buildCommand: projectSettings.buildCommand ?? undefined,
framework: projectSettings.framework,
nodeVersion: projectSettings.nodeVersion,
}
: build.config || {};
const buildOptions: BuildOptions = {

View File

@@ -47,9 +47,7 @@ import {
import { SchemaValidationFailed } from '../../util/errors';
import purchaseDomainIfAvailable from '../../util/domains/purchase-domain-if-available';
import confirm from '../../util/input/confirm';
import editProjectSettings, {
PartialProjectSettings,
} from '../../util/input/edit-project-settings';
import editProjectSettings from '../../util/input/edit-project-settings';
import {
getLinkedProject,
linkFolderToProject,
@@ -73,6 +71,7 @@ import { createGitMeta } from '../../util/create-git-meta';
import { isValidArchive } from '../../util/deploy/validate-archive-format';
import { parseEnv } from '../../util/parse-env';
import { errorToString, isErrnoException, isError } from '../../util/is-error';
import { pickOverrides } from '../../util/projects/project-settings';
export default async (client: Client): Promise<number> => {
const { output } = client;
@@ -509,14 +508,7 @@ export default async (client: Client): Promise<number> => {
let deployStamp = stamp();
let deployment = null;
const localConfigurationOverrides: PartialProjectSettings = {
buildCommand: localConfig?.buildCommand,
devCommand: localConfig?.devCommand,
framework: localConfig?.framework,
commandForIgnoringBuildStep: localConfig?.ignoreCommand,
installCommand: localConfig?.installCommand,
outputDirectory: localConfig?.outputDirectory,
};
const localConfigurationOverrides = pickOverrides(localConfig);
try {
const createArgs: any = {

View File

@@ -6,7 +6,6 @@ import parseListen from '../../util/dev/parse-listen';
import { ProjectEnvVariable } from '../../types';
import Client from '../../util/client';
import { getLinkedProject } from '../../util/projects/link';
import { getFrameworks } from '../../util/get-frameworks';
import { ProjectSettings } from '../../types';
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
import setupAndLink from '../../util/link/setup-and-link';
@@ -31,10 +30,7 @@ export default async function dev(
const listen = parseListen(opts['--listen'] || '3000');
// retrieve dev command
let [link, frameworks] = await Promise.all([
getLinkedProject(client, cwd),
getFrameworks(client),
]);
let link = await getLinkedProject(client, cwd);
if (link.status === 'not_linked' && !process.env.__VERCEL_SKIP_DEV_CMD) {
link = await setupAndLink(client, cwd, {
@@ -60,7 +56,6 @@ export default async function dev(
return link.exitCode;
}
let devCommand: string | undefined;
let projectSettings: ProjectSettings | undefined;
let projectEnvs: ProjectEnvVariable[] = [];
let systemEnvValues: string[] = [];
@@ -70,19 +65,6 @@ export default async function dev(
projectSettings = project;
if (project.devCommand) {
devCommand = project.devCommand;
} else if (project.framework) {
const framework = frameworks.find(f => f.slug === project.framework);
if (framework) {
const defaults = framework.settings.devCommand.value;
if (defaults) {
devCommand = defaults;
}
}
}
if (project.rootDirectory) {
cwd = join(cwd, project.rootDirectory);
}
@@ -95,16 +77,17 @@ export default async function dev(
]);
}
// This is just for tests - can be removed once project settings
// are respected locally in `.vercel/project.json`
if (process.env.VERCEL_DEV_COMMAND) {
devCommand = process.env.VERCEL_DEV_COMMAND;
}
const devServer = new DevServer(cwd, {
output,
projectSettings,
projectEnvs,
systemEnvValues,
});
// If there is no Development Command, we must delete the
// v3 Build Output because it will incorrectly be detected by
// @vercel/static-build in BuildOutputV3.getBuildOutputDirectory()
if (!devCommand) {
if (!devServer.devCommand) {
const outputDir = join(cwd, OUTPUT_DIR);
if (await fs.pathExists(outputDir)) {
output.log(`Removing ${OUTPUT_DIR}`);
@@ -112,13 +95,5 @@ export default async function dev(
}
}
const devServer = new DevServer(cwd, {
output,
devCommand,
projectSettings,
projectEnvs,
systemEnvValues,
});
await devServer.start(...listen);
}

View File

@@ -25,7 +25,7 @@ const help = () => {
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN'
)} Login token
-y, --yes Skip questions when setting up new project using default scope and settings
-y, --yes Skip confirmation when connecting a Git provider
${chalk.dim('Examples:')}
@@ -35,7 +35,9 @@ const help = () => {
${chalk.cyan(`$ ${getPkgName()} git connect`)}
${chalk.gray('')} Connect your Vercel Project to a Git repository using the remote URL
${chalk.gray(
''
)} Connect your Vercel Project to a Git repository using the remote URL
${chalk.cyan(
`$ ${getPkgName()} git connect https://github.com/user/repo.git`

View File

@@ -7,7 +7,7 @@ try {
process.cwd();
} catch (err: unknown) {
if (isError(err) && err.message.includes('uv_cwd')) {
console.error('Error! The current working directory does not exist.');
console.error('Error: The current working directory does not exist.');
process.exit(1);
}
}

View File

@@ -1,5 +1,4 @@
import bytes from 'bytes';
import Progress from 'progress';
import chalk from 'chalk';
import {
ArchiveFormat,
@@ -8,6 +7,7 @@ import {
VercelClientOptions,
} from '@vercel/client';
import { Output } from '../output';
import { progress } from '../output/progress';
import Now from '../../util';
import { Org } from '../../types';
import ua from '../ua';
@@ -68,7 +68,6 @@ export default async function processDeployment({
} = args;
const { debug } = output;
let bar: Progress | null = null;
const { env = {} } = requestBody;
@@ -114,32 +113,28 @@ export default async function processDeployment({
const missingSize = missing
.map((sha: string) => total.get(sha).data.length)
.reduce((a: number, b: number) => a + b, 0);
output.stopSpinner();
bar = new Progress(`${chalk.gray('>')} Upload [:bar] :percent :etas`, {
width: 20,
complete: '=',
incomplete: '',
total: missingSize,
clear: true,
});
bar.tick(0);
const totalSizeHuman = bytes.format(missingSize, { decimalPlaces: 1 });
uploads.forEach((e: any) =>
e.on('progress', () => {
if (!bar) return;
const totalBytesUploaded = uploads.reduce((acc: number, e: any) => {
const uploadedBytes = uploads.reduce((acc: number, e: any) => {
return acc + e.bytesUploaded;
}, 0);
// set the current progress bar value
bar.curr = totalBytesUploaded;
// trigger rendering
bar.tick(0);
if (bar.complete) {
const bar = progress(uploadedBytes, missingSize);
if (!bar || uploadedBytes === missingSize) {
output.spinner(deployingSpinnerVal, 0);
} else {
const uploadedHuman = bytes.format(uploadedBytes, {
decimalPlaces: 1,
fixedDecimals: true,
});
output.spinner(
`Uploading ${chalk.reset(
`[${bar}] (${uploadedHuman}/${totalSizeHuman})`
)}`,
0
);
}
})
);
@@ -154,10 +149,6 @@ export default async function processDeployment({
}
if (event.type === 'created') {
if (bar && !bar.complete) {
bar.tick(bar.total + 1);
}
await linkFolderToProject(
output,
cwd || paths[0],

View File

@@ -13,6 +13,7 @@ import {
Lambda,
FileBlob,
FileFsRef,
normalizePath,
} from '@vercel/build-utils';
import { isOfficialRuntime } from '@vercel/fs-detectors';
import plural from 'pluralize';
@@ -269,7 +270,9 @@ export async function executeBuild(
const { cleanUrls } = vercelConfig;
// Mimic fmeta-util and perform file renaming
Object.entries(output).forEach(([path, value]) => {
for (const [originalPath, value] of Object.entries(output)) {
let path = normalizePath(originalPath);
if (cleanUrls && path.endsWith('.html')) {
path = path.slice(0, -5);
@@ -284,7 +287,7 @@ export async function executeBuild(
}
output[path] = value;
});
}
// Convert the JSON-ified output map back into their corresponding `File`
// subclass type instances.

View File

@@ -101,6 +101,7 @@ import {
isError,
isSpawnError,
} from '../is-error';
import { pickOverrides } from '../projects/project-settings';
const frontendRuntimeSet = new Set(
frameworkList.map(f => f.useRuntime?.use || '@vercel/static-build')
@@ -136,6 +137,7 @@ export default class DevServer {
public address: string;
public devCacheDir: string;
private currentDevCommand?: string;
private caseSensitive: boolean;
private apiDir: string | null;
private apiExtensions: Set<string>;
@@ -149,10 +151,10 @@ export default class DevServer {
private watchAggregationTimeout: number;
private filter: (path: string) => boolean;
private podId: string;
private devCommand?: string;
private devProcess?: ChildProcess;
private devProcessPort?: number;
private devServerPids: Set<number>;
private originalProjectSettings?: ProjectSettings;
private projectSettings?: ProjectSettings;
private vercelConfigWarning: boolean;
@@ -173,7 +175,7 @@ export default class DevServer {
this.projectEnvs = options.projectEnvs || [];
this.files = {};
this.address = '';
this.devCommand = options.devCommand;
this.originalProjectSettings = options.projectSettings;
this.projectSettings = options.projectSettings;
this.caseSensitive = false;
this.apiDir = null;
@@ -549,6 +551,23 @@ export default class DevServer {
return this.getVercelConfigPromise;
}
get devCommand() {
if (this.projectSettings?.devCommand) {
return this.projectSettings.devCommand;
} else if (this.projectSettings?.framework) {
const frameworkSlug = this.projectSettings.framework;
const framework = frameworkList.find(f => f.slug === frameworkSlug);
if (framework) {
const defaults = framework.settings.devCommand.value;
if (defaults) {
return defaults;
}
}
}
return undefined;
}
async _getVercelConfig(): Promise<VercelConfig> {
const configPath = getVercelConfigPath(this.cwd);
@@ -563,6 +582,12 @@ export default class DevServer {
]);
await this.validateVercelConfig(vercelConfig);
this.projectSettings = {
...this.originalProjectSettings,
...pickOverrides(vercelConfig),
};
const { error: routeError, routes: maybeRoutes } =
getTransformedRoutes(vercelConfig);
if (routeError) {
@@ -703,6 +728,11 @@ export default class DevServer {
}
this.envConfigs = { buildEnv, runEnv, allEnv };
// If the `devCommand` was modified via project settings
// overrides then the dev process needs to be restarted
await this.runDevCommand();
return vercelConfig;
}
@@ -1856,7 +1886,9 @@ export default class DevServer {
devCacheDir,
env: {
...envConfigs.runEnv,
VERCEL_BUILDER_DEBUG: this.output.debugEnabled ? '1' : undefined,
VERCEL_DEBUG_PREFIX: this.output.debugEnabled
? '[builder]'
: undefined,
},
buildEnv: { ...envConfigs.buildEnv },
},
@@ -2203,10 +2235,21 @@ export default class DevServer {
async runDevCommand() {
const { devCommand, cwd } = this;
if (devCommand === this.currentDevCommand) {
// `devCommand` has not changed, so don't restart frontend dev process
return;
}
this.currentDevCommand = devCommand;
if (!devCommand) {
return;
}
if (this.devProcess) {
await treeKill(this.devProcess.pid);
}
this.output.log(
`Running Dev Command ${chalk.cyan.bold(`${devCommand}`)}`
);

View File

@@ -23,7 +23,6 @@ export { VercelConfig };
export interface DevServerOptions {
output: Output;
devCommand?: string;
projectSettings?: ProjectSettings;
systemEnvValues?: string[];
projectEnvs?: ProjectEnvVariable[];

View File

@@ -58,9 +58,9 @@ export async function connectGitProvider(
(err.action === 'Install GitHub App' || err.code === 'repo_not_found')
) {
client.output.error(
`Failed to link ${chalk.cyan(
`Failed to connect ${chalk.cyan(
repo
)}. Make sure there aren't any typos and that you have access to the repository if it's private.`
)} to project. Make sure there aren't any typos and that you have access to the repository if it's private.`
);
} else if (apiError && err.action === 'Add a Login Connection') {
client.output.error(

View File

@@ -1,127 +0,0 @@
import { Dictionary } from '@vercel/client';
import { parseRepoUrl } from '../git/connect-git-provider';
import Client from '../client';
import { Org, Project, ProjectSettings } from '../../types';
import { handleOptions } from './handle-options';
import {
promptGitConnectMultipleUrls,
promptGitConnectSingleUrl,
} from './git-connect-prompts';
function getProjectSettings(project: Project): ProjectSettings {
return {
createdAt: project.createdAt,
framework: project.framework,
devCommand: project.devCommand,
installCommand: project.installCommand,
buildCommand: project.buildCommand,
outputDirectory: project.outputDirectory,
rootDirectory: project.rootDirectory,
directoryListing: project.directoryListing,
nodeVersion: project.nodeVersion,
skipGitConnectDuringLink: project.skipGitConnectDuringLink,
};
}
export async function addGitConnection(
client: Client,
org: Org,
project: Project,
remoteUrls: Dictionary<string>,
autoConfirm: Boolean,
settings?: ProjectSettings
): Promise<number | void> {
if (!settings) {
settings = getProjectSettings(project);
}
if (Object.keys(remoteUrls).length === 1) {
return addSingleGitRemote(
client,
org,
project,
remoteUrls,
settings || project,
autoConfirm
);
} else if (Object.keys(remoteUrls).length > 1 && !project.link) {
return addMultipleGitRemotes(
client,
org,
project,
remoteUrls,
settings || project,
autoConfirm
);
}
}
async function addSingleGitRemote(
client: Client,
org: Org,
project: Project,
remoteUrls: Dictionary<string>,
settings: ProjectSettings,
autoConfirm: Boolean
) {
const [remoteName, remoteUrl] = Object.entries(remoteUrls)[0];
const repoInfo = parseRepoUrl(remoteUrl);
if (!repoInfo) {
client.output.debug(`Could not parse repo url ${repoInfo}.`);
return 1;
}
const { org: parsedOrg, repo, provider } = repoInfo;
const alreadyLinked =
project.link &&
project.link.org === parsedOrg &&
project.link.repo === repo &&
project.link.type === provider;
if (alreadyLinked) {
client.output.debug('Project already linked. Skipping...');
return;
}
const replace =
project.link &&
(project.link.org !== parsedOrg ||
project.link.repo !== repo ||
project.link.type !== provider);
let shouldConnectOption: string | undefined;
if (autoConfirm) {
shouldConnectOption = 'yes';
} else {
shouldConnectOption = await promptGitConnectSingleUrl(
client,
project,
remoteName,
remoteUrl,
replace
);
}
return handleOptions(
shouldConnectOption,
client,
org,
project,
settings,
repoInfo
);
}
async function addMultipleGitRemotes(
client: Client,
org: Org,
project: Project,
remoteUrls: Dictionary<string>,
settings: ProjectSettings,
autoConfirm: Boolean
) {
let remoteUrl: string | undefined;
if (autoConfirm) {
remoteUrl = remoteUrls['origin'] || Object.values(remoteUrls)[0];
} else {
client.output.log('Found multiple Git remote URLs in Git config.');
remoteUrl = await promptGitConnectMultipleUrls(client, remoteUrls);
}
return handleOptions(remoteUrl, client, org, project, settings);
}

View File

@@ -1,86 +0,0 @@
import { Dictionary } from '@vercel/client';
import chalk from 'chalk';
import { Project } from '../../types';
import Client from '../client';
import { formatProvider } from '../git/connect-git-provider';
import list from '../input/list';
export async function promptGitConnectSingleUrl(
client: Client,
project: Project,
remoteName: string,
remoteUrl: string,
hasDiffConnectedProvider = false
) {
const { output } = client;
if (hasDiffConnectedProvider) {
const currentRepoPath = `${project.link!.org}/${project.link!.repo}`;
const currentProvider = project.link!.type;
output.print('\n');
output.log(
`Found Git remote URL ${chalk.cyan(
remoteUrl
)}, which is different from the connected ${formatProvider(
currentProvider
)} repository ${chalk.cyan(currentRepoPath)}.`
);
} else {
output.print('\n');
output.log(
`Found local Git remote "${remoteName}": ${chalk.cyan(remoteUrl)}`
);
}
return await list(client, {
message: hasDiffConnectedProvider
? 'Do you want to replace it?'
: `Do you want to connect "${remoteName}" to your Vercel project?`,
choices: [
{
name: 'Yes',
value: 'yes',
short: 'yes',
},
{
name: 'No',
value: 'no',
short: 'no',
},
{
name: 'Do not ask again for this project',
value: 'opt-out',
short: 'no (opt out)',
},
],
});
}
export async function promptGitConnectMultipleUrls(
client: Client,
remoteUrls: Dictionary<string>
) {
const staticOptions = [
{
name: 'No',
value: 'no',
short: 'no',
},
{
name: 'Do not ask again for this project',
value: 'opt-out',
short: 'no (opt out)',
},
];
let choices = [];
for (const url of Object.values(remoteUrls)) {
choices.push({
name: url,
value: url,
short: url,
});
}
choices = choices.concat(staticOptions);
return await list(client, {
message: 'Do you want to connect a Git repository to your Vercel project?',
choices,
});
}

View File

@@ -1,98 +0,0 @@
import chalk from 'chalk';
import { Org, Project, ProjectSettings } from '../../types';
import Client from '../client';
import {
connectGitProvider,
disconnectGitProvider,
formatProvider,
RepoInfo,
parseRepoUrl,
} from '../git/connect-git-provider';
import { Output } from '../output';
import { getCommandName } from '../pkg-name';
import updateProject from '../projects/update-project';
export async function handleOptions(
option: string,
client: Client,
org: Org,
project: Project,
settings: ProjectSettings,
repoInfo?: RepoInfo
) {
const { output } = client;
if (option === 'no') {
skip(output);
return;
} else if (option === 'opt-out') {
optOut(client, project, settings);
return;
} else if (option !== '') {
// Option is "yes" or a URL
// Ensure parsed url exists
if (!repoInfo) {
const _repoInfo = parseRepoUrl(option);
if (!_repoInfo) {
output.debug(`Could not parse repo url ${option}.`);
return 1;
}
repoInfo = _repoInfo;
}
return connect(client, org, project, repoInfo);
}
}
async function optOut(
client: Client,
project: Project,
settings: ProjectSettings
) {
settings.skipGitConnectDuringLink = true;
await updateProject(client, project.name, settings);
client.output
.log(`Opted out. You can re-enable this prompt by visiting the Settings > Git page on the
dashboard for this Project.`);
}
function skip(output: Output) {
output.log('Skipping...');
output.log(
`You can connect a Git repository in the future by running ${getCommandName(
'git connect'
)}.`
);
}
async function connect(
client: Client,
org: Org,
project: Project,
repoInfo: RepoInfo
): Promise<number | void> {
const { output } = client;
const { provider, org: parsedOrg, repo } = repoInfo;
const repoPath = `${parsedOrg}/${repo}`;
output.log('Connecting...');
if (project.link) {
await disconnectGitProvider(client, org, project.id);
}
const connect = await connectGitProvider(
client,
org,
project.id,
provider,
repoPath
);
if (connect !== 1) {
output.log(
`Connected ${formatProvider(provider)} repository ${chalk.cyan(
repoPath
)}!`
);
} else {
return connect;
}
}

View File

@@ -28,8 +28,6 @@ import { EmojiLabel } from '../emoji';
import createDeploy from '../deploy/create-deploy';
import Now, { CreateOptions } from '../index';
import { isAPIError } from '../errors-ts';
import { getRemoteUrls } from '../create-git-meta';
import { addGitConnection } from './add-git-connection';
export interface SetupAndLinkOptions {
forceDelete?: boolean;
@@ -130,20 +128,6 @@ export default async function setupAndLink(
} else {
const project = projectOrNewProjectName;
const remoteUrls = await getRemoteUrls(join(path, '.git/config'), output);
if (remoteUrls && !project.skipGitConnectDuringLink) {
const connectGit = await addGitConnection(
client,
org,
project,
remoteUrls,
autoConfirm
);
if (typeof connectGit === 'number') {
return { status: 'error', exitCode: connectGit };
}
}
await linkFolderToProject(
output,
path,
@@ -258,21 +242,6 @@ export default async function setupAndLink(
const project = await createProject(client, newProjectName);
const remoteUrls = await getRemoteUrls(join(path, '.git/config'), output);
if (remoteUrls) {
const connectGit = await addGitConnection(
client,
org,
project,
remoteUrls,
autoConfirm,
settings
);
if (typeof connectGit === 'number') {
return { status: 'error', exitCode: connectGit };
}
}
await updateProject(client, project.id, settings);
Object.assign(project, settings);

View File

@@ -73,7 +73,7 @@ export class Output {
link?: string,
action = 'Learn More'
) => {
this.print(`${chalk.red(`Error!`)} ${str}\n`);
this.print(`${chalk.red(`Error:`)} ${str}\n`);
const details = slug ? `https://err.sh/vercel/${slug}` : link;
if (details) {
this.print(`${chalk.bold(action)}: ${renderLink(details)}\n`);

View File

@@ -23,5 +23,5 @@ export default function error(
metric.exception(messages.join('\n')).send();
}
return `${chalk.red('Error!')} ${messages.join('\n')}`;
return `${chalk.red('Error:')} ${messages.join('\n')}`;
}

View File

@@ -0,0 +1,23 @@
export interface ProgressOptions {
width?: number;
complete?: string;
incomplete?: string;
}
/**
* Returns a raw progress bar string.
*/
export function progress(
current: number,
total: number,
opts: ProgressOptions = {}
): string | null {
const { width = 20, complete = '=', incomplete = '-' } = opts;
if (total <= 0 || current < 0 || current > total) {
// Let the caller decide how to handle out-of-range values
return null;
}
const unit = total / width;
const pos = Math.floor(current / unit);
return `${complete.repeat(pos)}${incomplete.repeat(width - pos)}`;
}

View File

@@ -2,6 +2,8 @@ import { outputJSON } from 'fs-extra';
import { Org, Project, ProjectLink } from '../../types';
import { getLinkFromDir, VERCEL_DIR, VERCEL_DIR_PROJECT } from './link';
import { join } from 'path';
import { VercelConfig } from '@vercel/client';
import { PartialProjectSettings } from '../input/edit-project-settings';
export type ProjectLinkAndSettings = ProjectLink & {
settings: {
@@ -51,3 +53,26 @@ export async function writeProjectSettings(
export async function readProjectSettings(cwd: string) {
return await getLinkFromDir<ProjectLinkAndSettings>(cwd);
}
export function pickOverrides(
vercelConfig: VercelConfig
): PartialProjectSettings {
const overrides: PartialProjectSettings = {};
for (const prop of [
'buildCommand',
'devCommand',
'framework',
'ignoreCommand',
'installCommand',
'outputDirectory',
] as const) {
if (typeof vercelConfig[prop] !== 'undefined') {
if (prop === 'ignoreCommand') {
overrides.commandForIgnoringBuildStep = vercelConfig[prop];
} else {
overrides[prop] = vercelConfig[prop];
}
}
}
return overrides;
}

View File

@@ -22,28 +22,28 @@ export async function validateRootDirectory(
const suffix = errorSuffix ? ` ${errorSuffix}` : '';
if (!pathStat) {
output.print(
`${chalk.red('Error!')} The provided path ${chalk.cyan(
output.error(
`The provided path ${chalk.cyan(
`${toHumanPath(path)}`
)} does not exist.${suffix}\n`
)} does not exist.${suffix}`
);
return false;
}
if (!pathStat.isDirectory()) {
output.print(
`${chalk.red('Error!')} The provided path ${chalk.cyan(
output.error(
`The provided path ${chalk.cyan(
`${toHumanPath(path)}`
)} is a file, but expected a directory.${suffix}\n`
)} is a file, but expected a directory.${suffix}`
);
return false;
}
if (!path.startsWith(cwd)) {
output.print(
`${chalk.red('Error!')} The provided path ${chalk.cyan(
output.error(
`The provided path ${chalk.cyan(
`${toHumanPath(path)}`
)} is outside of the project.${suffix}\n`
)} is outside of the project.${suffix}`
);
return false;
}
@@ -59,7 +59,7 @@ export default async function validatePaths(
// can't deploy more than 1 path
if (paths.length > 1) {
output.print(`${chalk.red('Error!')} Can't deploy more than one path.\n`);
output.error(`Can't deploy more than one path.`);
return { valid: false, exitCode: 1 };
}
@@ -69,11 +69,7 @@ export default async function validatePaths(
const pathStat = await stat(path).catch(() => null);
if (!pathStat) {
output.print(
`${chalk.red('Error!')} Could not find ${chalk.cyan(
`${toHumanPath(path)}`
)}\n`
);
output.error(`Could not find ${chalk.cyan(`${toHumanPath(path)}`)}`);
return { valid: false, exitCode: 1 };
}

View File

@@ -1,5 +1,5 @@
{
"name": "nextjs",
"name": "nextjs-node",
"license": "MIT",
"scripts": {
"dev": "next",

View File

@@ -1,5 +1,5 @@
{
"name": "nextjs",
"name": "nextjs-src-dir",
"license": "MIT",
"scripts": {
"dev": "next",

View File

@@ -0,0 +1,3 @@
{
"devCommand": "next dev --port $PORT"
}

View File

@@ -5,5 +5,6 @@
"src": "/test",
"dest": "/?route-param=b"
}
]
],
"devCommand": "next dev --port $PORT"
}

View File

@@ -0,0 +1,13 @@
import mod from '../increment.wasm?module';
export const config = { runtime: 'experimental-edge' };
const init$ = WebAssembly.instantiate(mod);
/** @param {Request} req */
export default async req => {
const givenNumber = Number(new URL(req.url).searchParams.get('number') || 0);
const { exports } = await init$;
const added = exports.add_one(givenNumber);
return new Response(`${givenNumber} + 1 = ${added}`);
};

Binary file not shown.

View File

@@ -0,0 +1 @@
This is the original

View File

@@ -0,0 +1 @@
This is the overridden!

View File

@@ -0,0 +1,8 @@
{
"scripts": {
"build": "not used - required for zero-config"
},
"dependencies": {
"serve": "14.0.1"
}
}

View File

@@ -0,0 +1 @@
{"devCommand":"serve -p $PORT original"}

View File

@@ -0,0 +1,588 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@zeit/schemas@2.21.0":
version "2.21.0"
resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.21.0.tgz#cd242c6551ffb51830049d68d9743ab65b45b820"
integrity sha512-/J4WBTpWtQ4itN1rb3ao8LfClmVcmz2pO6oYb7Qd4h7VSqUhIbJIvrykz9Ew1WMg6eFWsKdsMHc5uPbFxqlCpg==
accepts@~1.3.5:
version "1.3.8"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
dependencies:
mime-types "~2.1.34"
negotiator "0.6.3"
ajv@8.11.0:
version "8.11.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f"
integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==
dependencies:
fast-deep-equal "^3.1.1"
json-schema-traverse "^1.0.0"
require-from-string "^2.0.2"
uri-js "^4.2.2"
ansi-align@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59"
integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==
dependencies:
string-width "^4.1.0"
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-regex@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a"
integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==
ansi-styles@^4.1.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"
ansi-styles@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.1.0.tgz#87313c102b8118abd57371afab34618bf7350ed3"
integrity sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==
arch@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11"
integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==
arg@5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
boxen@7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/boxen/-/boxen-7.0.0.tgz#9e5f8c26e716793fc96edcf7cf754cdf5e3fbf32"
integrity sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==
dependencies:
ansi-align "^3.0.1"
camelcase "^7.0.0"
chalk "^5.0.1"
cli-boxes "^3.0.0"
string-width "^5.1.2"
type-fest "^2.13.0"
widest-line "^4.0.1"
wrap-ansi "^8.0.1"
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
bytes@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==
camelcase@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-7.0.0.tgz#fd112621b212126741f998d614cbc2a8623fd174"
integrity sha512-JToIvOmz6nhGsUhAYScbo2d6Py5wojjNfoxoc2mEVLUdJ70gJK2gnd+ABY1Tc3sVMyK7QDPtN0T/XdlCQWITyQ==
chalk-template@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/chalk-template/-/chalk-template-0.4.0.tgz#692c034d0ed62436b9062c1707fadcd0f753204b"
integrity sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==
dependencies:
chalk "^4.1.2"
chalk@5.0.1, chalk@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6"
integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==
chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
cli-boxes@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145"
integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==
clipboardy@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-3.0.0.tgz#f3876247404d334c9ed01b6f269c11d09a5e3092"
integrity sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==
dependencies:
arch "^2.2.0"
execa "^5.1.1"
is-wsl "^2.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==
compressible@~2.0.16:
version "2.0.18"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==
dependencies:
mime-db ">= 1.43.0 < 2"
compression@1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
dependencies:
accepts "~1.3.5"
bytes "3.0.0"
compressible "~2.0.16"
debug "2.6.9"
on-headers "~1.0.2"
safe-buffer "5.1.2"
vary "~1.1.2"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
content-disposition@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==
cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
dependencies:
path-key "^3.1.0"
shebang-command "^2.0.0"
which "^2.0.1"
debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
deep-extend@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
eastasianwidth@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
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==
emoji-regex@^9.2.2:
version "9.2.2"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
execa@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
dependencies:
cross-spawn "^7.0.3"
get-stream "^6.0.0"
human-signals "^2.1.0"
is-stream "^2.0.0"
merge-stream "^2.0.0"
npm-run-path "^4.0.1"
onetime "^5.1.2"
signal-exit "^3.0.3"
strip-final-newline "^2.0.0"
fast-deep-equal@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-url-parser@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d"
integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==
dependencies:
punycode "^1.3.2"
get-stream@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
has-flag@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
human-signals@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
ini@~1.3.0:
version "1.3.8"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
is-docker@^2.0.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
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==
is-port-reachable@4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/is-port-reachable/-/is-port-reachable-4.0.0.tgz#dac044091ef15319c8ab2f34604d8794181f8c2d"
integrity sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==
is-stream@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
is-wsl@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
dependencies:
is-docker "^2.0.0"
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
json-schema-traverse@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-db@~1.33.0:
version "1.33.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db"
integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==
mime-types@2.1.18:
version "2.1.18"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8"
integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==
dependencies:
mime-db "~1.33.0"
mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mimic-fn@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
minimatch@3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
brace-expansion "^1.1.7"
minimist@^1.2.0:
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
negotiator@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
npm-run-path@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
dependencies:
path-key "^3.0.0"
on-headers@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
onetime@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
dependencies:
mimic-fn "^2.1.0"
path-is-inside@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==
path-key@^3.0.0, path-key@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
path-to-regexp@2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45"
integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==
punycode@^1.3.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==
punycode@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
range-parser@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==
rc@^1.0.1, rc@^1.1.6:
version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
dependencies:
deep-extend "^0.6.0"
ini "~1.3.0"
minimist "^1.2.0"
strip-json-comments "~2.0.1"
registry-auth-token@3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20"
integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==
dependencies:
rc "^1.1.6"
safe-buffer "^5.0.1"
registry-url@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942"
integrity sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==
dependencies:
rc "^1.0.1"
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
safe-buffer@5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@^5.0.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
serve-handler@6.1.3:
version "6.1.3"
resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.3.tgz#1bf8c5ae138712af55c758477533b9117f6435e8"
integrity sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==
dependencies:
bytes "3.0.0"
content-disposition "0.5.2"
fast-url-parser "1.1.3"
mime-types "2.1.18"
minimatch "3.0.4"
path-is-inside "1.0.2"
path-to-regexp "2.2.1"
range-parser "1.2.0"
serve@14.0.1:
version "14.0.1"
resolved "https://registry.yarnpkg.com/serve/-/serve-14.0.1.tgz#5b6ffc17e18e1a8e963cc392958d7df13e1ef9fd"
integrity sha512-tNGwxl27FwA8TbmMQqN0jTaSx8/trL532qZsJHX1VdiEIjjtMJHCs7AFS6OvtC7cTHOvmjXqt5yczejU6CV2Xg==
dependencies:
"@zeit/schemas" "2.21.0"
ajv "8.11.0"
arg "5.0.2"
boxen "7.0.0"
chalk "5.0.1"
chalk-template "0.4.0"
clipboardy "3.0.0"
compression "1.7.4"
is-port-reachable "4.0.0"
serve-handler "6.1.3"
update-check "1.5.4"
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
dependencies:
shebang-regex "^3.0.0"
shebang-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
signal-exit@^3.0.3:
version "3.0.7"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
string-width@^4.1.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@^5.0.1, string-width@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
dependencies:
eastasianwidth "^0.2.0"
emoji-regex "^9.2.2"
strip-ansi "^7.0.1"
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-ansi@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==
dependencies:
ansi-regex "^6.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==
strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
type-fest@^2.13.0:
version "2.19.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b"
integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==
update-check@1.5.4:
version "1.5.4"
resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.4.tgz#5b508e259558f1ad7dbc8b4b0457d4c9d28c8743"
integrity sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==
dependencies:
registry-auth-token "3.3.2"
registry-url "3.1.0"
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
dependencies:
punycode "^2.1.0"
vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
which@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
dependencies:
isexe "^2.0.0"
widest-line@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-4.0.1.tgz#a0fc673aaba1ea6f0a0d35b3c2795c9a9cc2ebf2"
integrity sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==
dependencies:
string-width "^5.0.1"
wrap-ansi@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.0.1.tgz#2101e861777fec527d0ea90c57c6b03aac56a5b3"
integrity sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==
dependencies:
ansi-styles "^6.1.0"
string-width "^5.0.1"
strip-ansi "^7.0.1"

View File

@@ -53,6 +53,33 @@ test('[vercel dev] should support edge functions', async () => {
}
});
test('[vercel dev] edge functions support WebAssembly files', async () => {
const dir = fixture('edge-function');
const { dev, port, readyResolver } = await testFixture(dir, {
env: {
ENV_VAR_IN_EDGE: '1',
},
});
try {
await readyResolver;
for (const { number, result } of [
{ number: 1, result: 2 },
{ number: 2, result: 3 },
{ number: 12, result: 13 },
]) {
let res = await fetch(
`http://localhost:${port}/api/webassembly?number=${number}`
);
validateResponseHeaders(res);
await expect(res.text()).resolves.toEqual(`${number} + 1 = ${result}`);
}
} finally {
await dev.kill('SIGTERM');
}
});
test(
'[vercel dev] edge functions respond properly the same as production',
testFixtureStdio('edge-function', async (testPath: any) => {
@@ -422,11 +449,7 @@ test('[vercel dev] should maintain query when proxy passing', async () => {
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',
},
});
const { dev, port, readyResolver } = await testFixture(dir);
try {
await readyResolver;
@@ -489,11 +512,7 @@ test('[vercel dev] should send `etag` header for static files', async () => {
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',
},
});
const { dev, port, readyResolver } = await testFixture(dir);
try {
await readyResolver;

View File

@@ -445,11 +445,7 @@ test(
test(
'[vercel dev] Middleware that has no response',
testFixtureStdio('middleware-no-response', async (testPath: any) => {
await testPath(
500,
'/api/hello',
'A server error has occurred\n\nEDGE_FUNCTION_INVOCATION_FAILED'
);
await testPath(200, '/api/hello', 'hello from a serverless function');
})
);
@@ -539,3 +535,40 @@ test(
);
})
);
test(
'[vercel dev] restarts dev process when `devCommand` setting is modified',
testFixtureStdio(
'project-settings-override',
async (_testPath: any, port: any) => {
const directory = fixture('project-settings-override');
const vercelJsonPath = join(directory, 'vercel.json');
const originalVercelJson = await fs.readJSON(vercelJsonPath);
try {
const originalResponse = await fetch(
`http://localhost:${port}/index.txt`
);
validateResponseHeaders(originalResponse);
const body = await originalResponse.text();
expect(body.trim()).toEqual('This is the original');
expect(originalResponse.status).toBe(200);
await fs.writeJSON(vercelJsonPath, {
devCommand: 'serve -p $PORT overridden',
});
const overriddenResponse = await fetch(
`http://localhost:${port}/index.txt`
);
validateResponseHeaders(overriddenResponse);
const body2 = await overriddenResponse.text();
expect(body2.trim()).toEqual('This is the overridden!');
expect(overriddenResponse.status).toBe(200);
} finally {
await fs.writeJSON(vercelJsonPath, originalVercelJson);
}
},
{ skipDeploy: true }
)
);

View File

@@ -13,6 +13,7 @@ const {
jest.setTimeout(6 * 60 * 1000);
const isCI = !!process.env.CI;
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const isCanary = () => getDistTag(cliVersion) === 'canary';
@@ -52,7 +53,7 @@ function fetchWithRetry(url, opts = {}) {
return res;
},
{
retries: opts.retries || 3,
retries: opts.retries ?? 3,
factor: 1,
}
);
@@ -150,9 +151,9 @@ async function testPath(
fetchOpts = {}
) {
const opts = {
retries: isCI ? 5 : 0,
...fetchOpts,
redirect: 'manual-dont-change',
retries: 5,
status,
};
const url = `${origin}${path}`;
@@ -330,7 +331,7 @@ function testFixtureStdio(
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(projectSettings),
retries: 3,
retries: isCI ? 3 : 0,
status: 200,
}
);

View File

@@ -1,5 +1,5 @@
{
"name": "only-module",
"name": "prefer-browser",
"private": true,
"main": "dist-main.js",
"module": "dist-module.js",

View File

@@ -0,0 +1 @@
output

View File

@@ -0,0 +1,9 @@
{
"orgId": ".",
"projectId": ".",
"settings": {
"buildCommand": "mkdir -p output && echo 2 > output/index.txt",
"outputDirectory": "output",
"framework": null
}
}

View File

@@ -0,0 +1,5 @@
{
"scripts": {
"build": "mkdir -p output && echo 1 > output/index.txt"
}
}

View File

@@ -0,0 +1,3 @@
{
"buildCommand": "mkdir -p output && echo 3 > output/index.txt"
}

View File

@@ -1,16 +0,0 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "secondary"]
url = https://github.com/user2/repo2.git
fetch = +refs/heads/*:refs/remotes/secondary/*
[remote "origin"]
url = https://github.com/user/repo.git
fetch = +refs/heads/*:refs/remotes/origin/*
[remote "gitlab"]
url = https://gitlab.com/user/repo.git
fetch = +refs/heads/*:refs/remotes/gitlab/*

View File

@@ -1,2 +0,0 @@
!.vercel
.vercel

View File

@@ -1,16 +0,0 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = https://github.com/user/repo.git
fetch = +refs/heads/*:refs/remotes/origin/*
[remote "secondary"]
url = https://github.com/user2/repo2.git
fetch = +refs/heads/*:refs/remotes/secondary/*
[remote "gitlab"]
url = https://gitlab.com/user/repo.git
fetch = +refs/heads/*:refs/remotes/gitlab/*

View File

@@ -1,13 +0,0 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = https://github.com/user2/repo2.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master

View File

@@ -1,2 +0,0 @@
!.vercel
.vercel

View File

@@ -1,13 +0,0 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = https://github.com/user/repo.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master

View File

@@ -76,25 +76,6 @@ module.exports = async function prepare(session, binaryPath, tmpFixturesDir) {
},
}),
},
'build-env-debug': {
'now.json': JSON.stringify({
builds: [{ src: 'index.js', use: '@vercel/node' }],
}),
'package.json': JSON.stringify({
scripts: {
'now-build': 'node now-build.js',
},
}),
'now-build.js': `
const fs = require('fs');
fs.writeFileSync(
'index.js',
fs.readFileSync('index.js', 'utf8')
.replace('BUILD_ENV_DEBUG', process.env.NOW_BUILDER_DEBUG ? 'on' : 'off'),
);
`,
'index.js': `module.exports = (req, res) => { res.status(200).send('BUILD_ENV_DEBUG'); }`,
},
'now-revert-alias-1': {
'index.json': JSON.stringify({ name: 'now-revert-alias-1' }),
'now.json': getRevertAliasConfigFile(),

View File

@@ -360,7 +360,7 @@ test('default command should prompt login with empty auth.json', async t => {
} catch (err) {
t.true(
err.stderr.includes(
'Error! No existing credentials found. Please run `vercel login` or pass "--token"'
'Error: No existing credentials found. Please run `vercel login` or pass "--token"'
)
);
}
@@ -749,7 +749,7 @@ test('deploy fails using --local-config flag with non-existent path', async t =>
t.is(exitCode, 1, formatOutput({ stderr, stdout }));
t.regex(stderr, /Error! Couldn't find a project configuration file at/);
t.regex(stderr, /Error: Couldn't find a project configuration file at/);
t.regex(stderr, /does-not-exist\.json/);
});
@@ -1451,7 +1451,7 @@ test('login with unregistered user', async t => {
console.log(stdout);
console.log(exitCode);
const goal = `Error! Please sign up: https://vercel.com/signup`;
const goal = `Error: Please sign up: https://vercel.com/signup`;
const lines = stderr.trim().split('\n');
const last = lines[lines.length - 1];
@@ -1629,7 +1629,7 @@ test('try to purchase a domain', async t => {
t.is(exitCode, 1);
t.regex(
stderr,
/Error! Could not purchase domain\. Please add a payment method using/
/Error: Could not purchase domain\. Please add a payment method using/
);
});
@@ -1655,7 +1655,7 @@ test('try to transfer-in a domain with "--code" option', async t => {
t.true(
stderr.includes(
`Error! The domain "${session}-test.com" is not transferable.`
`Error: The domain "${session}-test.com" is not transferable.`
)
);
t.is(exitCode, 1);
@@ -1680,7 +1680,7 @@ test('try to move an invalid domain', async t => {
console.log(stdout);
console.log(exitCode);
t.true(stderr.includes(`Error! Domain not found under `));
t.true(stderr.includes(`Error: Domain not found under `));
t.is(exitCode, 1);
});
@@ -1964,7 +1964,7 @@ test('try to create a builds deployments with wrong now.json', async t => {
t.is(exitCode, 1);
t.true(
stderr.includes(
'Error! Invalid now.json - should NOT have additional property `builder`. Did you mean `builds`?'
'Error: Invalid now.json - should NOT have additional property `builder`. Did you mean `builds`?'
)
);
t.true(stderr.includes('https://vercel.com/docs/configuration'));
@@ -1988,7 +1988,7 @@ test('try to create a builds deployments with wrong vercel.json', async t => {
t.is(exitCode, 1);
t.true(
stderr.includes(
'Error! Invalid vercel.json - should NOT have additional property `fake`. Please remove it.'
'Error: Invalid vercel.json - should NOT have additional property `fake`. Please remove it.'
)
);
t.true(stderr.includes('https://vercel.com/docs/configuration'));
@@ -2009,7 +2009,7 @@ test('try to create a builds deployments with wrong `build.env` property', async
t.is(exitCode, 1, formatOutput({ stdout, stderr }));
t.true(
stderr.includes(
'Error! Invalid vercel.json - should NOT have additional property `build.env`. Did you mean `{ "build": { "env": {"name": "value"} } }`?'
'Error: Invalid vercel.json - should NOT have additional property `build.env`. Did you mean `{ "build": { "env": {"name": "value"} } }`?'
),
formatOutput({ stdout, stderr })
);
@@ -2170,47 +2170,8 @@ test('use build-env', async t => {
t.is(content.trim(), 'bar');
});
test('use `--debug` CLI flag', async t => {
const directory = fixture('build-env-debug');
const { stderr, stdout, exitCode } = await execa(
binaryPath,
[
directory,
'--public',
'--name',
session,
'--debug',
...defaultArgs,
'--yes',
],
{
reject: false,
}
);
console.log(stderr);
console.log(stdout);
console.log(exitCode);
// Ensure the exit code is right
t.is(exitCode, 0, `Received:\n"${stderr}"\n"${stdout}"`);
// Test if the output is really a URL
const deploymentUrl = pickUrl(stdout);
const { href, host } = new URL(deploymentUrl);
t.is(host.split('-')[0], session);
await waitForDeployment(href);
// get the content
const response = await fetch(href);
const content = await response.text();
t.is(content.trim(), 'off');
});
test('try to deploy non-existing path', async t => {
const goal = `Error! The specified file or directory "${session}" does not exist.`;
const goal = `Error: The specified file or directory "${session}" does not exist.`;
const { stderr, stdout, exitCode } = await execa(
binaryPath,
@@ -2230,7 +2191,7 @@ test('try to deploy non-existing path', async t => {
test('try to deploy with non-existing team', async t => {
const target = fixture('static-deployment');
const goal = `Error! The specified scope does not exist`;
const goal = `Error: The specified scope does not exist`;
const { stderr, stdout, exitCode } = await execa(
binaryPath,
@@ -2313,7 +2274,7 @@ test('try to initialize example to existing directory', async t => {
tmpDir = tmp.dirSync({ unsafeCleanup: true });
const cwd = tmpDir.name;
const goal =
'Error! Destination path "angular" already exists and is not an empty directory. You may use `--force` or `-f` to override it.';
'Error: Destination path "angular" already exists and is not an empty directory. You may use `--force` or `-f` to override it.';
await ensureDir(path.join(cwd, 'angular'));
createFile(path.join(cwd, 'angular', '.gitignore'));
@@ -2330,7 +2291,7 @@ test('try to initialize misspelled example (noce) in non-tty', async t => {
tmpDir = tmp.dirSync({ unsafeCleanup: true });
const cwd = tmpDir.name;
const goal =
'Error! No example found for noce, run `vercel init` to see the list of available examples.';
'Error: No example found for noce, run `vercel init` to see the list of available examples.';
const { stdout, stderr, exitCode } = await execute(['init', 'noce'], { cwd });
@@ -2346,7 +2307,7 @@ test('try to initialize example "example-404"', async t => {
tmpDir = tmp.dirSync({ unsafeCleanup: true });
const cwd = tmpDir.name;
const goal =
'Error! No example found for example-404, run `vercel init` to see the list of available examples.';
'Error: No example found for example-404, run `vercel init` to see the list of available examples.';
const { stdout, stderr, exitCode } = await execute(['init', 'example-404'], {
cwd,
@@ -2522,7 +2483,7 @@ test('`vercel rm` should fail with unexpected option', async t => {
t.is(output.exitCode, 1, formatOutput(output));
t.regex(
output.stderr,
/Error! unknown or unexpected option: --fake/gm,
/Error: unknown or unexpected option: --fake/gm,
formatOutput(output)
);
});
@@ -2816,7 +2777,7 @@ test('invalid `--token`', async t => {
t.is(output.exitCode, 1, formatOutput(output));
t.true(
output.stderr.includes(
'Error! You defined "--token", but its contents are invalid. Must not contain: "\\n", ",", "."'
'Error: You defined "--token", but its contents are invalid. Must not contain: "\\n", ",", "."'
)
);
});
@@ -3539,7 +3500,7 @@ test('reject deploying with invalid token', async t => {
t.is(exitCode, 1, formatOutput({ stderr, stdout }));
t.regex(
stderr,
/Error! Could not retrieve Project Settings\. To link your Project, remove the `\.vercel` directory and deploy again\./g
/Error: Could not retrieve Project Settings\. To link your Project, remove the `\.vercel` directory and deploy again\./g
);
});

View File

@@ -711,7 +711,7 @@ describe('build', () => {
// Error gets printed to the terminal
await expect(client.stderr).toOutput(
'Error! Function must contain at least one property.'
'Error: Function must contain at least one property.'
);
// `builds.json` contains top-level "error" property
@@ -894,6 +894,27 @@ describe('build', () => {
}
});
it('should apply project settings overrides from "vercel.json"', async () => {
const cwd = fixture('project-settings-override');
const output = join(cwd, '.vercel/output');
try {
process.chdir(cwd);
const exitCode = await build(client);
expect(exitCode).toEqual(0);
// The `buildCommand` override in "vercel.json" outputs "3" to the
// index.txt file, so verify that that was produced in the build output
const contents = await fs.readFile(
join(output, 'static/index.txt'),
'utf8'
);
expect(contents.trim()).toEqual('3');
} finally {
process.chdir(originalCwd);
delete process.env.__VERCEL_BUILD_RUNNING;
}
});
describe('should find packages with different main/module/browser keys', function () {
let output: string;

View File

@@ -12,7 +12,7 @@ describe('deploy', () => {
client.setArgv('deploy', __filename);
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
`Error! Support for single file deployments has been removed.\nLearn More: https://vercel.link/no-single-file-deployments\n`
`Error: Support for single file deployments has been removed.\nLearn More: https://vercel.link/no-single-file-deployments\n`
);
await expect(exitCodePromise).resolves.toEqual(1);
});
@@ -21,7 +21,7 @@ describe('deploy', () => {
client.setArgv('deploy', __filename, join(__dirname, 'inspect.test.ts'));
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
`Error! Can't deploy more than one path.\n`
`Error: Can't deploy more than one path.\n`
);
await expect(exitCodePromise).resolves.toEqual(1);
});
@@ -30,7 +30,7 @@ describe('deploy', () => {
client.setArgv('deploy', 'does-not-exists');
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
`Error! The specified file or directory "does-not-exists" does not exist.\n`
`Error: The specified file or directory "does-not-exists" does not exist.\n`
);
await expect(exitCodePromise).resolves.toEqual(1);
});
@@ -41,7 +41,7 @@ describe('deploy', () => {
client.setArgv('deploy', cwd, '--prebuilt');
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
'> Prebuilt deployment cannot be created because `vercel build` failed with error:\n\nError! The build failed (top-level)\n'
'> Prebuilt deployment cannot be created because `vercel build` failed with error:\n\nError: The build failed (top-level)\n'
);
await expect(exitCodePromise).resolves.toEqual(1);
});
@@ -52,7 +52,7 @@ describe('deploy', () => {
client.setArgv('deploy', cwd, '--prebuilt');
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
'> Prebuilt deployment cannot be created because `vercel build` failed with error:\n\nError! The build failed within a Builder\n'
'> Prebuilt deployment cannot be created because `vercel build` failed with error:\n\nError: The build failed within a Builder\n'
);
await expect(exitCodePromise).resolves.toEqual(1);
});
@@ -61,7 +61,7 @@ describe('deploy', () => {
client.setArgv('deploy', __dirname, '--prebuilt');
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
'Error! The "--prebuilt" option was used, but no prebuilt output found in ".vercel/output". Run `vercel build` to generate a local build.\n'
'Error: The "--prebuilt" option was used, but no prebuilt output found in ".vercel/output". Run `vercel build` to generate a local build.\n'
);
await expect(exitCodePromise).resolves.toEqual(1);
});
@@ -80,7 +80,7 @@ describe('deploy', () => {
client.setArgv('deploy', cwd, '--prebuilt', '--prod');
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
'Error! The "--prebuilt" option was used with the target environment "production",' +
'Error: The "--prebuilt" option was used with the target environment "production",' +
' but the prebuilt output found in ".vercel/output" was built with target environment "preview".' +
' Please run `vercel --prebuilt`.\n' +
'Learn More: https://vercel.link/prebuilt-environment-mismatch\n'
@@ -102,7 +102,7 @@ describe('deploy', () => {
client.setArgv('deploy', cwd, '--prebuilt');
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
'Error! The "--prebuilt" option was used with the target environment "preview",' +
'Error: The "--prebuilt" option was used with the target environment "preview",' +
' but the prebuilt output found in ".vercel/output" was built with target environment "production".' +
' Please run `vercel --prebuilt --prod`.\n' +
'Learn More: https://vercel.link/prebuilt-environment-mismatch\n'
@@ -118,7 +118,7 @@ describe('deploy', () => {
};
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
'Error! The value of the `version` property within vercel.json can only be `2`.\n'
'Error: The value of the `version` property within vercel.json can only be `2`.\n'
);
await expect(exitCodePromise).resolves.toEqual(1);
});
@@ -132,7 +132,7 @@ describe('deploy', () => {
};
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
'Error! The `version` property inside your vercel.json file must be a number.\n'
'Error: The `version` property inside your vercel.json file must be a number.\n'
);
await expect(exitCodePromise).resolves.toEqual(1);
});

View File

@@ -39,11 +39,6 @@ describe('git', () => {
await expect(client.stderr).toOutput('Found project');
client.stdin.write('y\n');
await expect(client.stderr).toOutput(
'Do you want to connect "origin" to your Vercel project?'
);
client.stdin.write('n\n');
await expect(client.stderr).toOutput(
`Connecting Git remote: https://github.com/user/repo.git`
);
@@ -85,7 +80,7 @@ describe('git', () => {
const exitCode = await git(client);
expect(exitCode).toEqual(1);
await expect(client.stderr).toOutput(
`Error! No local Git repository found. Run \`git clone <url>\` to clone a remote Git repository first.\n`
`Error: No local Git repository found. Run \`git clone <url>\` to clone a remote Git repository first.\n`
);
} finally {
process.chdir(originalCwd);
@@ -107,7 +102,7 @@ describe('git', () => {
const exitCode = await git(client);
expect(exitCode).toEqual(1);
await expect(client.stderr).toOutput(
`Error! No remote URLs found in your Git config. Make sure you've configured a remote repo in your local Git config. Run \`git remote --help\` for more details.`
`Error: No remote URLs found in your Git config. Make sure you've configured a remote repo in your local Git config. Run \`git remote --help\` for more details.`
);
} finally {
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
@@ -134,7 +129,7 @@ describe('git', () => {
`Connecting Git remote: bababooey`
);
await expect(client.stderr).toOutput(
`Error! Failed to parse Git repo data from the following remote URL: bababooey\n`
`Error: Failed to parse Git repo data from the following remote URL: bababooey\n`
);
} finally {
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
@@ -295,7 +290,7 @@ describe('git', () => {
`Connecting Git remote: https://github.com/laksfj/asdgklsadkl`
);
await expect(client.stderr).toOutput(
`Failed to link laksfj/asdgklsadkl. Make sure there aren't any typos and that you have access to the repository if it's private.`
`Failed to connect laksfj/asdgklsadkl to project. Make sure there aren't any typos and that you have access to the repository if it's private.`
);
const exitCode = await gitPromise;

View File

@@ -33,7 +33,7 @@ describe('inspect', () => {
const exitCode = await inspect(client);
expect(exitCode).toEqual(1);
await expect(client.stderr).toOutput(
`Error! Failed to find deployment "bad.com" in ${user.username}\n`
`Error: Failed to find deployment "bad.com" in ${user.username}\n`
);
});
});

View File

@@ -1,357 +0,0 @@
import { join } from 'path';
import fs from 'fs-extra';
import link from '../../../src/commands/link';
import { useUser } from '../../mocks/user';
import { useTeams } from '../../mocks/team';
import {
defaultProject,
useUnknownProject,
useProject,
} from '../../mocks/project';
import { client } from '../../mocks/client';
import { useDeploymentMissingProjectSettings } from '../../mocks/deployment';
import { Project } from '../../../src/types';
describe('link', () => {
describe('git prompt', () => {
const originalCwd = process.cwd();
const fixture = (name: string) =>
join(__dirname, '../../fixtures/unit/link-connect-git', name);
it('should prompt to connect a new project with a single remote', async () => {
const cwd = fixture('single-remote');
try {
process.chdir(cwd);
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
useUser();
useUnknownProject();
useDeploymentMissingProjectSettings();
useTeams('team_dummy');
const linkPromise = link(client);
await expect(client.stderr).toOutput('Set up');
client.stdin.write('y\n');
await expect(client.stderr).toOutput('Which scope');
client.stdin.write('\r');
await expect(client.stderr).toOutput('Link to existing project?');
client.stdin.write('n\n');
await expect(client.stderr).toOutput('Whats your projects name?');
client.stdin.write('\r');
await expect(client.stderr).toOutput(
'In which directory is your code located?'
);
client.stdin.write('\r');
await expect(client.stderr).toOutput('Want to modify these settings?');
client.stdin.write('n\n');
await expect(client.stderr).toOutput(
'Found local Git remote "origin": https://github.com/user/repo.git'
);
await expect(client.stderr).toOutput(
'Do you want to connect "origin" to your Vercel project?'
);
client.stdin.write('\r');
await expect(client.stderr).toOutput(
'Connected GitHub repository user/repo!'
);
await expect(client.stderr).toOutput('Linked to');
await expect(linkPromise).resolves.toEqual(0);
} finally {
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
process.chdir(originalCwd);
}
});
it('should prompt to connect an existing project with a single remote to git', async () => {
const cwd = fixture('single-remote');
try {
process.chdir(cwd);
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
useUser();
useProject({
...defaultProject,
name: 'single-remote',
id: 'single-remote',
});
useTeams('team_dummy');
const linkPromise = link(client);
await expect(client.stderr).toOutput('Set up');
client.stdin.write('y\n');
await expect(client.stderr).toOutput('Which scope');
client.stdin.write('\r');
await expect(client.stderr).toOutput('Found project');
client.stdin.write('y\n');
await expect(client.stderr).toOutput(
'Found local Git remote "origin": https://github.com/user/repo.git'
);
await expect(client.stderr).toOutput(
'Do you want to connect "origin" to your Vercel project?'
);
client.stdin.write('\r');
await expect(client.stderr).toOutput(
'Connected GitHub repository user/repo!'
);
await expect(client.stderr).toOutput('Linked to');
await expect(linkPromise).resolves.toEqual(0);
} finally {
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
process.chdir(originalCwd);
}
});
it('should prompt to replace a connected repository if there is one remote', async () => {
const cwd = fixture('single-remote-existing-link');
try {
process.chdir(cwd);
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
useUser();
const project = useProject({
...defaultProject,
name: 'single-remote-existing-link',
id: 'single-remote-existing-link',
});
useTeams('team_dummy');
project.project.link = {
type: 'github',
org: 'user',
repo: 'repo',
repoId: 1010,
gitCredentialId: '',
sourceless: true,
createdAt: 1656109539791,
updatedAt: 1656109539791,
};
const linkPromise = link(client);
await expect(client.stderr).toOutput('Set up');
client.stdin.write('y\n');
await expect(client.stderr).toOutput('Which scope');
client.stdin.write('\r');
await expect(client.stderr).toOutput('Found project');
client.stdin.write('y\n');
await expect(client.stderr).toOutput(
`Found Git remote URL https://github.com/user2/repo2.git, which is different from the connected GitHub repository user/repo.`
);
await expect(client.stderr).toOutput('Do you want to replace it?');
client.stdin.write('\r');
await expect(client.stderr).toOutput(
'Connected GitHub repository user2/repo2!'
);
await expect(client.stderr).toOutput('Linked to');
await expect(linkPromise).resolves.toEqual(0);
} finally {
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
process.chdir(originalCwd);
}
});
it('should prompt to connect an existing project with multiple remotes', async () => {
const cwd = fixture('multiple-remotes');
try {
process.chdir(cwd);
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
useUser();
useProject({
...defaultProject,
name: 'multiple-remotes',
id: 'multiple-remotes',
});
useTeams('team_dummy');
const linkPromise = link(client);
await expect(client.stderr).toOutput('Set up');
client.stdin.write('y\n');
await expect(client.stderr).toOutput('Which scope');
client.stdin.write('\r');
await expect(client.stderr).toOutput('Found project');
client.stdin.write('y\n');
await expect(client.stderr).toOutput(
`> Do you want to connect a Git repository to your Vercel project?`
);
client.stdin.write('\r');
await expect(client.stderr).toOutput(
'Connected GitHub repository user/repo!'
);
await expect(client.stderr).toOutput('Linked to');
await expect(linkPromise).resolves.toEqual(0);
} finally {
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
process.chdir(originalCwd);
}
});
it('should not prompt to replace a connected repository if there is more than one remote', async () => {
const cwd = fixture('multiple-remotes');
try {
process.chdir(cwd);
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
useUser();
const project = useProject({
...defaultProject,
name: 'multiple-remotes',
id: 'multiple-remotes',
});
useTeams('team_dummy');
project.project.link = {
type: 'github',
org: 'user',
repo: 'repo',
repoId: 1010,
gitCredentialId: '',
sourceless: true,
createdAt: 1656109539791,
updatedAt: 1656109539791,
};
const linkPromise = link(client);
await expect(client.stderr).toOutput('Set up');
client.stdin.write('y\n');
await expect(client.stderr).toOutput('Which scope');
client.stdin.write('\r');
await expect(client.stderr).toOutput('Found project');
client.stdin.write('y\n');
expect(client.stderr).not.toOutput('Found multiple Git remote URLs');
await expect(client.stderr).toOutput('Linked to');
await expect(linkPromise).resolves.toEqual(0);
} finally {
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
process.chdir(originalCwd);
}
});
it('should set a project setting if user opts out', async () => {
const cwd = fixture('single-remote');
try {
process.chdir(cwd);
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
useUser();
useProject({
...defaultProject,
name: 'single-remote',
id: 'single-remote',
});
useTeams('team_dummy');
const linkPromise = link(client);
await expect(client.stderr).toOutput('Set up');
client.stdin.write('y\n');
await expect(client.stderr).toOutput('Which scope');
client.stdin.write('\r');
await expect(client.stderr).toOutput('Found project');
client.stdin.write('y\n');
await expect(client.stderr).toOutput(
'Found local Git remote "origin": https://github.com/user/repo.git'
);
await expect(client.stderr).toOutput(
'Do you want to connect "origin" to your Vercel project?'
);
client.stdin.write('\x1B[B'); // Down arrow
client.stdin.write('\x1B[B');
client.stdin.write('\r'); // Opt out
await expect(client.stderr).toOutput(`Opted out.`);
await expect(client.stderr).toOutput('Linked to');
await expect(linkPromise).resolves.toEqual(0);
const newProjectData: Project = await client.fetch(
`/v8/projects/single-remote`
);
expect(newProjectData.skipGitConnectDuringLink).toBeTruthy();
} finally {
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
process.chdir(originalCwd);
}
});
it('should not prompt to connect git if the project has skipGitConnectDuringLink property', async () => {
const cwd = fixture('single-remote');
try {
process.chdir(cwd);
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
useUser();
const project = useProject({
...defaultProject,
name: 'single-remote',
id: 'single-remote',
});
useTeams('team_dummy');
project.project.skipGitConnectDuringLink = true;
const linkPromise = link(client);
await expect(client.stderr).toOutput('Set up');
client.stdin.write('y\n');
await expect(client.stderr).toOutput('Which scope');
client.stdin.write('\r');
await expect(client.stderr).toOutput('Found project');
client.stdin.write('y\n');
expect(client.stderr).not.toOutput('Found local Git remote "origin"');
await expect(client.stderr).toOutput('Linked to');
await expect(linkPromise).resolves.toEqual(0);
} finally {
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
process.chdir(originalCwd);
}
});
it('should respect --yes', async () => {
const cwd = fixture('single-remote');
try {
process.chdir(cwd);
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
useUser();
useProject({
...defaultProject,
name: 'single-remote',
id: 'single-remote',
});
useTeams('team_dummy');
client.setArgv('--yes');
const linkPromise = link(client);
expect(client.stderr).not.toOutput('Do you want to connect "origin"');
await expect(client.stderr).toOutput('Linked to');
await expect(linkPromise).resolves.toEqual(0);
} finally {
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
process.chdir(originalCwd);
}
});
it('should respect --yes for multiple remotes when origin is not the first', async () => {
const cwd = fixture('multiple-remotes-prefer-origin');
try {
process.chdir(cwd);
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
useUser();
useProject({
...defaultProject,
name: 'multiple-remotes-prefer-origin',
id: 'multiple-remotes-prefer-origin',
});
useTeams('team_dummy');
client.setArgv('--yes');
const linkPromise = link(client);
expect(client.stderr).not.toOutput('Found multiple Git remote URLs');
await expect(client.stderr).toOutput(
'Connected GitHub repository user/repo'
);
await expect(linkPromise).resolves.toEqual(0);
} finally {
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
process.chdir(originalCwd);
}
});
});
});

View File

@@ -7,7 +7,7 @@ describe('login', () => {
client.setArgv('login', '--token', 'foo');
const exitCodePromise = login(client);
await expect(client.stderr).toOutput(
'Error! `--token` may not be used with the "login" command\n'
'Error: `--token` may not be used with the "login" command\n'
);
await expect(exitCodePromise).resolves.toEqual(2);
});

View File

@@ -0,0 +1,31 @@
import { progress } from '../../../../src/util/output/progress';
describe('progress()', () => {
test.each([
{ current: 0, total: 5, opts: { width: 5 }, expected: '-----' },
{ current: 1, total: 5, opts: { width: 5 }, expected: '=----' },
{ current: 2, total: 5, opts: { width: 5 }, expected: '==---' },
{ current: 3, total: 5, opts: { width: 5 }, expected: '===--' },
{ current: 4, total: 5, opts: { width: 5 }, expected: '====-' },
{ current: 5, total: 5, opts: { width: 5 }, expected: '=====' },
{ current: 0, total: 12, expected: '--------------------' },
{ current: 1, total: 12, expected: '=-------------------' },
{ current: 2, total: 12, expected: '===-----------------' },
{ current: 600, total: 1200, expected: '==========----------' },
{
current: 9,
total: 10,
opts: { complete: '.', incomplete: ' ', width: 10 },
expected: '......... ',
},
{ current: 10, total: 10, expected: '====================' },
{ current: 11, total: 10, expected: null },
{ current: -1, total: 10, expected: null },
{ current: 1, total: 0, expected: null },
])(
'$current / $total -> "$expected"',
({ current, total, opts, expected }) => {
expect(progress(current, total, opts)).toEqual(expected);
}
);
});

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "12.2.0",
"version": "12.2.4",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -31,7 +31,7 @@
"@types/node": "12.0.4",
"@types/node-fetch": "2.5.4",
"@types/recursive-readdir": "2.2.0",
"@types/tar-fs": "^2.0.1",
"@types/tar-fs": "1.16.1",
"typescript": "4.3.4"
},
"jest": {
@@ -43,7 +43,7 @@
]
},
"dependencies": {
"@vercel/build-utils": "5.3.1",
"@vercel/build-utils": "5.4.2",
"@vercel/routing-utils": "2.0.2",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",
@@ -54,6 +54,7 @@
"ms": "2.1.2",
"node-fetch": "2.6.7",
"querystring": "^0.2.0",
"sleep-promise": "8.0.1"
"sleep-promise": "8.0.1",
"tar-fs": "1.16.3"
}
}

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