Compare commits

...

36 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
138 changed files with 6142 additions and 7531 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

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

@@ -1,107 +1,107 @@
<script>
import { spring } from 'svelte/motion';
import { spring } from 'svelte/motion';
let count = 0;
let count = 0;
const displayed_count = spring();
$: displayed_count.set(count);
$: offset = modulo($displayed_count, 1);
const displayed_count = spring();
$: displayed_count.set(count);
$: offset = modulo($displayed_count, 1);
/**
* @param {number} n
* @param {number} m
*/
function modulo(n, m) {
// handle negative numbers
return ((n % m) + m) % m;
}
/**
* @param {number} n
* @param {number} m
*/
function modulo(n, m) {
// handle negative numbers
return ((n % m) + m) % m;
}
</script>
<div class="counter">
<button on:click={() => (count -= 1)} aria-label="Decrease the counter by one">
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5" />
</svg>
</button>
<button on:click={() => (count -= 1)} aria-label="Decrease the counter by one">
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5" />
</svg>
</button>
<div class="counter-viewport">
<div class="counter-digits" style="transform: translate(0, {100 * offset}%)">
<strong class="hidden" aria-hidden="true">{Math.floor($displayed_count + 1)}</strong>
<strong>{Math.floor($displayed_count)}</strong>
</div>
</div>
<div class="counter-viewport">
<div class="counter-digits" style="transform: translate(0, {100 * offset}%)">
<strong class="hidden" aria-hidden="true">{Math.floor($displayed_count + 1)}</strong>
<strong>{Math.floor($displayed_count)}</strong>
</div>
</div>
<button on:click={() => (count += 1)} aria-label="Increase the counter by one">
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5 M0.5,0 L0.5,1" />
</svg>
</button>
<button on:click={() => (count += 1)} aria-label="Increase the counter by one">
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5 M0.5,0 L0.5,1" />
</svg>
</button>
</div>
<style>
.counter {
display: flex;
border-top: 1px solid rgba(0, 0, 0, 0.1);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
margin: 1rem 0;
}
.counter {
display: flex;
border-top: 1px solid rgba(0, 0, 0, 0.1);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
margin: 1rem 0;
}
.counter button {
width: 2em;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
border: 0;
background-color: transparent;
touch-action: manipulation;
color: var(--text-color);
font-size: 2rem;
}
.counter button {
width: 2em;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
border: 0;
background-color: transparent;
touch-action: manipulation;
color: var(--text-color);
font-size: 2rem;
}
.counter button:hover {
background-color: var(--secondary-color);
}
.counter button:hover {
background-color: var(--secondary-color);
}
svg {
width: 25%;
height: 25%;
}
svg {
width: 25%;
height: 25%;
}
path {
vector-effect: non-scaling-stroke;
stroke-width: 2px;
stroke: var(--text-color);
}
path {
vector-effect: non-scaling-stroke;
stroke-width: 2px;
stroke: var(--text-color);
}
.counter-viewport {
width: 8em;
height: 4em;
overflow: hidden;
text-align: center;
position: relative;
}
.counter-viewport {
width: 8em;
height: 4em;
overflow: hidden;
text-align: center;
position: relative;
}
.counter-viewport strong {
position: absolute;
display: flex;
width: 100%;
height: 100%;
font-weight: 400;
color: var(--accent-color);
font-size: 4rem;
align-items: center;
justify-content: center;
}
.counter-viewport strong {
position: absolute;
display: flex;
width: 100%;
height: 100%;
font-weight: 400;
color: var(--accent-color);
font-size: 4rem;
align-items: center;
justify-content: center;
}
.counter-digits {
position: absolute;
width: 100%;
height: 100%;
}
.counter-digits {
position: absolute;
width: 100%;
height: 100%;
}
.hidden {
top: -100%;
user-select: none;
}
.hidden {
top: -100%;
user-select: none;
}
</style>

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

@@ -1,124 +1,124 @@
<script>
import { page } from '$app/stores';
import logo from './svelte-logo.svg';
import { page } from '$app/stores';
import logo from './svelte-logo.svg';
</script>
<header>
<div class="corner">
<a href="https://kit.svelte.dev">
<img src={logo} alt="SvelteKit" />
</a>
</div>
<div class="corner">
<a href="https://kit.svelte.dev">
<img src={logo} alt="SvelteKit" />
</a>
</div>
<nav>
<svg viewBox="0 0 2 3" aria-hidden="true">
<path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" />
</svg>
<ul>
<li class:active={$page.url.pathname === '/'}><a sveltekit:prefetch href="/">Home</a></li>
<li class:active={$page.url.pathname === '/about'}>
<a sveltekit:prefetch href="/about">About</a>
</li>
<li class:active={$page.url.pathname === '/todos'}>
<a sveltekit:prefetch href="/todos">Todos</a>
</li>
</ul>
<svg viewBox="0 0 2 3" aria-hidden="true">
<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
</svg>
</nav>
<nav>
<svg viewBox="0 0 2 3" aria-hidden="true">
<path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" />
</svg>
<ul>
<li class:active={$page.url.pathname === '/'}><a sveltekit:prefetch href="/">Home</a></li>
<li class:active={$page.url.pathname === '/about'}>
<a sveltekit:prefetch href="/about">About</a>
</li>
<li class:active={$page.url.pathname === '/todos'}>
<a sveltekit:prefetch href="/todos">Todos</a>
</li>
</ul>
<svg viewBox="0 0 2 3" aria-hidden="true">
<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
</svg>
</nav>
<div class="corner">
<!-- TODO put something else here? github link? -->
</div>
<div class="corner">
<!-- TODO put something else here? github link? -->
</div>
</header>
<style>
header {
display: flex;
justify-content: space-between;
}
header {
display: flex;
justify-content: space-between;
}
.corner {
width: 3em;
height: 3em;
}
.corner {
width: 3em;
height: 3em;
}
.corner a {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.corner a {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.corner img {
width: 2em;
height: 2em;
object-fit: contain;
}
.corner img {
width: 2em;
height: 2em;
object-fit: contain;
}
nav {
display: flex;
justify-content: center;
--background: rgba(255, 255, 255, 0.7);
}
nav {
display: flex;
justify-content: center;
--background: rgba(255, 255, 255, 0.7);
}
svg {
width: 2em;
height: 3em;
display: block;
}
svg {
width: 2em;
height: 3em;
display: block;
}
path {
fill: var(--background);
}
path {
fill: var(--background);
}
ul {
position: relative;
padding: 0;
margin: 0;
height: 3em;
display: flex;
justify-content: center;
align-items: center;
list-style: none;
background: var(--background);
background-size: contain;
}
ul {
position: relative;
padding: 0;
margin: 0;
height: 3em;
display: flex;
justify-content: center;
align-items: center;
list-style: none;
background: var(--background);
background-size: contain;
}
li {
position: relative;
height: 100%;
}
li {
position: relative;
height: 100%;
}
li.active::before {
--size: 6px;
content: '';
width: 0;
height: 0;
position: absolute;
top: 0;
left: calc(50% - var(--size));
border: var(--size) solid transparent;
border-top: var(--size) solid var(--accent-color);
}
li.active::before {
--size: 6px;
content: '';
width: 0;
height: 0;
position: absolute;
top: 0;
left: calc(50% - var(--size));
border: var(--size) solid transparent;
border-top: var(--size) solid var(--accent-color);
}
nav a {
display: flex;
height: 100%;
align-items: center;
padding: 0 1em;
color: var(--heading-color);
font-weight: 700;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.1em;
text-decoration: none;
transition: color 0.2s linear;
}
nav a {
display: flex;
height: 100%;
align-items: center;
padding: 0 1em;
color: var(--heading-color);
font-weight: 700;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.1em;
text-decoration: none;
transition: color 0.2s linear;
}
a:hover {
color: var(--accent-color);
}
a:hover {
color: var(--accent-color);
}
</style>

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.2",
"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;
}

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.4",
"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.2",
"@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",
"@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.1",
"@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

@@ -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 @@
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

@@ -449,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;
@@ -516,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

@@ -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

@@ -80,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);
@@ -102,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'));
@@ -129,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'));

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

@@ -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.1",
"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.2",
"@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"
}
}

View File

@@ -1,3 +1,4 @@
# `@vercel/edge`
A set of utilities to help you deploy your app on the Edge using Vercel.
A set of utilities to help you deploy any framework on the Edge using Vercel.
Please [follow the documentation](./docs) for examples and usage.

View File

@@ -0,0 +1,262 @@
# @vercel/edge
## Table of contents
### Interfaces
- [ExtraResponseInit](interfaces/ExtraResponseInit.md)
- [Geo](interfaces/Geo.md)
### Variables
- [CITY_HEADER_NAME](README.md#city_header_name)
- [COUNTRY_HEADER_NAME](README.md#country_header_name)
- [IP_HEADER_NAME](README.md#ip_header_name)
- [LATITUDE_HEADER_NAME](README.md#latitude_header_name)
- [LONGITUDE_HEADER_NAME](README.md#longitude_header_name)
- [REGION_HEADER_NAME](README.md#region_header_name)
### Functions
- [geolocation](README.md#geolocation)
- [ipAddress](README.md#ipaddress)
- [next](README.md#next)
- [rewrite](README.md#rewrite)
## Variables
### CITY_HEADER_NAME
`Const` **CITY_HEADER_NAME**: `"x-vercel-ip-city"`
City of the original client IP as calculated by Vercel Proxy.
#### Defined in
[src/edge-headers.ts:4](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L4)
---
### COUNTRY_HEADER_NAME
`Const` **COUNTRY_HEADER_NAME**: `"x-vercel-ip-country"`
Country of the original client IP as calculated by Vercel Proxy.
#### Defined in
[src/edge-headers.ts:8](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L8)
---
### IP_HEADER_NAME
`Const` **IP_HEADER_NAME**: `"x-real-ip"`
Client IP as calcualted by Vercel Proxy.
#### Defined in
[src/edge-headers.ts:12](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L12)
---
### LATITUDE_HEADER_NAME
`Const` **LATITUDE_HEADER_NAME**: `"x-vercel-ip-latitude"`
Latitude of the original client IP as calculated by Vercel Proxy.
#### Defined in
[src/edge-headers.ts:16](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L16)
---
### LONGITUDE_HEADER_NAME
`Const` **LONGITUDE_HEADER_NAME**: `"x-vercel-ip-longitude"`
Longitude of the original client IP as calculated by Vercel Proxy.
#### Defined in
[src/edge-headers.ts:20](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L20)
---
### REGION_HEADER_NAME
`Const` **REGION_HEADER_NAME**: `"x-vercel-ip-country-region"`
Region of the original client IP as calculated by Vercel Proxy.
#### Defined in
[src/edge-headers.ts:24](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L24)
## Functions
### geolocation
**geolocation**(`request`): [`Geo`](interfaces/Geo.md)
Returns the location information for the incoming request.
**`See`**
- [CITY_HEADER_NAME](README.md#city_header_name)
- [COUNTRY_HEADER_NAME](README.md#country_header_name)
- [REGION_HEADER_NAME](README.md#region_header_name)
- [LATITUDE_HEADER_NAME](README.md#latitude_header_name)
- [LONGITUDE_HEADER_NAME](README.md#longitude_header_name)
#### Parameters
| Name | Type | Description |
| :-------- | :-------- | :-------------------------------------------------------------- |
| `request` | `Request` | The incoming request object which provides the geolocation data |
#### Returns
[`Geo`](interfaces/Geo.md)
#### Defined in
[src/edge-headers.ts:80](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L80)
---
### ipAddress
**ipAddress**(`request`): `string` \| `undefined`
Returns the IP address of the request from the headers.
**`See`**
[IP_HEADER_NAME](README.md#ip_header_name)
#### Parameters
| Name | Type | Description |
| :-------- | :-------- | :------------------------------------------------ |
| `request` | `Request` | The incoming request object which provides the IP |
#### Returns
`string` \| `undefined`
#### Defined in
[src/edge-headers.ts:66](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L66)
---
### next
**next**(`init?`): `Response`
Returns a Response that instructs the system to continue processing the request.
**`Example`**
<caption>No-op middleware</caption>
```ts
import { next } from '@vercel/edge';
export default function middleware(_req: Request) {
return next();
}
```
**`Example`**
<caption>Add response headers to all requests</caption>
```ts
import { next } from '@vercel/edge';
export default function middleware(_req: Request) {
return next({
headers: { 'x-from-middleware': 'true' },
});
}
```
#### Parameters
| Name | Type | Description |
| :------ | :----------------------------------------------------- | :---------------------------------- |
| `init?` | [`ExtraResponseInit`](interfaces/ExtraResponseInit.md) | Additional options for the response |
#### Returns
`Response`
#### Defined in
[src/middleware-helpers.ts:94](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L94)
---
### rewrite
**rewrite**(`destination`, `init?`): `Response`
Returns a response that rewrites the request to a different URL.
**`Example`**
<caption>Rewrite all feature-flagged requests from `/:path*` to `/experimental/:path*`</caption>
```ts
import { rewrite, next } from '@vercel/edge';
export default async function middleware(req: Request) {
const flagged = await getFlag(req, 'isExperimental');
if (flagged) {
const url = new URL(req.url);
url.pathname = `/experimental{url.pathname}`;
return rewrite(url);
}
return next();
}
```
**`Example`**
<caption>JWT authentication for `/api/:path*` requests</caption>
```ts
import { rewrite, next } from '@vercel/edge';
export default function middleware(req: Request) {
const auth = checkJwt(req.headers.get('Authorization'));
if (!checkJwt) {
return rewrite(new URL('/api/error-unauthorized', req.url));
}
const url = new URL(req.url);
url.searchParams.set('_userId', auth.userId);
return rewrite(url);
}
export const config = { matcher: '/api/users/:path*' };
```
#### Parameters
| Name | Type | Description |
| :------------ | :------------------------------------------------------------------------ | :---------------------------------- |
| `destination` | `string` \| [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) | new URL to rewrite the request to |
| `init?` | [`ExtraResponseInit`](interfaces/ExtraResponseInit.md) | Additional options for the response |
#### Returns
`Response`
#### Defined in
[src/middleware-helpers.ts:53](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L53)

View File

@@ -0,0 +1,56 @@
# Interface: ExtraResponseInit
## Hierarchy
- `Omit`<`ResponseInit`, `"headers"`\>
**`ExtraResponseInit`**
## Table of contents
### Properties
- [headers](ExtraResponseInit.md#headers)
- [status](ExtraResponseInit.md#status)
- [statusText](ExtraResponseInit.md#statustext)
## Properties
### headers
`Optional` **headers**: `HeadersInit`
These headers will be sent to the user response
along with the response headers from the origin.
#### Defined in
[src/middleware-helpers.ts:6](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L6)
---
### status
`Optional` **status**: `number`
#### Inherited from
Omit.status
#### Defined in
node_modules/typescript/lib/lib.dom.d.ts:1578
---
### statusText
`Optional` **statusText**: `string`
#### Inherited from
Omit.statusText
#### Defined in
node_modules/typescript/lib/lib.dom.d.ts:1579

View File

@@ -0,0 +1,73 @@
# Interface: Geo
The location information of a given request.
## Table of contents
### Properties
- [city](Geo.md#city)
- [country](Geo.md#country)
- [latitude](Geo.md#latitude)
- [longitude](Geo.md#longitude)
- [region](Geo.md#region)
## Properties
### city
`Optional` **city**: `string`
The city that the request originated from.
#### Defined in
[src/edge-headers.ts:41](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L41)
---
### country
`Optional` **country**: `string`
The country that the request originated from.
#### Defined in
[src/edge-headers.ts:44](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L44)
---
### latitude
`Optional` **latitude**: `string`
The latitude of the client.
#### Defined in
[src/edge-headers.ts:50](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L50)
---
### longitude
`Optional` **longitude**: `string`
The longitude of the client.
#### Defined in
[src/edge-headers.ts:53](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L53)
---
### region
`Optional` **region**: `string`
The [Vercel Edge Network region](https://vercel.com/docs/concepts/edge-network/regions) that received the request.
#### Defined in
[src/edge-headers.ts:47](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L47)

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/edge",
"version": "0.0.3",
"version": "0.0.4",
"license": "MIT",
"main": "dist/index.js",
"module": "dist/index.mjs",
@@ -8,13 +8,17 @@
"scripts": {
"build": "tsup src/index.ts --dts --format esm,cjs",
"test": "jest --env node --verbose --runInBand --bail",
"test-unit": "yarn test"
"test-unit": "yarn test",
"build:docs": "typedoc && prettier --write docs/**/*.md docs/*.md"
},
"devDependencies": {
"@edge-runtime/jest-environment": "1.1.0-beta.7",
"@types/jest": "27.4.1",
"ts-node": "8.9.1",
"tsup": "6.1.2",
"typedoc": "0.23.10",
"typedoc-plugin-markdown": "3.13.4",
"typedoc-plugin-mdn-links": "2.0.0",
"typescript": "4.7.4"
},
"jest": {

View File

@@ -1,25 +1,25 @@
/**
* City of the original client IP calculated by Vercel Proxy.
* City of the original client IP as calculated by Vercel Proxy.
*/
export const CITY_HEADER_NAME = 'x-vercel-ip-city';
/**
* Country of the original client IP calculated by Vercel Proxy.
* Country of the original client IP as calculated by Vercel Proxy.
*/
export const COUNTRY_HEADER_NAME = 'x-vercel-ip-country';
/**
* Ip from Vercel Proxy. Do not confuse it with the client Ip.
* Client IP as calcualted by Vercel Proxy.
*/
export const IP_HEADER_NAME = 'x-real-ip';
/**
* Latitude of the original client IP calculated by Vercel Proxy.
* Latitude of the original client IP as calculated by Vercel Proxy.
*/
export const LATITUDE_HEADER_NAME = 'x-vercel-ip-latitude';
/**
* Longitude of the original client IP calculated by Vercel Proxy.
* Longitude of the original client IP as calculated by Vercel Proxy.
*/
export const LONGITUDE_HEADER_NAME = 'x-vercel-ip-longitude';
/**
* Region of the original client IP calculated by Vercel Proxy.
* Region of the original client IP as calculated by Vercel Proxy.
*/
export const REGION_HEADER_NAME = 'x-vercel-ip-country-region';
@@ -34,18 +34,22 @@ interface Request {
}
/**
* The location information of a given request
* The location information of a given request.
*/
export interface Geo {
/** The city that the request originated from */
/** The city that the request originated from. */
city?: string;
/** The country that the request originated from */
/** The country that the request originated from. */
country?: string;
/** The Vercel Edge Network region that received the request */
/** The [Vercel Edge Network region](https://vercel.com/docs/concepts/edge-network/regions) that received the request. */
region?: string;
/** The latitude of the client */
/** The latitude of the client. */
latitude?: string;
/** The longitude of the client */
/** The longitude of the client. */
longitude?: string;
}
@@ -57,19 +61,21 @@ function getHeader(request: Request, key: string): string | undefined {
* Returns the IP address of the request from the headers.
*
* @see {@link IP_HEADER_NAME}
* @param request The incoming request object which provides the IP
*/
export function ipAddress(request: Request): string | undefined {
return getHeader(request, IP_HEADER_NAME);
}
/**
* Returns the location information from for the incoming request
* Returns the location information for the incoming request.
*
* @see {@link CITY_HEADER_NAME}
* @see {@link COUNTRY_HEADER_NAME}
* @see {@link REGION_HEADER_NAME}
* @see {@link LATITUDE_HEADER_NAME}
* @see {@link LONGITUDE_HEADER_NAME}
* @param request The incoming request object which provides the geolocation data
*/
export function geolocation(request: Request): Geo {
return {

View File

@@ -1,13 +1,54 @@
export type ExtraResponseInit = Omit<ResponseInit, 'headers'> & {
export interface ExtraResponseInit extends Omit<ResponseInit, 'headers'> {
/**
* These headers will be sent to the user response
* along with the response headers from the origin
* along with the response headers from the origin.
*/
headers?: HeadersInit;
};
}
/**
* Rewrite the request into a different URL.
* Returns a response that rewrites the request to a different URL.
*
* @param destination new URL to rewrite the request to
* @param init Additional options for the response
*
*
* @example
* <caption>Rewrite all feature-flagged requests from `/:path*` to `/experimental/:path*`</caption>
*
* ```ts
* import { rewrite, next } from '@vercel/edge';
*
* export default async function middleware(req: Request) {
* const flagged = await getFlag(req, 'isExperimental');
* if (flagged) {
* const url = new URL(req.url);
* url.pathname = `/experimental{url.pathname}`;
* return rewrite(url);
* }
*
* return next();
* }
* ```
*
* @example
* <caption>JWT authentication for `/api/:path*` requests</caption>
*
* ```ts
* import { rewrite, next } from '@vercel/edge';
*
* export default function middleware(req: Request) {
* const auth = checkJwt(req.headers.get('Authorization'));
* if (!checkJwt) {
* return rewrite(new URL('/api/error-unauthorized', req.url));
* }
* const url = new URL(req.url);
* url.searchParams.set('_userId', auth.userId);
* return rewrite(url);
* }
*
* export const config = { matcher: '/api/users/:path*' };
* ```
*/
export function rewrite(
destination: string | URL,
@@ -22,7 +63,33 @@ export function rewrite(
}
/**
* This tells the Middleware to continue with the request.
* Returns a Response that instructs the system to continue processing the request.
*
* @param init Additional options for the response
*
* @example
* <caption>No-op middleware</caption>
*
* ```ts
* import { next } from '@vercel/edge';
*
* export default function middleware(_req: Request) {
* return next();
* }
* ```
*
* @example
* <caption>Add response headers to all requests</caption>
*
* ```ts
* import { next } from '@vercel/edge';
*
* export default function middleware(_req: Request) {
* return next({
* headers: { 'x-from-middleware': 'true' },
* })
* }
* ```
*/
export function next(init?: ExtraResponseInit): Response {
const headers = new Headers(init?.headers ?? {});

38
packages/edge/test/docs.test.ts vendored Normal file
View File

@@ -0,0 +1,38 @@
/// <reference types="@types/node" />
import { exec } from 'child_process';
import { promisify } from 'util';
import path from 'path';
const execAsync = promisify(exec);
const test = process.platform === 'win32' ? it.skip : it;
test('docs are up to date', async () => {
const cwd = path.resolve(__dirname, '../');
await execAsync(`yarn build:docs`, { cwd });
const result = await execAsync(`git status --short docs`, {
cwd,
encoding: 'utf-8',
});
const lines = result.stdout
.trim()
.split(/(?:\r?\n)+/)
.map(x => x.trim().split(/\s+/).slice(1).join(' '))
.filter(x => x.startsWith('docs/'))
.map(x => `* ${x}`)
.join('\n')
.trim();
if (lines !== '') {
const diff = await execAsync(`git diff docs`, { cwd, encoding: 'utf8' });
throw new Error(
'Docs are not up to date. Please re-run `yarn build:docs` to re-generate them.\nChanges:\n' +
lines +
'\n\n' +
diff.stdout
);
}
expect(result.stdout.trim()).toEqual('');
}, 120000);

View File

@@ -0,0 +1,10 @@
{
"$schema": "https://typedoc.org/schema.json",
"entryPoints": ["src/index.ts"],
"plugin": ["typedoc-plugin-markdown", "typedoc-plugin-mdn-links"],
"out": "docs",
"githubPages": false,
"gitRevision": "main",
"readme": "none",
"hideBreadcrumbs": true
}

View File

@@ -1 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M22.428.013c-.103.01-.431.042-.727.066C14.883.693 8.497 4.37 4.453 10.024A23.754 23.754 0 0 0 .216 20.51C.023 21.828 0 22.217 0 24.005c0 1.787.023 2.177.216 3.495 1.304 9.012 7.718 16.584 16.417 19.39 1.558.501 3.2.844 5.068 1.05.727.08 3.87.08 4.598 0 3.224-.356 5.954-1.154 8.648-2.529.412-.21.492-.267.436-.314-.038-.028-1.797-2.388-3.909-5.24l-3.838-5.184-4.809-7.117c-2.646-3.913-4.824-7.112-4.842-7.112-.019-.005-.038 3.157-.047 7.018-.014 6.76-.019 7.033-.103 7.192-.122.23-.216.324-.413.427-.15.075-.282.09-.99.09h-.812l-.216-.137a.878.878 0 0 1-.314-.342l-.099-.211.01-9.407.014-9.41.145-.184c.075-.098.235-.225.347-.286.193-.094.268-.103 1.08-.103.957 0 1.116.038 1.365.31.07.075 2.674 3.997 5.79 8.721s7.376 11.175 9.469 14.342l3.8 5.756.192-.127c1.704-1.107 3.505-2.683 4.932-4.325a23.888 23.888 0 0 0 5.65-12.268c.192-1.319.215-1.708.215-3.495 0-1.788-.023-2.177-.216-3.495-1.304-9.013-7.718-16.584-16.417-19.39C29.832.623 28.199.28 26.369.074c-.45-.047-3.551-.099-3.94-.061zm9.825 14.515a.947.947 0 0 1 .474.554c.038.122.047 2.73.038 8.608l-.014 8.436-1.488-2.28-1.492-2.28v-6.132c0-3.964.019-6.193.047-6.3a.957.957 0 0 1 .465-.592c.192-.098.262-.108 1-.108.694 0 .816.01.97.094z" fill="#fff"/></svg>
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><mask id="mask" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="48" height="48"><circle cx="24" cy="24" r="24" fill="#000"/></mask><g mask="url(#mask)"><circle cx="24" cy="24" r="23.2" fill="#000" stroke="#fff" stroke-width="1.6"/><path d="M39.8687 42.0055L18.4378 14.4H14.3999V33.592H17.6302V18.5023L37.333 43.9587C38.222 43.3637 39.069 42.7108 39.8687 42.0055Z" fill="url(#gradient0)"/><rect x="30.6667" y="14.4" width="3.2" height="19.2" fill="url(#gradient1)"/></g><defs><linearGradient id="gradient0" x1="29.0666" y1="31.0667" x2="38.5332" y2="42.8" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient><linearGradient id="gradient1" x1="32.2667" y1="14.4" x2="32.2132" y2="28.5001" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient></defs></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 997 B

View File

@@ -1 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M22.428.013c-.103.01-.431.042-.727.066C14.883.693 8.497 4.37 4.453 10.024A23.754 23.754 0 0 0 .216 20.51C.023 21.828 0 22.217 0 24.005c0 1.787.023 2.177.216 3.495 1.304 9.012 7.718 16.584 16.417 19.39 1.558.501 3.2.844 5.068 1.05.727.08 3.87.08 4.598 0 3.224-.356 5.954-1.154 8.648-2.529.412-.21.492-.267.436-.314-.038-.028-1.797-2.388-3.909-5.24l-3.838-5.184-4.809-7.117c-2.646-3.913-4.824-7.112-4.842-7.112-.019-.005-.038 3.157-.047 7.018-.014 6.76-.019 7.033-.103 7.192-.122.23-.216.324-.413.427-.15.075-.282.09-.99.09h-.812l-.216-.137a.878.878 0 0 1-.314-.342l-.099-.211.01-9.407.014-9.41.145-.184c.075-.098.235-.225.347-.286.193-.094.268-.103 1.08-.103.957 0 1.116.038 1.365.31.07.075 2.674 3.997 5.79 8.721s7.376 11.175 9.469 14.342l3.8 5.756.192-.127c1.704-1.107 3.505-2.683 4.932-4.325a23.888 23.888 0 0 0 5.65-12.268c.192-1.319.215-1.708.215-3.495 0-1.788-.023-2.177-.216-3.495-1.304-9.013-7.718-16.584-16.417-19.39C29.832.623 28.199.28 26.369.074c-.45-.047-3.551-.099-3.94-.061zm9.825 14.515a.947.947 0 0 1 .474.554c.038.122.047 2.73.038 8.608l-.014 8.436-1.488-2.28-1.492-2.28v-6.132c0-3.964.019-6.193.047-6.3a.957.957 0 0 1 .465-.592c.192-.098.262-.108 1-.108.694 0 .816.01.97.094z" fill="#000"/></svg>
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><mask id="mask" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="48" height="48"><circle cx="24" cy="24" r="24" fill="#000"/></mask><g mask="url(#mask)"><circle cx="24" cy="24" r="23.2" fill="#000" stroke="#000" stroke-width="1.6"/><path d="M39.8687 42.0055L18.4378 14.4H14.3999V33.592H17.6302V18.5023L37.333 43.9587C38.222 43.3637 39.069 42.7108 39.8687 42.0055Z" fill="url(#gradient0)"/><rect x="30.6667" y="14.4" width="3.2" height="19.2" fill="url(#gradient1)"/></g><defs><linearGradient id="gradient0" x1="29.0666" y1="31.0667" x2="38.5332" y2="42.8" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient><linearGradient id="gradient1" x1="32.2667" y1="14.4" x2="32.2132" y2="28.5001" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient></defs></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 997 B

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/frameworks",
"version": "1.1.3",
"version": "1.1.4",
"main": "./dist/frameworks.js",
"types": "./dist/frameworks.d.ts",
"files": [

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/fs-detectors",
"version": "2.0.5",
"version": "3.0.0",
"description": "Vercel filesystem detectors",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -19,7 +19,7 @@
"test-unit": "yarn test"
},
"dependencies": {
"@vercel/frameworks": "1.1.3",
"@vercel/frameworks": "1.1.4",
"@vercel/routing-utils": "2.0.2",
"glob": "8.0.3",
"js-yaml": "4.1.0",

View File

@@ -1,3 +1,5 @@
import { posix as posixPath } from 'path';
export interface Stat {
name: string;
path: string;
@@ -76,12 +78,43 @@ export abstract class DetectorFilesystem {
/**
* Returns a list of Stat objects from the current working directory.
* @param dirPath The path of the directory to read.
* @param options.potentialFiles optional. Array of potential file names (relative to the path). If provided, these will be used to mark the filesystem caches as existing or not existing.
*/
public readdir = async (name: string): Promise<Stat[]> => {
let p = this.readdirCache.get(name);
public readdir = async (
dirPath: string,
options?: { potentialFiles?: string[] }
): Promise<Stat[]> => {
let p = this.readdirCache.get(dirPath);
if (!p) {
p = this._readdir(name);
this.readdirCache.set(name, p);
p = this._readdir(dirPath);
this.readdirCache.set(dirPath, p);
const directoryContent = await p;
const directoryFiles = new Set<string>();
for (const file of directoryContent) {
if (file.type === 'file') {
// we know this file exists, mark it as so on the filesystem
this.fileCache.set(file.path, Promise.resolve(true));
this.pathCache.set(file.path, Promise.resolve(true));
directoryFiles.add(file.name);
}
}
if (options?.potentialFiles) {
// calculate the set of paths that truly do not exist
const filesThatDoNotExist = options.potentialFiles.filter(
path => !directoryFiles.has(path)
);
for (const filePath of filesThatDoNotExist) {
const fullFilePath =
dirPath === '/' ? filePath : posixPath.join(dirPath, filePath);
// we know this file does not exist, mark it as so on the filesystem
this.fileCache.set(fullFilePath, Promise.resolve(false));
this.pathCache.set(fullFilePath, Promise.resolve(false));
}
}
}
return p;
};

View File

@@ -59,6 +59,19 @@ export const workspaceManagers: Array<
],
},
},
{
name: 'default',
slug: 'yarn',
detectors: {
every: [
{
path: 'package.json',
matchContent:
'"workspaces":\\s*(?:\\[[^\\]]*]|{[^}]*"packages":[^}]*})',
},
],
},
},
];
export default workspaceManagers;

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