Compare commits

...

41 Commits

Author SHA1 Message Date
Nathan Rajlich
84d859a016 Publish Canary
- @vercel/frameworks@0.2.1-canary.1
 - @vercel/build-utils@2.9.1-canary.1
 - vercel@21.2.4-canary.1
 - @vercel/client@9.0.7-canary.1
 - @vercel/routing-utils@1.9.3-canary.0
2021-02-11 14:42:07 -08:00
Nathan Rajlich
9238687089 [frameworks] Restore correct order of frameworks for detection (#5828) 2021-02-11 14:41:20 -08:00
Nathan Rajlich
212c91cf32 [frameworks] Add type annotations (#5819)
* [frameworks] Add type annotations

This will be necessary for static type analysis in our API docs generator.

* Apply suggestions from code review

Co-authored-by: Steven <steven@ceriously.com>

* Add quotes to @examples

Co-authored-by: Steven <steven@ceriously.com>
2021-02-11 11:42:13 -08:00
Nathan Rajlich
48b34f5b02 [frameworks] Add test case to ensure all "demo" URLs are public Vercel deployments (#5818)
All demo URLs must be public for the template import flow, so this test case ensures that we don't accidentally publish a demo URL that is not public.
2021-02-10 22:35:08 +00:00
Nathan Rajlich
b2c72538a3 [frameworks] Export frameworks array with as const (#5817)
This will be necessary for static type analysis in our API docs generator.

The default export is still typed as `Framework[]` for backwards-compat purposes, but the `frameworks` export is the statically typed array.
2021-02-10 21:40:05 +00:00
Nathan Rajlich
57d8702188 [build-utils][cli] Fix frameworks usage (#5811)
Fixes unit test failures like: https://github.com/vercel/vercel/runs/1867209532#step:11:191 from PR #5740
2021-02-10 04:14:02 +00:00
Allen Hai
9a72195d8a [routing-utils] add user boolean prop to route schema (#5788)
This PR adds a boolean `user` property to help determine which routes where added by the user and which routes were added by the platform.
2021-02-10 01:42:50 +00:00
Nathan Rajlich
29ab7f7cfb Publish Canary
- @vercel/frameworks@0.2.1-canary.0
 - @vercel/build-utils@2.9.1-canary.0
 - vercel@21.2.4-canary.0
 - @vercel/client@9.0.7-canary.0
2021-02-09 15:13:16 -08:00
Nathan Rajlich
ffa36c12d5 [frameworks] Convert to TypeScript (#5740)
This PR converts the `frameworks.json` file to TypeScript, and extends the values with the detection logic from `@vercel/static-build`, so that it's publicly editable. You also don't need to do the type casting downstream anymore.

As a consequence, it also makes Zola a 1st-class framework, as it was previously missing from the `frameworks.json` file, but present in the static-build frameworks. An example has been included based on their "Getting Started" tutorial.

CH-3808
CH-18771
2021-02-09 22:55:49 +00:00
Nathan Rajlich
d1fc729d31 [cli] Use sleep() util function in vc dev (#5784) 2021-02-08 17:20:47 -08:00
Nathan Rajlich
ba47ad00f9 [examples][tests] Update "gridsome" and "hexo" for Node 14 (#5802)
The version of `sharp` used in gridsome 0.6.0 does not compile on Node 14.
2021-02-08 19:02:39 -05:00
Steven
2b62de803f Publish Stable
- @vercel/build-utils@2.9.0
 - vercel@21.2.3
 - @vercel/client@9.0.6
2021-02-04 15:43:26 -05:00
Steven
196433ec9d Publish Canary
- @vercel/build-utils@2.8.1-canary.1
 - vercel@21.2.3-canary.0
 - @vercel/client@9.0.6-canary.4
2021-02-04 14:26:59 -05:00
Steven
0cdd571087 [build-utils] Add support for Node.js 14, warn for Node.js 10 (#5761)
### Related

Related to #4754 

### Upstream

https://aws.amazon.com/blogs/compute/node-js-14-x-runtime-now-available-in-aws-lambda/

### 📋 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

- [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
2021-02-04 14:25:16 -05:00
Steven
5e2227507d [docs] Fix links to GH Discussion categories (#5777) 2021-02-01 12:08:57 -05:00
Nathan Rajlich
e3d478a471 Publish Stable
- vercel@21.2.2
 - @vercel/go@1.1.8
2021-01-29 16:57:35 -08:00
Steven
187d41434a Publish Canary
- vercel@21.2.2-canary.1
2021-01-29 18:55:54 -05:00
Steven
f6fde2d351 [cli] Update error message when project is not found (#5770)
Both `vc` and `vc dev` already ask to link/setup when the project is not found, so this will update the error message to mention the possible reasons why a project was not found.

### Before

```
$ vc
Vercel CLI 21.2.1
️ Your project was either removed from Vercel or you’re not a member of it anymore.
? Set up and deploy “~/Code/myproject”? [Y/n]
```

### After

```
$ vc
Vercel CLI 21.2.1
️ Your Project was either deleted, transferred to a new Team, or you don’t have access to it anymore.
? Set up and deploy “~/Code/myproject”? [Y/n]
```


### 📋 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

- [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
2021-01-29 18:54:30 -05:00
Steven
e09ceca95d Publish Canary
- @vercel/build-utils@2.8.1-canary.0
 - @vercel/cgi@1.0.7-canary.0
 - vercel@21.2.2-canary.0
 - @vercel/client@9.0.6-canary.3
 - @vercel/go@1.1.8-canary.0
 - @vercel/node@1.9.1-canary.0
 - @vercel/python@1.2.5-canary.1
 - @vercel/ruby@1.2.6-canary.0
2021-01-29 12:40:38 -05:00
Igor Klopov
d9318af654 [go] support skipDownload flag (#5767) 2021-01-29 12:37:06 -05:00
Nathan Rajlich
679cd6f14e [cli] Use output.stopSpinner() interface consistently (#5762)
There was an issue with `output.spinner()` spinner if the old-style
interface was used (invoking the return value to stop the spinner),
which caused the internal spinner instance in `output` to be defined,
but in a "stopped" state. Then later the `text` gets updated to a
different value but the spinner is still stopped and nothing gets
rendered.

The fix is to ensure that `output.stopSpinner()` is called rather
than directly invoking the cancel function. In fact, the return
value of `output.spinner()` has been removed completely.
2021-01-28 14:16:17 -08:00
Nathan Rajlich
47c7a13324 [all] Rewrite build scripts in JavaScript (#5763)
Fixes these strange Windows failures that randomly started happening:

https://github.com/vercel/vercel/pull/5761/checks?check_run_id=1780742477
2021-01-28 19:20:07 +00:00
Nathan Rajlich
f498d25d8b Publish Stable
- vercel@21.2.1
2021-01-27 14:26:52 -08:00
Nathan Rajlich
726d3924ae Publish Canary
- vercel@21.2.1-canary.0
 - @vercel/python@1.2.5-canary.0
2021-01-26 18:31:12 -08:00
Nathan Rajlich
d3e6c2d335 [python] Fix request URL path encoding for basic, WSGI and ASGI interfaces (#5739)
`@vercel/python` has been incorrectly "unquoting" the incoming request
URL for all Python interfaces (basic, WSGI, ASGI). The proper behavior is
to pass along the request URL to the serverless function handler unmodified.

This is proven by the additional test that is added in this PR, which actually
fires up the corresponding Python server via i.e. `python3 api/asgi.py`,
and executes the test probes defined in the
`test/fixtures/00-request-path/vercel.json` file. Adding this test ensures
that the request URL behavior of `@vercel/python` matches the behavior
as compared to executing via `python` directly.
2021-01-26 12:37:15 -08:00
Nathan Rajlich
f9ed84a5c9 [cli] Pass the output object to vercel switch command (#5756)
Fixes error: `Cannot read property 'spinner' of undefined`
2021-01-26 12:34:08 -08:00
Nathan Rajlich
88a815b5f0 Publish Stable
- vercel@21.2.0
2021-01-25 14:44:41 -08:00
Steven
9f49743ea4 Publish Stable
- @vercel/build-utils@2.8.0
2021-01-22 18:53:38 -05:00
Steven
0ac3ae19c0 Publish Canary
- @vercel/build-utils@2.7.1-canary.2
 - vercel@21.1.1-canary.3
 - @vercel/client@9.0.6-canary.2
2021-01-22 18:48:09 -05:00
Steven
e7920fd783 [build-utils] Fix warning message (#5741) 2021-01-22 18:47:33 -05:00
Steven
b4c13470df Publish Canary
- @vercel/build-utils@2.7.1-canary.1
 - vercel@21.1.1-canary.2
 - @vercel/client@9.0.6-canary.1
2021-01-22 11:59:09 -05:00
Steven
2a797b77b9 [build-utils] Add support for config.nodeVersion (#5729)
### 📋 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
2021-01-22 11:57:55 -05:00
Nathan Rajlich
85a34126df Publish Canary
- vercel@21.1.1-canary.1
2021-01-21 22:48:10 -08:00
Nathan Rajlich
92889c5376 [cli] Re-use a single Output instance throughout the codebase (#5722)
Previously every command implementation was invoking `createOutput()` individually, causing multiple instances of `Output` to be created, which was unnecessary, and causes issues with the shared `spinner` instance introduced by #5717.

Now, the `Output` instance created in `src/index.js` is passed down to the sub-command being executed and therefore re-used.
2021-01-21 19:57:08 -08:00
Nathan Rajlich
66a7fa30b8 [cli] Show login prompt for vercel dev (#5718)
`vercel dev` requires the user to be logged in ever since the project needs to be queried from the database.

**Before:**

<img width="682" alt="Screen Shot 2021-01-18 at 7 58 56 PM" src="https://user-images.githubusercontent.com/71256/104986545-14aac800-59c8-11eb-8302-836a5481ce5f.png">

**After:**

<img width="497" alt="Screen Shot 2021-01-18 at 7 59 01 PM" src="https://user-images.githubusercontent.com/71256/104986547-15dbf500-59c8-11eb-85f6-d0ad628c8e8d.png">
2021-01-21 19:56:35 -08:00
Nathan Rajlich
a7ba405503 [cli] Make Output class a singleton and have reusable Spinner (#5717)
This is a refactor of the `ora` Spinner usage to make the preferred usage be via the `output.spinner()` and `output.stopSpinner()` functions.

* The `Output` instance has a local spinner instance that can be updated via multiple calls to `output.spinner()`.
* `output.print()` and friends call `output.stopSpinner()` implicitly, so `output.stopSpinner()` doesn't need to be called unless it is desired to immediately remove the spinner before rendering the next text.
* Because there's meant to be a shared `spinner` for the `output` instance, it means that the same `output` instance needs to be used throughout the CLI lifecycle. Therefore, the `createOutput()` function now returns a singleton so that it's guaranteed to be the same instance.

https://user-images.githubusercontent.com/71256/105009979-0670a200-59f0-11eb-9853-232f92eae536.mov

https://user-images.githubusercontent.com/71256/105010034-17211800-59f0-11eb-918d-b15f457e9640.mov

In the "before" video above, note how there's a brief second where the spinner is cleared, but the deployment URL has not yet been printed. In the "after" video, this is fixed and the spinner is only cleared once the CLI is ready to render the deployment URL.
2021-01-19 11:38:29 -08:00
luc
84145245ba Publish Canary
- @vercel/build-utils@2.7.1-canary.0
 - vercel@21.1.1-canary.0
 - @vercel/client@9.0.6-canary.0
2021-01-19 09:29:20 +01:00
Luc Leray
90d2e8b63b [cli] Hide "Development" for secret env variables (#5710) 2021-01-19 09:27:42 +01:00
Nathan Rajlich
0d31fe8018 [cli] Fix cleanUrls: true when using outputDirectory (#5701)
This is essentially a refactor of the `@vercel/static` builder that
simplifies the code a lot and fixes a bug where `cleanUrls: true`
was not working correctly when using an `outputDirectory`.
2021-01-18 12:03:34 -08:00
Mark Glagola
37d747c241 [cli] Add auto renew prompt to domains buy (#5663)
Add auto renew prompt to `domains buy`

```diff
 > The domain "example.com" is available to buy under <username>!
 > Buy now for $20 (1yr)? [y|N] 
+ > Auto renew yearly for $20? [Y|n] 
```

### 📋 Checklist

#### 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
2021-01-15 19:13:37 +00:00
Nathan Rajlich
3f052d905f [build-utils] Remove now-next and now-static-build tests (#5695)
These directories are no longer in this repository, so the tests were failing.
2021-01-14 13:50:19 +00:00
158 changed files with 5008 additions and 4096 deletions

View File

@@ -4,8 +4,8 @@ contact_links:
url: https://vercel.com/support/request
about: Report a bug using the Vercel support form
- name: Feature Request
url: https://github.com/vercel/vercel/discussions/new?category_id=66161
url: https://github.com/vercel/vercel/discussions/new?category=ideas
about: Share ideas for new features
- name: Ask a Question
url: https://github.com/vercel/vercel/discussions/new?category_id=66160
url: https://github.com/vercel/vercel/discussions/new?category=help
about: Ask the community for help

View File

@@ -11,7 +11,7 @@ on:
jobs:
test:
name: Dev
timeout-minutes: 45
timeout-minutes: 60
strategy:
fail-fast: false
matrix:

View File

@@ -12,6 +12,11 @@ const frameworks = (_frameworks as Framework[])
...frameworkItem,
detectors: undefined,
sort: undefined,
dependency: undefined,
defaultRoutes: undefined,
cachePattern: undefined,
devCommand: undefined,
buildCommand: undefined,
};
if (framework.logo) {

View File

@@ -5,7 +5,7 @@
"description": "API for the vercel/vercel repo",
"main": "index.js",
"scripts": {
"build": "yarn --cwd .. && node ../utils/run.js build all"
"vercel-build": "yarn --cwd .. && node ../utils/run.js build all"
},
"dependencies": {
"@sentry/node": "5.11.1",

View File

@@ -5,6 +5,7 @@
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"noEmitOnError": true,
"esModuleInterop": true,
"module": "commonjs",
"moduleResolution": "node",

View File

@@ -8,6 +8,6 @@
"explore": "gridsome explore"
},
"dependencies": {
"gridsome": "^0.6.0"
"gridsome": "0.7.23"
}
}

View File

@@ -3,18 +3,18 @@
"version": "0.0.0",
"private": true,
"hexo": {
"version": "3.9.0"
"version": "5.3.0"
},
"dependencies": {
"hexo": "^3.9.0",
"hexo-generator-archive": "^0.1.5",
"hexo-generator-category": "^0.1.3",
"hexo-generator-index": "^0.2.1",
"hexo-generator-tag": "^0.2.0",
"hexo-renderer-ejs": "^0.3.1",
"hexo-renderer-stylus": "^0.3.3",
"hexo-renderer-marked": "^1.0.1",
"hexo-server": "^0.3.3"
"hexo": "^5.3.0",
"hexo-generator-archive": "^1.0.0",
"hexo-generator-category": "^1.0.0",
"hexo-generator-index": "^2.0.0",
"hexo-generator-tag": "^1.0.0",
"hexo-renderer-ejs": "^1.0.0",
"hexo-renderer-marked": "^3.3.0",
"hexo-renderer-stylus": "^2.0.1",
"hexo-server": "^2.0.0"
},
"scripts": {
"dev": "hexo server -p $PORT",

2
examples/zola/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/public
/.vercel

16
examples/zola/config.toml Normal file
View File

@@ -0,0 +1,16 @@
# The URL the site will be built for
base_url = "/"
# Whether to automatically compile all Sass files in the sass directory
compile_sass = true
# Whether to build a search index to be used later on by a JavaScript library
build_search_index = false
[markdown]
# Whether to do syntax highlighting
# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
highlight_code = false
[extra]
# Put all your custom variables here

View File

@@ -0,0 +1,6 @@
+++
title = "List of blog posts"
sort_by = "date"
template = "blog.html"
page_template = "blog-page.html"
+++

View File

@@ -0,0 +1,6 @@
+++
title = "My first post"
date = 2019-11-27
+++
This is my first blog post.

View File

@@ -0,0 +1,6 @@
+++
title = "My second post"
date = 2019-11-28
+++
This is my second blog post.

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>MyBlog</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css">
</head>
<body>
<section class="section">
<div class="container">
{% block content %} {% endblock %}
</div>
</section>
</body>
</html>

View File

@@ -0,0 +1,9 @@
{% extends "base.html" %}
{% block content %}
<h1 class="title">
{{ page.title }}
</h1>
<p class="subtitle"><strong>{{ page.date }}</strong></p>
{{ page.content | safe }}
{% endblock content %}

View File

@@ -0,0 +1,12 @@
{% extends "base.html" %}
{% block content %}
<h1 class="title">
{{ section.title }}
</h1>
<ul>
{% for page in section.pages %}
<li><a href="{{ page.permalink | safe }}">{{ page.title }}</a></li>
{% endfor %}
</ul>
{% endblock content %}

View File

@@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block content %}
<h1 class="title">
This is my blog made with Zola.
</h1>
<p>Click <a href="/blog/">here</a> to see my posts.</p>
{% endblock content %}

View File

@@ -39,7 +39,7 @@
"publish-from-github": "./utils/publish.sh",
"changelog": "node utils/changelog.js",
"build": "node utils/run.js build all",
"now-build": "mkdir -p public && echo '<a href=\"https://vercel.com/import\">Import</a>' > public/output.html",
"vercel-build": "mkdir -p public && echo '<a href=\"https://vercel.com/import\">Import</a>' > public/output.html",
"test-unit": "node utils/run.js test-unit",
"test-integration-cli": "node utils/run.js test-integration-cli",
"test-integration-once": "node utils/run.js test-integration-once",

View File

@@ -1,945 +0,0 @@
[
{
"name": "Blitz.js",
"slug": "blitzjs",
"demo": "https://blitzjs.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/blitz.svg",
"tagline": "Blitz.js: The Fullstack React Framework",
"description": "A brand new Blitz.js app - the result of running `npx blitz new`.",
"website": "https://blitzjs.com",
"useRuntime": { "src": "package.json", "use": "@vercel/next" },
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"blitz\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `blitz build`"
},
"devCommand": {
"value": "blitz start"
},
"outputDirectory": {
"placeholder": "Next.js default"
}
}
},
{
"name": "Next.js",
"slug": "nextjs",
"demo": "https://nextjs.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/next.svg",
"tagline": "Next.js makes you productive with React instantly — whether you want to build static or dynamic sites.",
"description": "A Next.js app and a Serverless Function API.",
"website": "https://nextjs.org",
"sort": 1,
"useRuntime": { "src": "package.json", "use": "@vercel/next" },
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"next\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `next build`"
},
"devCommand": {
"value": "next dev --port $PORT"
},
"outputDirectory": {
"placeholder": "Next.js default"
}
},
"recommendedIntegrations": [
{
"id": "oac_5lUsiANun1DEzgLg0NZx5Es3",
"dependencies": ["next-plugin-sentry", "next-sentry-source-maps"]
}
]
},
{
"name": "Gatsby.js",
"slug": "gatsby",
"demo": "https://gatsby.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/gatsby.svg",
"tagline": "Gatsby helps developers build blazing fast websites and apps with React.",
"description": "A Gatsby app, using the default starter theme and a Serverless Function API.",
"website": "https://gatsbyjs.org",
"sort": 2,
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"gatsby\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `gatsby build`"
},
"devCommand": {
"value": "gatsby develop --port $PORT"
},
"outputDirectory": {
"value": "public"
}
}
},
{
"name": "Hexo",
"slug": "hexo",
"demo": "https://hexo.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/hexo.svg",
"tagline": "Hexo is a fast, simple & powerful blog framework powered by Node.js.",
"description": "A Hexo site, created with the Hexo CLI.",
"website": "https://hexo.io",
"sort": 3,
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"hexo\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `hexo generate`"
},
"devCommand": {
"value": "hexo server --port $PORT"
},
"outputDirectory": {
"value": "public"
}
}
},
{
"name": "Eleventy",
"slug": "eleventy",
"demo": "https://eleventy.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/eleventy.svg",
"tagline": "11ty is a simpler static site generator written in JavaScript, created to be an alternative to Jekyll.",
"description": "An Eleventy site, created with npm init.",
"website": "https://www.11ty.dev",
"sort": 4,
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@11ty\\/eleventy\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `npx @11ty/eleventy`"
},
"devCommand": {
"value": "npx @11ty/eleventy --serve --watch --port $PORT"
},
"outputDirectory": {
"value": "_site"
}
}
},
{
"name": "Docusaurus 2",
"slug": "docusaurus-2",
"demo": "https://docusaurus-2.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/docusaurus.svg",
"tagline": "Docusaurus makes it easy to maintain Open Source documentation websites.",
"description": "A static Docusaurus site that makes it easy to maintain OSS documentation.",
"website": "https://v2.docusaurus.io",
"detectors": {
"some": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@docusaurus\\/core\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `docusaurus build`"
},
"devCommand": {
"value": "docusaurus start --port $PORT"
},
"outputDirectory": {
"value": "build"
}
}
},
{
"name": "Docusaurus 1",
"slug": "docusaurus",
"demo": "https://docusaurus.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/docusaurus.svg",
"tagline": "Docusaurus makes it easy to maintain Open Source documentation websites.",
"description": "A static Docusaurus site that makes it easy to maintain OSS documentation.",
"website": "https://docusaurus.io/",
"detectors": {
"some": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"docusaurus\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `docusaurus-build`"
},
"devCommand": {
"value": "docusaurus-start --port $PORT"
},
"outputDirectory": {
"value": "build"
}
}
},
{
"name": "Preact",
"slug": "preact",
"demo": "https://preact.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/preact.svg",
"tagline": "Preact is a fast 3kB alternative to React with the same modern API.",
"description": "A Preact app, created with the Preact CLI.",
"website": "https://preactjs.com",
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"preact-cli\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `preact build`"
},
"devCommand": {
"value": "preact watch --port $PORT"
},
"outputDirectory": {
"value": "build"
}
}
},
{
"name": "Dojo",
"slug": "dojo",
"demo": "https://dojo.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/dojo.png",
"tagline": "Dojo is a modern progressive, TypeScript first framework.",
"description": "A Dojo app, created with the Dojo CLI's cli-create-app command.",
"website": "https://dojo.io",
"detectors": {
"some": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@dojo\\/framework\":\\s*\".+?\"[^}]*}"
},
{
"path": ".dojorc"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `dojo build`"
},
"devCommand": {
"value": "dojo build -m dev -w -s -p $PORT"
},
"outputDirectory": {
"value": "output/dist"
}
}
},
{
"name": "Ember.js",
"slug": "ember",
"demo": "https://ember.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/ember.svg",
"tagline": "Ember.js helps webapp developers be more productive out of the box.",
"description": "An Ember app, created with the Ember CLI.",
"website": "https://emberjs.com/",
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"ember-cli\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `ember build`"
},
"devCommand": {
"value": "ember serve --port $PORT"
},
"outputDirectory": {
"value": "dist"
}
}
},
{
"name": "Vue.js",
"slug": "vue",
"demo": "https://vue.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/vue.svg",
"tagline": "Vue.js is a versatile JavaScript framework that is as approachable as it is performant.",
"description": "A Vue.js app, created with the Vue CLI.",
"website": "https://vuejs.org",
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@vue\\/cli-service\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `vue-cli-service build`"
},
"devCommand": {
"value": "vue-cli-service serve --port $PORT"
},
"outputDirectory": {
"value": "dist"
}
}
},
{
"name": "Scully",
"slug": "scully",
"demo": "https://scully.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/scullyio-logo.png",
"tagline": "Scully is a static site generator for Angular.",
"description": "The Static Site Generator for Angular apps.",
"website": "https://github.com/scullyio/scully",
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@scullyio\\/init\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `ng build && scully`"
},
"devCommand": {
"value": "ng serve --port $PORT"
},
"outputDirectory": {
"value": "dist"
}
}
},
{
"name": "Ionic Angular",
"slug": "ionic-angular",
"demo": "https://ionic-angular.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/ionic.svg",
"tagline": "Ionic Angular allows you to build mobile PWAs with Angular and the Ionic Framework.",
"description": "An Ionic Angular site, created with the Ionic CLI.",
"website": "https://ionicframework.com",
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@ionic\\/angular\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `ng build`"
},
"devCommand": {
"value": "ng start"
},
"outputDirectory": {
"value": "www"
}
}
},
{
"name": "Angular",
"slug": "angular",
"demo": "https://angular.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/angular.svg",
"tagline": "Angular is a TypeScript-based cross-platform framework from Google.",
"description": "An Angular app, created with the Angular CLI.",
"website": "https://angular.io",
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@angular\\/cli\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `ng build`"
},
"devCommand": {
"value": "ng serve --port $PORT"
},
"outputDirectory": {
"value": "dist"
}
}
},
{
"name": "Polymer",
"slug": "polymer",
"demo": "https://polymer.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/polymer.svg",
"tagline": "Polymer is an open-source webapps library from Google, for building using Web Components.",
"description": "A Polymer app, created with the Polymer CLI.",
"website": "https://www.polymer-project.org/",
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"polymer-cli\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `polymer build`"
},
"devCommand": {
"value": "polymer serve --port $PORT"
},
"outputDirectory": {
"value": "build"
}
}
},
{
"name": "Svelte",
"slug": "svelte",
"demo": "https://svelte.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/svelte.svg",
"tagline": "Svelte lets you write high performance reactive apps with significantly less boilerplate. ",
"description": "A Svelte app, using the Svelte template, and a Serverless Function API.",
"website": "https://svelte.dev",
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"sirv-cli\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `rollup -c`"
},
"devCommand": {
"value": "sirv public --single --dev --port $PORT"
},
"outputDirectory": {
"value": "public"
}
}
},
{
"name": "Ionic React",
"slug": "ionic-react",
"demo": "https://ionic-react.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/ionic.svg",
"tagline": "Ionic React allows you to build mobile PWAs with React and the Ionic Framework.",
"description": "An Ionic React site, created with the Ionic CLI.",
"website": "https://ionicframework.com",
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@ionic\\/react\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `react-scripts build`"
},
"devCommand": {
"value": "react-scripts start"
},
"outputDirectory": {
"value": "build"
}
}
},
{
"name": "Create React App",
"slug": "create-react-app",
"demo": "https://react-functions.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/react.svg",
"tagline": "Create React App allows you to get going with React in no time.",
"description": "A React app, bootstrapped with create-react-app, and a Serverless Function API.",
"website": "https://create-react-app.dev",
"detectors": {
"some": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"react-scripts\":\\s*\".+?\"[^}]*}"
},
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"react-dev-utils\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `react-scripts build`"
},
"devCommand": {
"value": "react-scripts start"
},
"outputDirectory": {
"value": "build"
}
}
},
{
"name": "Gridsome",
"slug": "gridsome",
"demo": "https://gridsome.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/gridsome.svg",
"tagline": "Gridsome is a Vue.js-powered framework for building websites & apps that are fast by default.",
"description": "A Gridsome app, created with the Gridsome CLI.",
"website": "https://gridsome.org/",
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"gridsome\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `gridsome build`"
},
"devCommand": {
"value": "gridsome develop -p $PORT"
},
"outputDirectory": {
"value": "dist"
}
}
},
{
"name": "UmiJS",
"slug": "umijs",
"demo": "https://umijs.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/umi.svg",
"tagline": "UmiJS is an extensible enterprise-level React application framework.",
"description": "An UmiJS app, created using the Umi CLI.",
"website": "https://umijs.org",
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"umi\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `umi build`"
},
"devCommand": {
"value": "umi dev --port $PORT"
},
"outputDirectory": {
"value": "dist"
}
}
},
{
"name": "Sapper",
"slug": "sapper",
"demo": "https://sapper.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/svelte.svg",
"tagline": "Sapper is a framework for building high-performance universal web apps with Svelte.",
"description": "A Sapper app, using the Sapper template.",
"website": "https://sapper.svelte.dev",
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"sapper\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `sapper export`"
},
"devCommand": {
"value": "sapper dev --port $PORT"
},
"outputDirectory": {
"value": "__sapper__/export"
}
}
},
{
"name": "Saber",
"slug": "saber",
"demo": "https://saber.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/saber.svg",
"tagline": "Saber is a framework for building static sites in Vue.js that supports data from any source.",
"description": "A Saber site, created with npm init.",
"website": "https://saber.land/",
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"saber\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `saber build`"
},
"devCommand": {
"value": "saber --port $PORT"
},
"outputDirectory": {
"value": "public"
}
}
},
{
"name": "Stencil",
"slug": "stencil",
"demo": "https://stencil.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/stencil.svg",
"tagline": "Stencil is a powerful toolchain for building Progressive Web Apps and Design Systems.",
"description": "A Stencil site, created with the Stencil CLI.",
"website": "https://stenciljs.com/",
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@stencil\\/core\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `stencil build`"
},
"devCommand": {
"value": "stencil build --dev --watch --serve --port $PORT"
},
"outputDirectory": {
"value": "www"
}
}
},
{
"name": "Nuxt.js",
"slug": "nuxtjs",
"demo": "https://nuxtjs.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/nuxt.svg",
"tagline": "Nuxt.js is the web comprehensive framework that lets you dream big with Vue.js.",
"description": "A Nuxt.js app, bootstrapped with create-nuxt-app.",
"website": "https://nuxtjs.org",
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"nuxt(-edge)?\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `nuxt generate`"
},
"devCommand": {
"value": "nuxt"
},
"outputDirectory": {
"value": "dist"
}
}
},
{
"name": "RedwoodJS",
"slug": "redwoodjs",
"demo": "https://redwoodjs.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/redwoodjs.svg",
"tagline": "RedwoodJS is a full-stack framework for the Jamstack.",
"description": "A RedwoodJS app, bootstraped with create-redwood-app.",
"website": "https://redwoodjs.com",
"useRuntime": { "src": "package.json", "use": "@vercel/redwood" },
"ignoreRuntimes": ["@vercel/node"],
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@redwoodjs\\/core\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"value": "yarn rw build && yarn rw db up --no-db-client --auto-approve && yarn rw dataMigrate up"
},
"devCommand": {
"value": "yarn rw dev --fwd=\"--port=$PORT --open=false\""
},
"outputDirectory": {
"placeholder": "RedwoodJS default"
}
}
},
{
"name": "Hugo",
"slug": "hugo",
"demo": "https://hugo.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/hugo.svg",
"tagline": "Hugo is the worlds fastest framework for building websites, written in Go.",
"description": "A Hugo site, created with the Hugo CLI.",
"website": "https://gohugo.io",
"sort": 5,
"detectors": {
"some": [
{
"path": "config.yaml"
},
{
"path": "config.toml"
},
{
"path": "config.json"
}
]
},
"settings": {
"installCommand": {
"placeholder": "None"
},
"buildCommand": {
"placeholder": "`npm run build` or `hugo -D --gc`"
},
"devCommand": {
"value": "hugo server -D -w -p $PORT"
},
"outputDirectory": {
"placeholder": "`public` or `publishDir` from the `config` file"
}
}
},
{
"name": "Jekyll",
"slug": "jekyll",
"demo": "https://jekyll.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/jekyll.svg",
"tagline": "Jekyll makes it super easy to transform your plain text into static websites and blogs.",
"description": "A Jekyll site, created with the Jekyll CLI.",
"website": "https://jekyllrb.com/",
"detectors": {
"every": [
{
"path": "_config.yml"
}
]
},
"settings": {
"installCommand": {
"value": "bundle install"
},
"buildCommand": {
"placeholder": "`npm run build` or `jekyll build`"
},
"devCommand": {
"value": "bundle exec jekyll serve --watch --port $PORT"
},
"outputDirectory": {
"placeholder": "`_site` or `destination` from `_config.yml`"
}
}
},
{
"name": "Brunch",
"slug": "brunch",
"demo": "https://brunch.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/brunch.svg",
"tagline": "Brunch is a fast and simple webapp build tool with seamless incremental compilation for rapid development.",
"description": "A Brunch app, created with the Brunch CLI.",
"website": "https://brunch.io/",
"detectors": {
"every": [
{
"path": "brunch-config.js"
}
]
},
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run build` or `brunch build --production`"
},
"devCommand": {
"value": "brunch watch --server --port $PORT"
},
"outputDirectory": {
"value": "public"
}
}
},
{
"name": "Middleman",
"slug": "middleman",
"demo": "https://middleman.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/middleman.svg",
"tagline": "Middleman is a static site generator that uses all the shortcuts and tools in modern web development.",
"description": "A Middleman app, created with the Middleman CLI.",
"website": "https://middlemanapp.com/",
"detectors": {
"every": [
{
"path": "config.rb"
}
]
},
"settings": {
"installCommand": {
"value": "bundle install"
},
"buildCommand": {
"value": "`npm run build` or `bundle exec middleman build`"
},
"devCommand": {
"value": "bundle exec middleman server -p $PORT"
},
"outputDirectory": {
"value": "build"
}
}
},
{
"name": "Other",
"slug": null,
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/other.svg",
"description": "No framework or a unoptimized framework.",
"settings": {
"installCommand": {
"placeholder": "`yarn install` or `npm install`"
},
"buildCommand": {
"placeholder": "`npm run vercel-build` or `npm run build`"
},
"devCommand": {
"placeholder": "None"
},
"outputDirectory": {
"placeholder": "`public` if it exists, or `.`"
}
}
}
]

View File

@@ -1,38 +0,0 @@
export interface FrameworkDetectionItem {
path: string;
matchContent?: string;
}
export interface SettingPlaceholder {
placeholder: string;
}
export interface SettingValue {
value: string;
}
export type Setting = SettingValue | SettingPlaceholder;
export interface Framework {
name: string;
slug: string | null;
logo: string;
demo?: string;
tagline?: string;
website?: string;
description: string;
sort?: number;
useRuntime?: { src: string; use: string };
ignoreRuntimes?: string[];
detectors?: {
every?: FrameworkDetectionItem[];
some?: FrameworkDetectionItem[];
};
settings: {
buildCommand: Setting;
devCommand: Setting;
outputDirectory: Setting;
};
recommendedIntegrations?: {
id: string;
dependencies: string[];
}[];
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -1,14 +1,26 @@
{
"name": "@vercel/frameworks",
"version": "0.2.0",
"main": "frameworks.json",
"version": "0.2.1-canary.1",
"main": "./dist/frameworks.js",
"types": "./dist/frameworks.d.ts",
"files": [
"dist"
],
"license": "UNLICENSED",
"scripts": {
"build": "tsc",
"test-unit": "jest --env node --verbose --runInBand --bail"
},
"dependencies": {
"@iarna/toml": "2.2.3",
"js-yaml": "3.13.1"
},
"devDependencies": {
"@types/jest": "24.0.22",
"@types/js-yaml": "3.12.1",
"@types/node": "12.0.4",
"@types/node-fetch": "2.5.8",
"@vercel/routing-utils": "1.9.3-canary.0",
"ajv": "6.12.2",
"jest": "24.9.0",
"ts-jest": "24.1.0",

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
import yaml from 'js-yaml';
import toml from '@iarna/toml';
import { promises } from 'fs';
const { readFile } = promises;
async function readFileOrNull(file: string) {
try {
const data = await readFile(file);
return data;
} catch (err) {
if (err.code !== 'ENOENT') {
throw err;
}
}
return null;
}
export async function readConfigFile<T>(
files: string | string[]
): Promise<T | null> {
files = Array.isArray(files) ? files : [files];
for (const name of files) {
const data = await readFileOrNull(name);
if (data) {
const str = data.toString('utf8');
if (name.endsWith('.json')) {
return JSON.parse(str) as T;
} else if (name.endsWith('.toml')) {
return (toml.parse(str) as unknown) as T;
} else if (name.endsWith('.yaml') || name.endsWith('.yml')) {
return yaml.safeLoad(str, { filename: name }) as T;
}
}
}
return null;
}

View File

@@ -0,0 +1,173 @@
import { Route } from '@vercel/routing-utils';
export interface FrameworkDetectionItem {
/**
* A file path
* @example "package.json"
*/
path: string;
/**
* A matcher
* @example "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"next\":\\s*\".+?\"[^}]*}"
*/
matchContent?: string;
}
export interface SettingPlaceholder {
/**
* A placeholder value for when the framework has not a predefined one
* @example "`npm run build` or `next build`"
*/
placeholder: string;
}
export interface SettingValue {
/**
* A predefined setting for the detected framework
* @example "next dev --port $PORT"
*/
value: string;
}
export type Setting = SettingValue | SettingPlaceholder;
/**
* Framework detection information.
*/
export interface Framework {
/**
* Name of the framework
* @example "Next.js"
*/
name: string;
/**
* A unique identifier for the framework
* @example "nextjs"
*/
slug: string | null;
/**
* A URL to the logo of the framework
* @example "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/next.svg"
*/
logo: string;
/**
* A URL to a deployed example of the framework
* @example "https://nextjs.now-examples.vercel.app"
*/
demo?: string;
/**
* A marketing tagline for the framework
* @example "Next.js makes you productive with React instantly — whether you want to build static or dynamic sites."
*/
tagline?: string;
/**
* A URL to the official website of the framework
* @example "https://nextjs.org"
*/
website?: string;
/**
* Short description of the framework
* @example "A Next.js app and a Serverless Function API."
*/
description: string;
/**
* A ponderated value to sort matching frameworks
* @example 1
*/
sort?: number;
/**
* Runtime configuration required to run the framework in Vercel
*/
useRuntime?: {
/**
* Runtime source
* @example "package.json"
*/
src: string;
/**
* Runtime
* @example "@vercel/next"
*/
use: string;
};
ignoreRuntimes?: string[];
/**
* Detectors used to find out the framework
*/
detectors?: {
/**
* Collection of detectors that must be matched for the framework
* to be detected.
*/
every?: FrameworkDetectionItem[];
/**
* Collection of detectors where one match triggers the framework
* to be detected.
*/
some?: FrameworkDetectionItem[];
};
settings: {
/**
* Default Install Command or a placeholder
*/
installCommand: Setting;
/**
* Default Build Command or a placeholder
*/
buildCommand: Setting;
/**
* Default Development Command or a placeholder
*/
devCommand: Setting;
/**
* Default Output Directory
*/
outputDirectory: Setting;
};
/**
* A list of recommended integrations for the framework
*/
recommendedIntegrations?: {
/**
* Id of the recommended integration
* @example "oac_5lUsiANun1DEzgLg0NZx5Es3"
*/
id: string;
/**
* Dependencies of the recommended integration
* @example ["next-plugin-sentry", "next-sentry-source-maps"]
*/
dependencies: string[];
}[];
/**
* Name of a dependency in `package.json` to detect this framework.
* @example "hexo"
*/
dependency?: string;
/**
* Function that returns the name of the directory that the framework outputs
* its build results to. In some cases this is read from a configuration file.
*/
getOutputDirName: (dirPrefix: string) => Promise<string>;
/**
* An array (or a function that returns an array) of default `Route` rules that
* the framework uses.
* @example [{ handle: 'filesystem' }, { src: '.*', status: 404, dest: '404.html' }]
*/
defaultRoutes?: Route[] | ((dirPrefix: string) => Promise<Route[]>);
/**
* A glob string of files to cache for future deployments.
* @example ".cache/**"
*/
cachePattern?: string;
/**
* The default build command for the framework.
* @example "next build"
*/
buildCommand: string | null;
/**
* The default development command for the framework.
* @example "next dev"
*/
devCommand: string | null;
}

View File

@@ -1,9 +1,11 @@
import Ajv from 'ajv';
import assert from 'assert';
import { join } from 'path';
import { existsSync } from 'fs';
import { isString } from 'util';
import { Framework } from '../';
const frameworkList = require('../frameworks.json') as Framework[];
import fetch from 'node-fetch';
import { URL, URLSearchParams } from 'url';
import frameworkList from '../src/frameworks';
const SchemaFrameworkDetectionItem = {
type: 'array',
@@ -53,8 +55,15 @@ const Schema = {
type: 'array',
items: {
type: 'object',
required: ['name', 'slug', 'logo', 'description', 'settings'],
additionalProperties: false,
required: [
'name',
'slug',
'logo',
'description',
'settings',
'buildCommand',
'devCommand',
],
properties: {
name: { type: 'string' },
slug: { type: ['string', 'null'] },
@@ -122,10 +131,25 @@ const Schema = {
},
},
},
dependency: { type: 'string' },
cachePattern: { type: 'string' },
buildCommand: { type: ['string', 'null'] },
devCommand: { type: ['string', 'null'] },
},
},
};
async function getDeployment(host: string) {
const query = new URLSearchParams();
query.set('url', host);
const res = await fetch(
`https://api.vercel.com/v11/deployments/get?${query}`
);
const body = await res.json();
return body;
}
describe('frameworks', () => {
it('ensure there is an example for every framework', async () => {
const root = join(__dirname, '..', '..', '..');
@@ -173,4 +197,30 @@ describe('frameworks', () => {
}
});
});
it('ensure unique slug', async () => {
const slugs = new Set<string>();
for (const { slug } of frameworkList) {
if (typeof slug === 'string') {
assert(!slugs.has(slug), `Slug "${slug}" is not unique`);
slugs.add(slug);
}
}
});
it('ensure all demo URLs are "public"', async () => {
await Promise.all(
frameworkList
.filter(f => typeof f.demo === 'string')
.map(async f => {
const url = new URL(f.demo!);
const deployment = await getDeployment(url.hostname);
assert.equal(
deployment.public,
true,
`Demo URL ${f.demo} is not "public"`
);
})
);
});
});

View File

@@ -15,6 +15,6 @@
"strict": true,
"target": "esnext"
},
"include": ["src/**/*", "test/**/*"],
"include": ["src/*.ts"],
"exclude": ["node_modules"]
}

View File

@@ -0,0 +1,30 @@
#!/usr/bin/env node
const fs = require('fs-extra');
const execa = require('execa');
const { join } = require('path');
async function main() {
const outDir = join(__dirname, 'dist');
// Start fresh
await fs.remove(outDir);
// Compile TypeScript
await execa('tsc', [], { stdio: 'inherit' });
// Run `ncc`
const mainDir = join(outDir, 'main');
await execa('ncc', ['build', 'src/index.ts', '-o', mainDir], {
stdio: 'inherit',
});
// Move compiled ncc file to out dir
await fs.rename(join(mainDir, 'index.js'), join(outDir, 'index.js'));
// Delete leftover "main" dir
await fs.remove(mainDir);
}
main().catch(err => {
console.error(err);
process.exit(1);
});

View File

@@ -1,13 +0,0 @@
#!/bin/bash
set -euo pipefail
out="dist"
rm -rf "$out"
tsc
rm "$out/index.js"
ncc build "src/index.ts" -o "$out/main"
mv "$out/main/index.js" "$out/index.js"
rm -rf "$out/main"

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "2.7.0",
"version": "2.9.1-canary.1",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -11,10 +11,10 @@
"directory": "packages/now-build-utils"
},
"scripts": {
"build": "./build.sh",
"build": "node build",
"test-unit": "jest --env node --verbose --runInBand --bail test/unit.*test.*",
"test-integration-once": "jest --env node --verbose --runInBand --bail test/integration.test.js",
"prepublishOnly": "./build.sh"
"prepublishOnly": "node build"
},
"devDependencies": {
"@iarna/toml": "2.2.3",
@@ -29,7 +29,7 @@
"@types/node-fetch": "^2.1.6",
"@types/semver": "6.0.0",
"@types/yazl": "^2.4.1",
"@vercel/frameworks": "0.2.0",
"@vercel/frameworks": "0.2.1-canary.1",
"@vercel/ncc": "0.24.0",
"aggregate-error": "3.0.1",
"async-retry": "1.2.3",

View File

@@ -2,10 +2,9 @@ import minimatch from 'minimatch';
import { valid as validSemver } from 'semver';
import { parse as parsePath, extname } from 'path';
import { Route, Source } from '@vercel/routing-utils';
import _frameworks, { Framework } from '@vercel/frameworks';
import frameworkList, { Framework } from '@vercel/frameworks';
import { PackageJson, Builder, Config, BuilderFunctions } from './types';
import { isOfficialRuntime } from './';
const frameworkList = _frameworks as Framework[];
const slugToFramework = new Map<string | null, Framework>(
frameworkList.map(f => [f.slug, f])
);

View File

@@ -4,28 +4,34 @@ import { NodeVersion } from '../types';
import { NowBuildError } from '../errors';
import debug from '../debug';
const allOptions: NodeVersion[] = [
const allOptions = [
{ major: 14, range: '14.x', runtime: 'nodejs14.x' },
{ major: 12, range: '12.x', runtime: 'nodejs12.x' },
{ major: 10, range: '10.x', runtime: 'nodejs10.x' },
{
major: 10,
range: '10.x',
runtime: 'nodejs10.x',
discontinueDate: new Date('2021-03-30'),
},
{
major: 8,
range: '8.10.x',
runtime: 'nodejs8.10',
discontinueDate: new Date('2020-01-06'),
},
];
] as const;
const pleaseSet =
'Please set "engines": { "node": "' +
'Please change your Project Settings or set "engines": { "node": "' +
getLatestNodeVersion().range +
'" } in your `package.json` file to upgrade to Node.js ' +
'" } in your `package.json` file to use Node.js ' +
getLatestNodeVersion().major +
'.';
const upstreamProvider =
'This change is the result of a decision made by an upstream infrastructure provider (AWS).' +
'\nRead more: https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html';
export function getLatestNodeVersion(): NodeVersion {
export function getLatestNodeVersion() {
return allOptions[0];
}
@@ -37,7 +43,7 @@ export async function getSupportedNodeVersion(
engineRange?: string,
isAuto?: boolean
): Promise<NodeVersion> {
let selection = getLatestNodeVersion();
let selection: NodeVersion = getLatestNodeVersion();
if (engineRange) {
const found =

View File

@@ -165,8 +165,8 @@ export function getSpawnOptions(
export async function getNodeVersion(
destPath: string,
_nodeVersion?: string,
_config?: Config,
meta?: Meta
config: Config = {},
meta: Meta = {}
): Promise<NodeVersion> {
if (meta && meta.isDev) {
// Use the system-installed version of `node` in PATH for `vercel dev`
@@ -174,13 +174,22 @@ export async function getNodeVersion(
return { ...latest, runtime: 'nodejs' };
}
const { packageJson } = await scanParentDirs(destPath, true);
let range: string | undefined;
let { nodeVersion } = config;
let isAuto = true;
if (packageJson && packageJson.engines && packageJson.engines.node) {
range = packageJson.engines.node;
if (
nodeVersion &&
nodeVersion !== packageJson.engines.node &&
!meta.isDev
) {
console.warn(
'Warning: Due to `engines` existing in your `package.json` file, the Node.js Version defined in your Project Settings will not apply. Learn More: http://vercel.link/node-version'
);
}
nodeVersion = packageJson.engines.node;
isAuto = false;
}
return getSupportedNodeVersion(range, isAuto);
return getSupportedNodeVersion(nodeVersion, isAuto);
}
async function scanParentDirs(destPath: string, readPackageJson = false) {

View File

@@ -39,7 +39,7 @@ for (const fixture of fs.readdirSync(fixturesPath)) {
// few foreign tests
const buildersToTestWith = ['now-next', 'now-node', 'now-static-build'];
const buildersToTestWith = ['now-node'];
// eslint-disable-next-line no-restricted-syntax
for (const builder of buildersToTestWith) {

View File

@@ -0,0 +1,6 @@
{
"private": true,
"engines": {
"node": "14.x"
}
}

View File

@@ -1,14 +1,6 @@
import path from 'path';
import { readFileSync } from 'fs-extra';
import { Framework } from '@vercel/frameworks';
import frameworkList from '@vercel/frameworks';
import { detectFramework, DetectorFilesystem } from '../src';
const frameworkList = JSON.parse(
readFileSync(
path.join(__dirname, '..', '..', 'frameworks', 'frameworks.json')
).toString()
) as Framework[];
class VirtualFilesystem extends DetectorFilesystem {
private files: Map<string, Buffer>;

View File

@@ -24,6 +24,19 @@ async function expectBuilderError(promise, pattern) {
);
}
let warningMessages;
const originalConsoleWarn = console.warn;
beforeEach(() => {
warningMessages = [];
console.warn = m => {
warningMessages.push(m);
};
});
afterEach(() => {
console.warn = originalConsoleWarn;
});
it('should re-create symlinks properly', async () => {
if (process.platform === 'win32') {
console.log('Skipping test on windows');
@@ -81,6 +94,10 @@ it('should only match supported node versions', async () => {
'major',
12
);
expect(await getSupportedNodeVersion('14.x', false)).toHaveProperty(
'major',
14
);
expect(getSupportedNodeVersion('8.11.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('6.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('999.x', false)).rejects.toThrow();
@@ -104,6 +121,10 @@ it('should only match supported node versions', async () => {
'major',
12
);
expect(await getSupportedNodeVersion('14.x', true)).toHaveProperty(
'major',
14
);
const foundMessage = /Found `engines` in `package\.json` with an invalid Node\.js version range/;
await expectBuilderError(
getSupportedNodeVersion('8.11.x', false),
@@ -125,8 +146,8 @@ it('should match all semver ranges', async () => {
// See https://docs.npmjs.com/files/package.json#engines
expect(await getSupportedNodeVersion('10.0.0')).toHaveProperty('major', 10);
expect(await getSupportedNodeVersion('10.x')).toHaveProperty('major', 10);
expect(await getSupportedNodeVersion('>=10')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('>=10.3.0')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('>=10')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('>=10.3.0')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('8.5.0 - 10.5.0')).toHaveProperty(
'major',
10
@@ -150,20 +171,57 @@ it('should ignore node version in vercel dev getNodeVersion()', async () => {
).toHaveProperty('runtime', 'nodejs');
});
it('should select project setting from config when no package.json is found', async () => {
expect(
await getNodeVersion('/tmp', undefined, { nodeVersion: '14.x' }, {})
).toHaveProperty('range', '14.x');
expect(warningMessages).toStrictEqual([]);
});
it('should prefer package.json engines over project setting from config and warn', async () => {
expect(
await getNodeVersion(
path.join(__dirname, 'pkg-engine-node'),
undefined,
{ nodeVersion: '12.x' },
{}
)
).toHaveProperty('range', '14.x');
expect(warningMessages).toStrictEqual([
'Warning: Due to `engines` existing in your `package.json` file, the Node.js Version defined in your Project Settings will not apply. Learn More: http://vercel.link/node-version',
]);
});
it('should not warn when package.json engines matches project setting from config', async () => {
expect(
await getNodeVersion(
path.join(__dirname, 'pkg-engine-node'),
undefined,
{ nodeVersion: '14.x' },
{}
)
).toHaveProperty('range', '14.x');
expect(warningMessages).toStrictEqual([]);
});
it('should get latest node version', async () => {
expect(await getLatestNodeVersion()).toHaveProperty('major', 12);
expect(await getLatestNodeVersion()).toHaveProperty('major', 14);
});
it('should throw for discontinued versions', async () => {
// Mock a future date so that Node 8 becomes discontinued
const realDateNow = Date.now.bind(global.Date);
global.Date.now = () => new Date('2020-02-14').getTime();
global.Date.now = () => new Date('2021-04-01').getTime();
expect(getSupportedNodeVersion('8.10.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('8.10.x', true)).rejects.toThrow();
expect(getSupportedNodeVersion('10.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('10.x', true)).rejects.toThrow();
expect(getDiscontinuedNodeVersions().length).toBe(1);
expect(getDiscontinuedNodeVersions()[0]).toHaveProperty('range', '8.10.x');
const discontinued = getDiscontinuedNodeVersions();
expect(discontinued.length).toBe(2);
expect(discontinued[0]).toHaveProperty('range', '10.x');
expect(discontinued[1]).toHaveProperty('range', '8.10.x');
global.Date.now = realDateNow;
});

25
packages/now-cgi/build.js Normal file
View File

@@ -0,0 +1,25 @@
#!/usr/bin/env node
const execa = require('execa');
const { join } = require('path');
const { homedir } = require('os');
async function main() {
process.env.GOOS = 'linux';
process.env.GOARCH = 'amd64';
process.env.GOPATH = join(homedir(), 'go');
await execa('go', ['get', 'github.com/aws/aws-lambda-go/events'], {
stdio: 'inherit',
});
await execa('go', ['get', 'github.com/aws/aws-lambda-go/lambda'], {
stdio: 'inherit',
});
await execa('go', ['build', '-o', 'handler', 'main.go'], {
stdio: 'inherit',
});
}
main().catch(err => {
console.error(err);
process.exit(1);
});

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env bash
export GOOS=linux
export GOARCH=amd64
export GOPATH=$HOME/go
go get github.com/aws/aws-lambda-go/events
go get github.com/aws/aws-lambda-go/lambda
go build -o handler main.go

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/cgi",
"version": "1.0.6",
"version": "1.0.7-canary.0",
"license": "MIT",
"repository": {
"type": "git",
@@ -8,7 +8,8 @@
"directory": "packages/now-cgi"
},
"scripts": {
"prepublish": "./build.sh"
"build": "node build",
"prepublishOnly": "node build"
},
"files": [
"index.js",

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "21.1.0",
"version": "21.2.4-canary.1",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -61,11 +61,11 @@
"node": ">= 10"
},
"dependencies": {
"@vercel/build-utils": "2.7.0",
"@vercel/go": "1.1.7",
"@vercel/node": "1.9.0",
"@vercel/python": "1.2.4",
"@vercel/ruby": "1.2.5",
"@vercel/build-utils": "2.9.1-canary.1",
"@vercel/go": "1.1.8",
"@vercel/node": "1.9.1-canary.0",
"@vercel/python": "1.2.5-canary.1",
"@vercel/ruby": "1.2.6-canary.0",
"update-notifier": "4.1.0"
},
"devDependencies": {
@@ -100,7 +100,7 @@
"@types/universal-analytics": "0.4.2",
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@vercel/frameworks": "0.2.0",
"@vercel/frameworks": "0.2.1-canary.1",
"@vercel/ncc": "0.24.0",
"@zeit/fun": "0.11.2",
"@zeit/source-map-support": "0.6.2",

View File

@@ -2,7 +2,6 @@ import chalk from 'chalk';
import { handleError } from '../../util/error';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import logo from '../../util/output/logo';
@@ -92,15 +91,14 @@ export default async function main(ctx) {
return 2;
}
const output = createOutput({ debug: argv['--debug'] });
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
switch (subcommand) {
case 'ls':
return ls(ctx, argv, args, output);
return ls(ctx, argv, args);
case 'rm':
return rm(ctx, argv, args, output);
return rm(ctx, argv, args);
default:
return set(ctx, argv, args, output);
return set(ctx, argv, args);
}
}

View File

@@ -10,9 +10,10 @@ import strlen from '../../util/strlen.ts';
import getCommandFlags from '../../util/get-command-flags';
import { getCommandName } from '../../util/pkg-name.ts';
export default async function ls(ctx, opts, args, output) {
export default async function ls(ctx, opts, args) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
@@ -23,6 +24,7 @@ export default async function ls(ctx, opts, args, output) {
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;
@@ -47,9 +49,9 @@ export default async function ls(ctx, opts, args, output) {
token,
debug: debugEnabled,
currentTeam,
output,
});
const lsStamp = stamp();
let cancelWait;
if (args.length > 0) {
output.error(
@@ -60,17 +62,13 @@ export default async function ls(ctx, opts, args, output) {
return 1;
}
cancelWait = output.spinner(
`Fetching aliases under ${chalk.bold(contextName)}`
);
output.spinner(`Fetching aliases under ${chalk.bold(contextName)}`);
const { aliases, pagination } = await getAliases(
now,
undefined,
nextTimestamp
);
if (cancelWait) cancelWait();
output.log(`aliases found under ${chalk.bold(contextName)} ${lsStamp()}`);
console.log(printAliasTable(aliases));

View File

@@ -12,9 +12,10 @@ import { isValidName } from '../../util/is-valid-name';
import findAliasByAliasOrId from '../../util/alias/find-alias-by-alias-or-id';
import { getCommandName } from '../../util/pkg-name.ts';
export default async function rm(ctx, opts, args, output) {
export default async function rm(ctx, opts, args) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
@@ -25,6 +26,7 @@ export default async function rm(ctx, opts, args, output) {
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;
@@ -39,8 +41,13 @@ export default async function rm(ctx, opts, args, output) {
throw err;
}
// $FlowFixMe
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
currentTeam,
output,
});
const [aliasOrId] = args;
if (args.length !== 1) {

View File

@@ -29,11 +29,11 @@ type Options = {
export default async function set(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
localConfig,
} = ctx;
@@ -49,6 +49,7 @@ export default async function set(
token,
currentTeam,
debug: debugEnabled,
output,
});
let user: User;

View File

@@ -14,7 +14,6 @@ import addBilling from './add';
import exit from '../../util/exit';
import Client from '../../util/client.ts';
import getScope from '../../util/get-scope.ts';
import createOutput from '../../util/output';
import { getPkgName } from '../../util/pkg-name.ts';
const help = () => {
@@ -90,8 +89,9 @@ function buildInquirerChoices(cards) {
const _default =
source.id === cards.defaultSource ? ` ${chalk.bold('(default)')}` : '';
const id = `${chalk.cyan(`ID: ${source.id}`)}${_default}`;
const number = `${chalk.gray('#### ').repeat(3)}${source.last4 ||
source.card.last4}`;
const number = `${chalk.gray('#### ').repeat(3)}${
source.last4 || source.card.last4
}`;
const str = [
id,
indent(source.name || source.owner.name, 2),
@@ -106,11 +106,16 @@ function buildInquirerChoices(cards) {
});
}
async function run({ token, config: { currentTeam } }) {
async function run({ token, output, config: { currentTeam } }) {
const start = new Date();
const creditCards = new NowCreditCards({ apiUrl, token, debug, currentTeam });
const output = createOutput({ debug });
const client = new Client({ apiUrl, token, currentTeam, debug });
const creditCards = new NowCreditCards({
apiUrl,
token,
debug,
currentTeam,
output,
});
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {
@@ -147,8 +152,9 @@ async function run({ token, config: { currentTeam } }) {
const id = `${chalk.gray('-')} ${chalk.cyan(
`ID: ${source.id}`
)}${_default}`;
const number = `${chalk.gray('#### ').repeat(3)}${source.last4 ||
source.card.last4}`;
const number = `${chalk.gray('#### ').repeat(3)}${
source.last4 || source.card.last4
}`;
return [
id,
@@ -231,8 +237,9 @@ async function run({ token, config: { currentTeam } }) {
const elapsed = ms(new Date() - start);
console.log(
success(
`${card.brand || card.card.brand} ending in ${card.last4 ||
card.card.last4} is now the default ${chalk.gray(`[${elapsed}]`)}`
`${card.brand || card.card.brand} ending in ${
card.last4 || card.card.last4
} is now the default ${chalk.gray(`[${elapsed}]`)}`
)
);
} else {
@@ -301,9 +308,9 @@ async function run({ token, config: { currentTeam } }) {
const deletedCard = cards.sources.find(card => card.id === cardId);
const remainingCards = cards.sources.filter(card => card.id !== cardId);
let text = `${deletedCard.brand ||
deletedCard.card.brand} ending in ${deletedCard.last4 ||
deletedCard.card.last4} was deleted`;
let text = `${deletedCard.brand || deletedCard.card.brand} ending in ${
deletedCard.last4 || deletedCard.card.last4
} was deleted`;
// ${chalk.gray(`[${elapsed}]`)}
if (cardId === cards.defaultSource) {
@@ -317,11 +324,11 @@ async function run({ token, config: { currentTeam } }) {
card => card.id === cards.defaultCardId
);
text += `\n${newDefaultCard.brand ||
newDefaultCard.card.brand} ending in ${newDefaultCard.last4 ||
newDefaultCard.card.last4} in now default for ${chalk.bold(
contextName
)}`;
text += `\n${
newDefaultCard.brand || newDefaultCard.card.brand
} ending in ${
newDefaultCard.last4 || newDefaultCard.card.last4
} in now default for ${chalk.bold(contextName)}`;
}
}

View File

@@ -8,7 +8,6 @@ import stamp from '../../util/output/stamp';
import createCertFromFile from '../../util/certs/create-cert-from-file';
import createCertForCns from '../../util/certs/create-cert-for-cns';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import { getCommandName } from '../../util/pkg-name';
interface Options {
@@ -22,11 +21,11 @@ interface Options {
async function add(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
): Promise<number> {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
@@ -49,6 +48,7 @@ async function add(
token,
currentTeam,
debug: debugEnabled,
output,
});
try {
@@ -62,7 +62,13 @@ async function add(
throw err;
}
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
currentTeam,
output,
});
if (overwite) {
output.error('Overwrite option is deprecated');
@@ -71,7 +77,7 @@ async function add(
}
if (crtPath || keyPath || caPath) {
if (args.length !== 0 || (!crtPath || !keyPath || !caPath)) {
if (args.length !== 0 || !crtPath || !keyPath || !caPath) {
output.error(
`Invalid number of arguments to create a custom certificate entry. Usage:`
);
@@ -113,12 +119,12 @@ async function add(
(res, item) => res.concat(item.split(',')),
[]
);
const cancelWait = output.spinner(
output.spinner(
`Generating a certificate for ${chalk.bold(cns.join(', '))}`
);
cert = await createCertForCns(now, cns, contextName);
cancelWait();
output.stopSpinner();
}
if (cert instanceof Error) {

View File

@@ -3,7 +3,6 @@ import chalk from 'chalk';
// @ts-ignore
import { handleError } from '../../util/error';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import logo from '../../util/output/logo';
@@ -104,17 +103,17 @@ export default async function main(ctx: NowContext) {
return 0;
}
const output = createOutput({ debug: argv['--debug'] });
const { output } = ctx;
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
switch (subcommand) {
case 'issue':
return issue(ctx, argv, args, output);
return issue(ctx, argv, args);
case 'ls':
return ls(ctx, argv, args, output);
return ls(ctx, argv, args);
case 'rm':
return rm(ctx, argv, args, output);
return rm(ctx, argv, args);
case 'add':
return add(ctx, argv, args, output);
return add(ctx, argv, args);
case 'renew':
output.error('Renewing certificates is deprecated, issue a new one.');
return 1;

View File

@@ -28,11 +28,11 @@ type Options = {
export default async function issue(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
@@ -55,6 +55,7 @@ export default async function issue(
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;

View File

@@ -8,7 +8,6 @@ import getScope from '../../util/get-scope';
import stamp from '../../util/output/stamp';
import getCerts from '../../util/certs/get-certs';
import strlen from '../../util/strlen';
import { Output } from '../../util/output';
import { NowContext, Cert } from '../../types';
import getCommandFlags from '../../util/get-command-flags';
import { getCommandName } from '../../util/pkg-name';
@@ -21,17 +20,17 @@ interface Options {
async function ls(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
): Promise<number> {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const { '--debug': debug, '--next': nextTimestamp } = opts;
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {
@@ -48,7 +47,7 @@ async function ls(
output.error('Please provide a number for flag --next');
return 1;
}
const now = new Now({ apiUrl, token, debug, currentTeam });
const now = new Now({ apiUrl, token, debug, currentTeam, output });
const lsStamp = stamp();
if (args.length !== 0) {

View File

@@ -18,21 +18,17 @@ type Options = {
'--debug': boolean;
};
async function rm(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
) {
async function rm(ctx: NowContext, opts: Options, args: string[]) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const rmStamp = stamp();
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;

View File

@@ -3,7 +3,6 @@ import { resolve, basename } from 'path';
import { fileNameSymbol } from '@vercel/client';
import Client from '../../util/client.ts';
import getScope from '../../util/get-scope.ts';
import createOutput from '../../util/output';
import code from '../../util/output/code';
import highlight from '../../util/output/highlight';
import { readLocalConfig } from '../../util/config/files';
@@ -15,6 +14,7 @@ import deploy from './latest';
export default async ctx => {
const {
authConfig,
output,
config: { currentTeam },
apiUrl,
} = ctx;
@@ -48,7 +48,6 @@ export default async ctx => {
localConfig = readLocalConfig(paths[0]);
}
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
const stats = {};
if (argv['--help']) {
@@ -74,6 +73,7 @@ export default async ctx => {
apiUrl,
token: authConfig.token,
currentTeam,
output,
debug: debugEnabled,
});
try {

View File

@@ -233,8 +233,7 @@ export default async function main(
const paths = Object.keys(stats);
const debugEnabled = argv['--debug'];
// $FlowFixMe
const isTTY = process.stdout.isTTY;
const { isTTY } = process.stdout;
const quiet = !isTTY;
// check paths
@@ -263,6 +262,7 @@ export default async function main(
apiUrl: ctx.apiUrl,
token: ctx.authConfig.token,
debug: debugEnabled,
output,
});
// retrieve `project` and `org` from .vercel
@@ -645,6 +645,7 @@ export default async function main(
token: ctx.authConfig.token,
currentTeam: org.id,
debug: debugEnabled,
output,
}),
err.meta.domain,
contextName
@@ -727,6 +728,7 @@ export default async function main(
token: ctx.authConfig.token,
currentTeam: org.type === 'team' ? org.id : null,
debug: debugEnabled,
output,
}),
deployment,
deployStamp,

View File

@@ -2,7 +2,6 @@ import { resolve, join } from 'path';
import DevServer from '../../util/dev/server';
import parseListen from '../../util/dev/parse-listen';
import { Output } from '../../util/output';
import { NowContext, ProjectEnvVariable } from '../../types';
import Client from '../../util/client';
import { getLinkedProject } from '../../util/projects/link';
@@ -22,9 +21,9 @@ type Options = {
export default async function dev(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const { output } = ctx;
const [dir = '.'] = args;
let cwd = resolve(dir);
const listen = parseListen(opts['--listen'] || '3000');
@@ -35,6 +34,7 @@ export default async function dev(
token: ctx.authConfig.token,
currentTeam: ctx.config.currentTeam,
debug,
output,
});
// retrieve dev command
@@ -49,7 +49,6 @@ export default async function dev(
link = await setupAndLink(
ctx,
output,
cwd,
forceDelete,
autoConfirm,

View File

@@ -7,7 +7,6 @@ import getSubcommand from '../../util/get-subcommand';
import { NowContext } from '../../types';
import { NowError } from '../../util/now-error';
import handleError from '../../util/handle-error';
import createOutput from '../../util/output/create-output';
import logo from '../../util/output/logo';
import cmd from '../../util/output/cmd';
import highlight from '../../util/output/highlight';
@@ -51,7 +50,7 @@ const help = () => {
export default async function main(ctx: NowContext) {
let argv;
let args;
let output;
const { output } = ctx;
try {
argv = getArgs(ctx.argv.slice(2), {
@@ -63,9 +62,7 @@ export default async function main(ctx: NowContext) {
'--port': Number,
'-p': '--port',
});
const debug = argv['--debug'];
args = getSubcommand(argv._.slice(1), COMMAND_CONFIG).args;
output = createOutput({ debug });
if ('--port' in argv) {
output.warn('`--port` is deprecated, please use `--listen` instead');
@@ -120,7 +117,7 @@ export default async function main(ctx: NowContext) {
}
try {
return await dev(ctx, argv, args, output);
return await dev(ctx, argv, args);
} catch (err) {
if (err.code === 'ENOTFOUND') {
// Error message will look like the following:

View File

@@ -6,7 +6,6 @@ import {
DNSInvalidType,
} from '../../util/errors-ts';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import addDNSRecord from '../../util/dns/add-dns-record';
import Client from '../../util/client';
import getScope from '../../util/get-scope';
@@ -22,17 +21,17 @@ type Options = {
export default async function add(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
apiUrl,
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {

View File

@@ -1,6 +1,5 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import Client from '../../util/client';
import getScope from '../../util/get-scope';
import { DomainNotFound, InvalidDomain } from '../../util/errors-ts';
@@ -15,17 +14,17 @@ type Options = {
export default async function add(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
apiUrl,
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import handleError from '../../util/handle-error';
@@ -112,16 +111,15 @@ export default async function main(ctx: NowContext) {
return 2;
}
const output = createOutput({ debug: argv['--debug'] });
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
switch (subcommand) {
case 'add':
return add(ctx, argv, args, output);
return add(ctx, argv, args);
case 'import':
return importZone(ctx, argv, args, output);
return importZone(ctx, argv, args);
case 'rm':
return rm(ctx, argv, args, output);
return rm(ctx, argv, args);
default:
return ls(ctx, argv, args, output);
return ls(ctx, argv, args);
}
}

View File

@@ -1,6 +1,5 @@
import chalk from 'chalk';
import ms from 'ms';
import { Output } from '../../util/output';
import { DomainNotFound } from '../../util/errors-ts';
import { DNSRecord, NowContext } from '../../types';
import Client from '../../util/client';
@@ -22,17 +21,17 @@ type Options = {
export default async function ls(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
apiUrl,
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const { '--debug': debug, '--next': nextTimestamp } = opts;
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {

View File

@@ -17,17 +17,17 @@ type Options = {
export default async function rm(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
apiUrl,
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
try {
await getScope(client);
@@ -100,12 +100,7 @@ function readConfirmation(
process.stdin
.on('data', d => {
process.stdin.pause();
resolve(
d
.toString()
.trim()
.toLowerCase() === 'y'
);
resolve(d.toString().trim().toLowerCase() === 'y');
})
.resume();
});

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import * as ERRORS from '../../util/errors-ts';
import Client from '../../util/client';
import formatNSTable from '../../util/format-ns-table';
@@ -24,18 +23,18 @@ type Options = {
export default async function add(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const force = opts['--force'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {

View File

@@ -2,7 +2,6 @@ import chalk from 'chalk';
import psl from 'psl';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import * as ERRORS from '../../util/errors-ts';
import Client from '../../util/client';
import getDomainPrice from '../../util/domains/get-domain-price';
@@ -21,17 +20,17 @@ type Options = {
export default async function buy(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {
@@ -70,7 +69,10 @@ export default async function buy(
}
const availableStamp = stamp();
const domainPrice = await getDomainPrice(client, domainName);
const [domainPrice, renewalPrice] = await Promise.all([
getDomainPrice(client, domainName),
getDomainPrice(client, domainName, 'renewal'),
]);
if (domainPrice instanceof Error) {
output.prettyError(domainPrice);
@@ -102,14 +104,22 @@ export default async function buy(
return 0;
}
const autoRenew = await promptBool(
renewalPrice.period === 1
? `Auto renew yearly for ${chalk.bold(`$${price}`)}?`
: `Auto renew every ${renewalPrice.period} years for ${chalk.bold(
`$${price}`
)}?`,
{ defaultValue: true }
);
let buyResult;
const purchaseStamp = stamp();
const stopPurchaseSpinner = output.spinner('Purchasing');
output.spinner('Purchasing');
try {
buyResult = await purchaseDomain(client, domainName, price);
buyResult = await purchaseDomain(client, domainName, price, autoRenew);
} catch (err) {
stopPurchaseSpinner();
output.error(
'An unexpected error occurred while purchasing your domain. Please try again later.'
);
@@ -117,7 +127,7 @@ export default async function buy(
return 1;
}
stopPurchaseSpinner();
output.stopSpinner();
if (buyResult instanceof ERRORS.SourceNotFound) {
output.error(

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import handleError from '../../util/handle-error';
@@ -106,24 +105,23 @@ export default async function main(ctx: NowContext) {
return 2;
}
const output = createOutput({ debug: argv['--debug'] });
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
switch (subcommand) {
case 'add':
return add(ctx, argv, args, output);
return add(ctx, argv, args);
case 'inspect':
return inspect(ctx, argv, args, output);
return inspect(ctx, argv, args);
case 'move':
return move(ctx, argv, args, output);
return move(ctx, argv, args);
case 'buy':
return buy(ctx, argv, args, output);
return buy(ctx, argv, args);
case 'rm':
return rm(ctx, argv, args, output);
return rm(ctx, argv, args);
case 'transferIn':
return transferIn(ctx, argv, args, output);
return transferIn(ctx, argv, args);
case 'verify':
return verify(ctx, argv, args, output);
return verify(ctx, argv, args);
default:
return ls(ctx, argv, args, output);
return ls(ctx, argv, args);
}
}

View File

@@ -24,17 +24,17 @@ type Options = {
export default async function inspect(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;

View File

@@ -7,7 +7,6 @@ import Client from '../../util/client';
import getDomains from '../../util/domains/get-domains';
import getScope from '../../util/get-scope';
import stamp from '../../util/output/stamp';
import { Output } from '../../util/output';
import formatTable from '../../util/format-table';
import { formatDateWithoutTime } from '../../util/format-date';
import { Domain, NowContext } from '../../types';
@@ -24,17 +23,17 @@ type Options = {
export default async function ls(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const { '--debug': debug, '--next': nextTimestamp } = opts;
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
if (typeof nextTimestamp !== undefined && Number.isNaN(nextTimestamp)) {

View File

@@ -2,7 +2,6 @@ import chalk from 'chalk';
import plural from 'pluralize';
import { NowContext, User, Team } from '../../types';
import { Output } from '../../util/output';
import * as ERRORS from '../../util/errors-ts';
import Client from '../../util/client';
import getScope from '../../util/get-scope';
@@ -25,17 +24,17 @@ type Options = {
export default async function move(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
let user = null;

View File

@@ -26,17 +26,17 @@ type Options = {
export default async function rm(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
const [domainName] = args;
let contextName = null;

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import * as ERRORS from '../../util/errors-ts';
import Client from '../../util/client';
import getScope from '../../util/get-scope';
@@ -24,17 +23,17 @@ type Options = {
export default async function transferIn(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {

View File

@@ -1,13 +1,11 @@
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import { NowBuildError } from '@vercel/build-utils';
import { getCommandName } from '../../util/pkg-name';
export default async function verify(
_ctx: NowContext,
{ output }: NowContext,
_opts: {},
args: string[],
output: Output
args: string[]
) {
const [domainName] = args;

View File

@@ -122,7 +122,17 @@ export default async function add(
const existing = new Set(
envs.filter(r => r.key === envName).map(r => r.target)
);
const choices = getEnvTargetChoices().filter(c => !existing.has(c.value));
const choices = getEnvTargetChoices().filter(c => {
// hide Development if "Secret" is chosen
if (
envType === ProjectEnvType.Secret &&
c.value === ProjectEnvTarget.Development
) {
return false;
}
return !existing.has(c.value);
});
if (choices.length === 0) {
output.error(

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import getInvalidSubcommand from '../../util/get-invalid-subcommand';
@@ -109,15 +108,15 @@ export default async function main(ctx: NowContext) {
}
const debug = argv['--debug'];
const output = createOutput({ debug });
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
const {
authConfig: { token },
apiUrl,
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
const link = await getLinkedProject(output, client);
if (link.status === 'error') {
return link.exitCode;

View File

@@ -4,7 +4,6 @@ import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import { NowContext } from '../../types';
import handleError from '../../util/handle-error';
import createOutput from '../../util/output/create-output';
import logo from '../../util/output/logo';
import error from '../../util/output/error';
import init from './init';
@@ -47,7 +46,6 @@ const help = () => {
export default async function main(ctx: NowContext) {
let argv;
let args;
let output;
try {
argv = getArgs(ctx.argv.slice(2), {
@@ -55,7 +53,6 @@ export default async function main(ctx: NowContext) {
'-f': Boolean,
});
args = getSubcommand(argv._.slice(1), COMMAND_CONFIG).args;
output = createOutput({ debug: argv['--debug'] });
} catch (err) {
handleError(err);
return 1;
@@ -67,15 +64,15 @@ export default async function main(ctx: NowContext) {
}
if (argv._.length > 3) {
output.error('Too much arguments.');
ctx.output.error('Too much arguments.');
return 1;
}
try {
return await init(ctx, argv, args, output);
return await init(ctx, argv, args);
} catch (err) {
console.log(error(err.message));
output.debug(err.stack);
ctx.output.debug(err.stack);
return 1;
}
}

View File

@@ -34,9 +34,9 @@ const EXAMPLE_API = 'https://now-example-files.zeit.sh';
export default async function init(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const { output } = ctx;
const [name, dir] = args;
const force = opts['-f'] || opts['--force'];
@@ -82,12 +82,12 @@ export default async function init(
* Fetch example list json
*/
async function fetchExampleList(output: Output) {
const stopSpinner = output.spinner('Fetching examples');
output.spinner('Fetching examples');
const url = `${EXAMPLE_API}/v2/list.json`;
try {
const resp = await fetch(url);
stopSpinner();
output.stopSpinner();
if (resp.status !== 200) {
throw new Error(`Failed fetching list.json (${resp.statusText}).`);
@@ -95,7 +95,7 @@ async function fetchExampleList(output: Output) {
return (await resp.json()) as Example[];
} catch (e) {
stopSpinner();
output.stopSpinner();
}
}
@@ -127,13 +127,13 @@ async function extractExample(
ver: string = 'v2'
) {
const folder = prepareFolder(process.cwd(), dir || name, force);
const stopSpinner = output.spinner(`Fetching ${name}`);
output.spinner(`Fetching ${name}`);
const url = `${EXAMPLE_API}/${ver}/download/${name}.tar.gz`;
return fetch(url)
.then(async resp => {
stopSpinner();
output.stopSpinner();
if (resp.status !== 200) {
throw new Error(`Could not get ${name}.tar.gz`);
@@ -163,7 +163,7 @@ async function extractExample(
return 0;
})
.catch(e => {
stopSpinner();
output.stopSpinner();
throw e;
});
}

View File

@@ -3,7 +3,6 @@ import getArgs from '../util/get-args';
import buildsList from '../util/output/builds';
import routesList from '../util/output/routes';
import indent from '../util/output/indent';
import createOutput from '../util/output';
import Now from '../util';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed.ts';
@@ -59,9 +58,13 @@ export default async function main(ctx) {
return 2;
}
const apiUrl = ctx.apiUrl;
const {
apiUrl,
output,
authConfig: { token },
config,
} = ctx;
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
const { print, log, error } = output;
// extract the first parameter
@@ -73,16 +76,13 @@ export default async function main(ctx) {
return 1;
}
const {
authConfig: { token },
config,
} = ctx;
const { currentTeam } = config;
const client = new Client({
apiUrl,
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;
@@ -90,25 +90,30 @@ export default async function main(ctx) {
({ contextName } = await getScope(client));
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
error(err.message);
return 1;
}
throw err;
}
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
currentTeam,
output,
});
// resolve the deployment, since we might have been given an alias
const depFetchStart = Date.now();
const cancelWait = output.spinner(
output.spinner(
`Fetching deployment "${deploymentIdOrHost}" in ${chalk.bold(contextName)}`
);
try {
deployment = await now.findDeployment(deploymentIdOrHost);
} catch (err) {
cancelWait();
if (err.status === 404) {
error(
`Failed to find deployment "${deploymentIdOrHost}" in ${chalk.bold(
@@ -136,7 +141,6 @@ export default async function main(ctx) {
? await now.fetch(`/v1/now/deployments/${id}/builds`)
: { builds: [] };
cancelWait();
log(
`Fetched deployment "${url}" in ${chalk.bold(contextName)} ${elapsed(
Date.now() - depFetchStart

View File

@@ -1,6 +1,5 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import handleError from '../../util/handle-error';
@@ -66,8 +65,6 @@ export default async function main(ctx: NowContext) {
return 2;
}
const debug = argv['--debug'];
const output = createOutput({ debug });
const { args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
const path = args[0] || process.cwd();
const autoConfirm = argv['--confirm'];
@@ -75,7 +72,6 @@ export default async function main(ctx: NowContext) {
const link = await setupAndLink(
ctx,
output,
path,
forceDelete,
autoConfirm,

View File

@@ -3,12 +3,10 @@ import ms from 'ms';
import table from 'text-table';
import Now from '../util';
import getArgs from '../util/get-args';
import createOutput from '../util/output';
import { handleError } from '../util/error';
import cmd from '../util/output/cmd.ts';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed.ts';
import wait from '../util/output/wait';
import strlen from '../util/strlen.ts';
import Client from '../util/client.ts';
import getScope from '../util/get-scope.ts';
@@ -78,11 +76,15 @@ export default async function main(ctx) {
return 1;
}
const debugEnabled = argv['--debug'];
const {
authConfig: { token },
output,
apiUrl,
config,
} = ctx;
const { print, log, error, note, debug } = createOutput({
debug: debugEnabled,
});
const debugEnabled = argv['--debug'];
const { print, log, error, note, debug, spinner } = output;
if (argv._.length > 2) {
error(`${getCommandName('ls [app]')} accepts at most one argument`);
@@ -92,24 +94,19 @@ export default async function main(ctx) {
let app = argv._[1];
let host = null;
const apiUrl = ctx.apiUrl;
if (argv['--help']) {
help();
return 0;
}
const meta = parseMeta(argv['--meta']);
const {
authConfig: { token },
config,
} = ctx;
const { currentTeam, includeScheme } = config;
const client = new Client({
apiUrl,
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;
@@ -131,11 +128,15 @@ export default async function main(ctx) {
return 1;
}
const stopSpinner = wait(
`Fetching deployments in ${chalk.bold(contextName)}`
);
spinner(`Fetching deployments in ${chalk.bold(contextName)}`);
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
output,
currentTeam,
});
const start = new Date();
if (app && !isValidName(app)) {
@@ -156,7 +157,6 @@ export default async function main(ctx) {
const hostParts = asHost.split('-');
if (hostParts < 2) {
stopSpinner();
error('Only deployment hostnames are allowed, no aliases');
return 1;
}
@@ -165,19 +165,12 @@ export default async function main(ctx) {
host = asHost;
}
let response;
try {
debug('Fetching deployments');
response = await now.list(app, {
version: 6,
meta,
nextTimestamp,
});
} catch (err) {
stopSpinner();
throw err;
}
debug('Fetching deployments');
const response = await now.list(app, {
version: 6,
meta,
nextTimestamp,
});
let { deployments, pagination } = response;
@@ -193,7 +186,6 @@ export default async function main(ctx) {
if (err.status === 404) {
debug('Ignore findDeployment 404');
} else {
stopSpinner();
throw err;
}
}
@@ -210,7 +202,6 @@ export default async function main(ctx) {
deployments = deployments.filter(deployment => deployment.url === host);
}
stopSpinner();
log(
`Deployments under ${chalk.bold(contextName)} ${elapsed(
Date.now() - start

View File

@@ -19,7 +19,6 @@ import getGlobalPathConfig from '../util/config/global-path';
import hp from '../util/humanize-path';
import logo from '../util/output/logo';
import exit from '../util/exit';
import createOutput from '../util/output';
import executeLogin from '../util/login/login.ts';
import { prependEmoji, emoji } from '../util/emoji';
import { getCommandName, getPkgName } from '../util/pkg-name.ts';
@@ -141,15 +140,12 @@ const login = async ctx => {
await exit(0);
}
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
const { apiUrl, output } = ctx;
argv._ = argv._.slice(1);
const apiUrl = ctx.apiUrl;
let email;
let emailIsValid = false;
let stopSpinner;
const possibleAddress = argv._[0];
@@ -190,19 +186,18 @@ const login = async ctx => {
let verificationToken;
let securityCode;
stopSpinner = output.spinner('Sending you an email');
output.spinner('Sending you an email');
try {
const data = await executeLogin(apiUrl, email);
verificationToken = data.token;
securityCode = data.securityCode;
} catch (err) {
stopSpinner();
console.log(error(err.message));
output.error(err.message);
return 1;
}
stopSpinner();
output.stopSpinner();
// Clear up `Sending email` success message
process.stdout.write(eraseLines(possibleAddress ? 1 : 2));
@@ -215,7 +210,7 @@ const login = async ctx => {
)}.\n`
);
stopSpinner = output.spinner('Waiting for your confirmation');
output.spinner('Waiting for your confirmation');
let token;
@@ -228,14 +223,13 @@ const login = async ctx => {
// /now/registraton is currently returning plain text in that case
// we just wait for the user to click on the link
} else {
stopSpinner();
console.log(err.message);
output.error(err.message);
return 1;
}
}
}
stopSpinner();
output.stopSpinner();
console.log(ok('Email confirmed'));
// There's no need to save the user since we always

View File

@@ -11,7 +11,7 @@ import {
} from '../util/config/files';
import getArgs from '../util/get-args';
import { NowContext } from '../types';
import createOutput, { Output } from '../util/output';
import { Output } from '../util/output';
import { getPkgName } from '../util/pkg-name';
const help = () => {
@@ -54,13 +54,11 @@ export default async function main(ctx: NowContext): Promise<number> {
return 2;
}
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
return logout(ctx.apiUrl, output);
return logout(ctx.apiUrl, ctx.output);
}
const logout = async (apiUrl: string, output: Output) => {
const spinner = output.spinner('Logging out...', 200);
output.spinner('Logging out', 200);
const configContent = readConfigFile();
const authContent = readAuthConfigFile();
@@ -83,7 +81,6 @@ const logout = async (apiUrl: string, output: Output) => {
writeToAuthConfigFile(authContent);
output.debug('Configuration has been deleted');
} catch (err) {
spinner();
output.error(`Couldn't remove config while logging out`);
return 1;
}
@@ -98,14 +95,12 @@ const logout = async (apiUrl: string, output: Output) => {
if (res.status === 403) {
output.debug('Token is invalid so it cannot be revoked');
} else if (res.status !== 200) {
spinner();
const err = await res.json();
output.error('Failed to revoke token');
output.debug(err ? err.message : '');
return 1;
}
spinner();
output.log('Logged out!');
return 0;
};

View File

@@ -1,7 +1,6 @@
import mri from 'mri';
import chalk from 'chalk';
import Now from '../util';
import createOutput from '../util/output';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed.ts';
import { maybeURL, normalizeURL } from '../util/url';
@@ -59,7 +58,6 @@ export default async function main(ctx) {
let deploymentIdOrURL;
let debug;
let apiUrl;
let head;
let limit;
let follow;
@@ -87,8 +85,12 @@ export default async function main(ctx) {
return 2;
}
const debugEnabled = argv.debug;
const output = createOutput({ debug: debugEnabled });
const {
authConfig: { token },
apiUrl,
output,
config,
} = ctx;
try {
since = argv.since ? toTimestamp(argv.since) : 0;
@@ -117,7 +119,6 @@ export default async function main(ctx) {
}
debug = argv.debug;
apiUrl = ctx.apiUrl;
head = argv.head;
limit = typeof argv.n === 'number' ? argv.n : 100;
@@ -125,17 +126,14 @@ export default async function main(ctx) {
if (follow) until = 0;
outputMode = argv.output in logPrinters ? argv.output : 'short';
const {
authConfig: { token },
config,
} = ctx;
const { currentTeam } = config;
const now = new Now({ apiUrl, token, debug, currentTeam });
const now = new Now({ apiUrl, token, debug, currentTeam, output });
const client = new Client({
apiUrl,
token,
currentTeam,
debug: debugEnabled,
debug,
output,
});
let contextName = null;
@@ -154,14 +152,12 @@ export default async function main(ctx) {
const id = deploymentIdOrURL;
const depFetchStart = Date.now();
const cancelWait = output.spinner(
`Fetching deployment "${id}" in ${chalk.bold(contextName)}`
);
output.spinner(`Fetching deployment "${id}" in ${chalk.bold(contextName)}`);
try {
deployment = await now.findDeployment(id);
} catch (err) {
cancelWait();
output.stopSpinner();
now.close();
if (err.status === 404) {
@@ -182,7 +178,6 @@ export default async function main(ctx) {
throw err;
}
cancelWait();
output.log(
`Fetched deployment "${deployment.url}" in ${chalk.bold(
contextName
@@ -206,6 +201,7 @@ export default async function main(ctx) {
quiet: false,
debug,
findOpts: findOpts1,
output,
});
const printedEventIds = new Set();
@@ -232,6 +228,7 @@ export default async function main(ctx) {
quiet: false,
debug,
findOpts: findOpts2,
output,
});
}

View File

@@ -8,7 +8,6 @@ import exit from '../util/exit';
import Client from '../util/client.ts';
import logo from '../util/output/logo';
import getScope from '../util/get-scope';
import createOutput from '../util/output';
import getCommandFlags from '../util/get-command-flags';
import wait from '../util/output/wait';
import { getPkgName, getCommandName } from '../util/pkg-name.ts';
@@ -76,13 +75,12 @@ const main = async ctx => {
return exit(2);
}
const output = createOutput({ debug });
const {
authConfig: { token },
config: { currentTeam },
output,
} = ctx;
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
@@ -288,12 +286,7 @@ function readConfirmation(projectName) {
process.stdin
.on('data', d => {
process.stdin.pause();
resolve(
d
.toString()
.trim()
.toLowerCase() === 'y'
);
resolve(d.toString().trim().toLowerCase() === 'y');
})
.resume();
});

View File

@@ -5,7 +5,6 @@ import plural from 'pluralize';
import table from 'text-table';
import Now from '../util';
import getAliases from '../util/alias/get-aliases';
import createOutput from '../util/output';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed.ts';
import { normalizeURL } from '../util/url';
@@ -79,12 +78,16 @@ export default async function main(ctx) {
argv._ = argv._.slice(1);
const apiUrl = ctx.apiUrl;
const {
apiUrl,
authConfig: { token },
output,
config,
} = ctx;
const hard = argv.hard || false;
const skipConfirmation = argv.yes || false;
const ids = argv._;
const debugEnabled = argv.debug;
const output = createOutput({ debug: debugEnabled });
const { success, error, log } = output;
if (argv.help || ids[0] === 'help') {
@@ -107,16 +110,13 @@ export default async function main(ctx) {
return 1;
}
const {
authConfig: { token },
config,
} = ctx;
const { currentTeam } = config;
const client = new Client({
apiUrl,
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;
@@ -133,7 +133,7 @@ export default async function main(ctx) {
throw err;
}
const cancelWait = output.spinner(
output.spinner(
`Fetching deployment(s) ${ids
.map(id => `"${id}"`)
.join(' ')} in ${chalk.bold(contextName)}`
@@ -198,7 +198,7 @@ export default async function main(ctx) {
})
);
} finally {
cancelWait();
output.stopSpinner();
}
deployments = deployments.filter((match, i) => {
@@ -235,11 +235,9 @@ export default async function main(ctx) {
}
if (!skipConfirmation) {
const confirmation = (await readConfirmation(
deployments,
projects,
output
)).toLowerCase();
const confirmation = (
await readConfirmation(deployments, projects, output)
).toLowerCase();
if (confirmation !== 'y' && confirmation !== 'yes') {
output.log('Aborted');
@@ -248,7 +246,13 @@ export default async function main(ctx) {
}
}
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
currentTeam,
output,
});
const start = new Date();
await Promise.all([

View File

@@ -9,7 +9,6 @@ import exit from '../util/exit';
import logo from '../util/output/logo';
import Client from '../util/client.ts';
import getScope from '../util/get-scope.ts';
import createOutput from '../util/output';
import confirm from '../util/input/confirm';
import getCommandFlags from '../util/get-command-flags';
import getPrefixedFlags from '../util/get-prefixed-flags';
@@ -104,10 +103,10 @@ const main = async ctx => {
const {
authConfig: { token },
output,
config: { currentTeam },
} = ctx;
const output = createOutput({ debug });
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {
@@ -139,7 +138,7 @@ export default async ctx => {
};
async function run({ output, token, contextName, currentTeam, ctx }) {
const secrets = new NowSecrets({ apiUrl, token, debug, currentTeam });
const secrets = new NowSecrets({ apiUrl, token, debug, currentTeam, output });
const args = argv._.slice(1);
const start = Date.now();

View File

@@ -52,7 +52,7 @@ const help = () => {
${chalk.gray('')} Invite new members (interactively)
${chalk.cyan(`$ ${getPkgName()} teams invite`)}
${chalk.gray('')} Paginate results, where ${chalk.dim(
'`1584722256178`'
)} is the time in milliseconds since the UNIX epoch.
@@ -97,10 +97,11 @@ const main = async ctx => {
const {
authConfig: { token },
output,
config,
} = ctx;
return run({ token, config });
return run({ token, config, output });
};
export default async ctx => {
@@ -112,7 +113,7 @@ export default async ctx => {
}
};
async function run({ token, config }) {
async function run({ token, config, output }) {
const { currentTeam } = config;
const teams = new NowTeams({ apiUrl, token, debug, currentTeam });
const args = argv._;
@@ -126,6 +127,7 @@ async function run({ token, config }) {
config,
apiUrl,
token,
output,
argv,
});
break;
@@ -138,6 +140,7 @@ async function run({ token, config }) {
apiUrl,
token,
debug,
output,
});
break;
}
@@ -154,6 +157,7 @@ async function run({ token, config }) {
config,
apiUrl,
token,
output,
});
break;
}

View File

@@ -14,7 +14,6 @@ import success from '../../util/output/success';
import getUser from '../../util/get-user.ts';
import Client from '../../util/client.ts';
import { getCommandName } from '../../util/pkg-name.ts';
import createOutput from '../../util/output';
const validateEmail = data => regexEmail.test(data.trim()) || data.length === 0;
@@ -65,8 +64,8 @@ export default async function ({
noopMsg = 'No changes made',
apiUrl,
token,
output,
} = {}) {
const output = createOutput();
const { currentTeam: currentTeamId } = config;
const stopSpinner = wait('Fetching teams');
@@ -77,7 +76,7 @@ export default async function ({
stopSpinner();
const stopUserSpinner = wait('Fetching user information');
const client = new Client({ apiUrl, token });
const client = new Client({ apiUrl, token, output });
let user;
try {
user = await getUser(client);

View File

@@ -8,7 +8,6 @@ import info from '../../util/output/info';
import error from '../../util/output/error';
import chars from '../../util/output/chars';
import table from '../../util/output/table';
import createOutput from '../../util/output';
import getUser from '../../util/get-user.ts';
import Client from '../../util/client.ts';
import getPrefixedFlags from '../../util/get-prefixed-flags';
@@ -16,9 +15,8 @@ import { getPkgName } from '../../util/pkg-name.ts';
import getCommandFlags from '../../util/get-command-flags';
import cmd from '../../util/output/cmd.ts';
export default async function({ teams, config, apiUrl, token, argv }) {
export default async function ({ teams, config, apiUrl, token, output, argv }) {
const { next } = argv;
const output = createOutput({ debug: argv['--debug'] });
if (typeof next !== 'undefined' && !Number.isInteger(next)) {
output.error('Please provide a number for flag --next');
@@ -36,7 +34,7 @@ export default async function({ teams, config, apiUrl, token, argv }) {
stopSpinner();
const stopUserSpinner = wait('Fetching user information');
const client = new Client({ apiUrl, token, currentTeam });
const client = new Client({ apiUrl, token, currentTeam, output });
let user;
try {
user = await getUser(client);

View File

@@ -2,8 +2,6 @@
import chalk from 'chalk';
// Utilities
import wait from '../../util/output/wait';
import listInput from '../../util/input/list';
import success from '../../util/output/success';
import info from '../../util/output/info';
@@ -24,22 +22,20 @@ const updateCurrentTeam = (config, newTeam) => {
writeToConfigFile(config);
};
export default async function({ apiUrl, token, debug, args, config }) {
let stopSpinner = wait('Fetching teams');
export default async function ({ apiUrl, token, debug, args, config, output }) {
output.spinner('Fetching teams');
// We're loading the teams here without `currentTeam`, so that
// people can use `vercel switch` in the case that their
// current team was deleted.
const teams = new NowTeams({ apiUrl, token, debug });
const teams = new NowTeams({ apiUrl, token, debug, output });
const list = (await teams.ls()).teams;
let { currentTeam } = config;
const accountIsCurrent = !currentTeam;
stopSpinner();
const stopUserSpinner = wait('Fetching user information');
const client = new Client({ apiUrl, token });
output.spinner('Fetching user information');
const client = new Client({ apiUrl, token, output });
let user;
try {
user = await getUser(client);
@@ -52,8 +48,6 @@ export default async function({ apiUrl, token, debug, args, config }) {
throw err;
}
stopUserSpinner();
if (accountIsCurrent) {
currentTeam = {
slug: user.username || user.email,
@@ -84,10 +78,10 @@ export default async function({ apiUrl, token, debug, args, config }) {
}
if (desiredSlug === user.username) {
stopSpinner = wait('Saving');
output.spinner('Saving');
updateCurrentTeam(config);
stopSpinner();
output.stopSpinner();
console.log(
success(`Your account (${chalk.bold(desiredSlug)}) is now active!`)
);
@@ -163,10 +157,10 @@ export default async function({ apiUrl, token, debug, args, config }) {
return 0;
}
stopSpinner = wait('Saving');
output.spinner('Saving');
updateCurrentTeam(config);
stopSpinner();
output.stopSpinner();
console.log(success(`Your account (${chalk.bold(choice)}) is now active!`));
return 0;
}
@@ -176,10 +170,10 @@ export default async function({ apiUrl, token, debug, args, config }) {
return 0;
}
stopSpinner = wait('Saving');
output.spinner('Saving');
updateCurrentTeam(config, newTeam);
stopSpinner();
output.stopSpinner();
console.log(
success(
`The team ${chalk.bold(newTeam.name)} (${newTeam.slug}) is now active!`

View File

@@ -5,7 +5,6 @@ import logo from '../util/output/logo';
import handleError from '../util/handle-error';
import getArgs from '../util/get-args';
import { NowContext } from '../types';
import createOutput from '../util/output';
import getUpdateCommand from '../util/get-update-command';
import { getPkgName, getTitleName } from '../util/pkg-name';
@@ -35,6 +34,7 @@ const help = () => {
export default async function main(ctx: NowContext): Promise<number> {
let argv;
const { output } = ctx;
try {
argv = getArgs(ctx.argv.slice(2), {
@@ -55,8 +55,6 @@ export default async function main(ctx: NowContext): Promise<number> {
return 2;
}
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
output.log(
`Please run ${cmd(
await getUpdateCommand()

View File

@@ -4,7 +4,6 @@ import logo from '../util/output/logo';
import { handleError } from '../util/error';
import Client from '../util/client.ts';
import getScope from '../util/get-scope.ts';
import createOutput from '../util/output';
import { getPkgName } from '../util/pkg-name.ts';
const help = () => {
@@ -55,10 +54,10 @@ const main = async ctx => {
const debug = argv['--debug'];
const {
authConfig: { token },
output,
apiUrl,
} = ctx;
const output = createOutput({ debug });
const client = new Client({ apiUrl, token, debug });
const client = new Client({ apiUrl, token, debug, output });
let contextName = null;
try {

View File

@@ -291,7 +291,7 @@ const main = async argv_ => {
let authConfig = null;
const subcommandsWithoutToken = ['login', 'help', 'init', 'dev', 'update'];
const subcommandsWithoutToken = ['login', 'help', 'init', 'update'];
if (authConfigExists) {
try {
@@ -346,6 +346,7 @@ const main = async argv_ => {
// the context object to supply to the providers or the commands
const ctx = {
output,
config,
authConfig,
localConfig,
@@ -536,7 +537,7 @@ const main = async argv_ => {
!(targetCommand === 'teams' && argv._[3] !== 'invite')
) {
let user = null;
const client = new Client({ apiUrl, token });
const client = new Client({ apiUrl, token, output });
try {
user = await getUser(client);

View File

@@ -1,4 +1,5 @@
import { NowConfig } from './util/dev/types';
import { Output } from './util/output';
export type ThenArg<T> = T extends Promise<infer U> ? U : T;
@@ -8,6 +9,7 @@ export interface NowContext {
authConfig: {
token: string;
};
output: Output;
config: {
currentTeam: string;
updateChannel: string;

View File

@@ -19,14 +19,14 @@ export default async function createAlias(
alias: string,
externalDomain: boolean
) {
let cancelMessage = output.spinner(`Creating alias`);
output.spinner(`Creating alias`);
const result = await performCreateAlias(
client,
contextName,
deployment,
alias
);
cancelMessage();
output.stopSpinner();
if (result instanceof ERRORS.CertMissing) {
const cert = await createCertForAlias(
@@ -40,14 +40,14 @@ export default async function createAlias(
return cert;
}
let cancelMessage = output.spinner(`Creating alias`);
output.spinner(`Creating alias`);
const secondTry = await performCreateAlias(
client,
contextName,
deployment,
alias
);
cancelMessage();
output.stopSpinner();
return secondTry;
}

View File

@@ -37,9 +37,7 @@ export async function getDeploymentForAlias(
contextName: string,
localConfig: NowConfig
) {
const cancelWait = output.spinner(
`Fetching deployment to alias in ${chalk.bold(contextName)}`
);
output.spinner(`Fetching deployment to alias in ${chalk.bold(contextName)}`);
// When there are no args at all we try to get the targets from the config
if (args.length === 2) {
@@ -49,7 +47,7 @@ export async function getDeploymentForAlias(
contextName,
deploymentId
);
cancelWait();
output.stopSpinner();
return deployment;
}
@@ -68,6 +66,6 @@ export async function getDeploymentForAlias(
user,
contextName
);
cancelWait();
output.stopSpinner();
return deployment;
}

View File

@@ -13,20 +13,21 @@ export default async function createCertificateForAlias(
alias: string,
shouldBeWildcard: boolean
) {
output.spinner(`Generating a certificate…`);
const cns = shouldBeWildcard ? getWildcardCnsForAlias(alias) : [alias];
const cancelMessage = output.spinner(`Generating a certificate...`);
const certStamp = stamp();
const cert = await createCertForCns(client, cns, context);
if (cert instanceof NowError) {
cancelMessage();
output.stopSpinner();
return cert;
}
cancelMessage();
output.log(
`Certificate for ${joinWords(cert.cns)} (${
cert.uid
}) created ${certStamp()}`
);
return cert;
}

View File

@@ -33,6 +33,7 @@ export default class Client extends EventEmitter {
forceNew = false,
withCache = false,
debug = false,
output = createOutput({ debug }),
}: {
apiUrl: string;
token: string;
@@ -40,13 +41,14 @@ export default class Client extends EventEmitter {
forceNew?: boolean;
withCache?: boolean;
debug?: boolean;
output?: Output;
}) {
super();
this._token = token;
this._debug = debug;
this._forceNew = forceNew;
this._withCache = withCache;
this._output = createOutput({ debug });
this._output = output;
this._apiUrl = apiUrl;
this._onRetry = this._onRetry.bind(this);
this.currentTeam = currentTeam;

View File

@@ -22,25 +22,21 @@ export default async function generateCertForDeploy(
return new InvalidDomain(deployURL);
}
const cancelSetupWait = output.spinner(
`Setting custom suffix domain ${domain}`
);
output.spinner(`Setting custom suffix domain ${domain}`);
const result = await setupDomain(output, client, domain, contextName);
cancelSetupWait();
output.stopSpinner();
if (result instanceof NowError) {
return result;
}
// Generate the certificate with the given parameters
const cancelCertWait = output.spinner(
`Generating a wildcard certificate for ${domain}`
);
output.spinner(`Generating a wildcard certificate for ${domain}`);
const cert = await createCertForCns(
client,
[domain, `*.${domain}`],
contextName
);
cancelCertWait();
output.stopSpinner();
if (cert instanceof NowError) {
return cert;
}

View File

@@ -100,11 +100,7 @@ export default async function processDeployment({
skipAutoDetectionConfirmation,
};
let queuedSpinner = null;
let buildSpinner = null;
let deploySpinner = null;
const deployingSpinner = output.spinner(
output.spinner(
isSettingUpProject
? 'Setting up project'
: `Deploying ${chalk.bold(`${org.slug}/${projectName}`)}`,
@@ -138,16 +134,7 @@ export default async function processDeployment({
.map((sha: string) => event.payload.total.get(sha).data.length)
.reduce((a: number, b: number) => a + b, 0);
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
if (deploySpinner) {
deploySpinner();
}
deployingSpinner();
output.stopSpinner();
bar = new Progress(`${chalk.gray('>')} Upload [:bar] :percent :etas`, {
width: 20,
complete: '=',
@@ -170,8 +157,6 @@ export default async function processDeployment({
}
if (event.type === 'created') {
deployingSpinner();
if (bar && !bar.complete) {
bar.tick(bar.total + 1);
}
@@ -191,63 +176,36 @@ export default async function processDeployment({
now.url = event.payload.url;
output.stopSpinner();
printInspectUrl(output, event.payload.url, deployStamp, org.slug);
if (quiet) {
process.stdout.write(`https://${event.payload.url}`);
}
if (queuedSpinner === null) {
queuedSpinner =
event.payload.readyState === 'QUEUED'
? output.spinner('Queued', 0)
: output.spinner('Building', 0);
}
output.spinner(
event.payload.readyState === 'QUEUED' ? 'Queued' : 'Building',
0
);
}
if (event.type === 'building') {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner === null) {
buildSpinner = output.spinner('Building', 0);
}
output.spinner('Building', 0);
}
if (event.type === 'canceled') {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
output.stopSpinner();
return event.payload;
}
if (event.type === 'ready') {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
deploySpinner = output.spinner('Completing', 0);
output.spinner('Completing', 0);
}
// Handle error events
if (event.type === 'error') {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
if (deploySpinner) {
deploySpinner();
}
deployingSpinner();
output.stopSpinner();
const error = await now.handleDeploymentError(event.payload, {
hashes,
@@ -263,32 +221,12 @@ export default async function processDeployment({
// Handle alias-assigned event
if (event.type === 'alias-assigned') {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
if (deploySpinner) {
deploySpinner();
}
deployingSpinner();
event.payload.indications = indications;
return event.payload;
}
}
} catch (err) {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
if (deploySpinner) {
deploySpinner();
}
deployingSpinner();
output.stopSpinner();
throw err;
}
}

View File

@@ -222,14 +222,14 @@ async function npmInstall(
) {
const sortedPackages = packagesToInstall.sort();
const stopSpinner = silent
? () => {}
: output.spinner(
`Installing ${pluralize(
'Runtime',
sortedPackages.length
)}: ${sortedPackages.join(', ')}`
);
if (!silent) {
output.spinner(
`Installing ${pluralize(
'Runtime',
sortedPackages.length
)}: ${sortedPackages.join(', ')}`
);
}
output.debug(`Running npm install in ${cwd}`);
@@ -253,7 +253,7 @@ async function npmInstall(
stdio: output.isDebugEnabled() ? 'inherit' : 'pipe',
});
if (result.failed) {
stopSpinner();
output.stopSpinner();
if (result.stdout) {
console.log(result.stdout);
}
@@ -272,7 +272,7 @@ async function npmInstall(
});
}
} finally {
stopSpinner();
output.stopSpinner();
}
}

View File

@@ -280,7 +280,6 @@ export async function executeBuild(
path = extensionless;
}
delete output[path];
output[path] = value;
});
@@ -402,7 +401,7 @@ export async function getBuildMatches(
const builds = nowConfig.builds || [{ src: '**', use: '@vercel/static' }];
for (const buildConfig of builds) {
let { src = '**', use } = buildConfig;
let { src = '**', use, config = {} } = buildConfig;
if (!use) {
continue;
@@ -437,6 +436,15 @@ export async function getBuildMatches(
for (const file of files) {
src = relative(cwd, file);
// Remove the output directory prefix
if (config.zeroConfig && config.outputDirectory) {
const outputMatch = config.outputDirectory + '/';
if (src.startsWith(outputMatch)) {
src = src.slice(outputMatch.length);
}
}
const builderWithPkg = await getBuilder(use, output);
matches.push({
...buildConfig,

View File

@@ -41,10 +41,11 @@ import {
spawnCommand,
isOfficialRuntime,
} from '@vercel/build-utils';
import _frameworks, { Framework } from '@vercel/frameworks';
import frameworkList from '@vercel/frameworks';
import cmd from '../output/cmd';
import link from '../output/link';
import sleep from '../sleep';
import { Output } from '../output';
import { relative } from '../path-helpers';
import { getDistTag } from '../get-dist-tag';
@@ -89,7 +90,6 @@ import {
import { ProjectEnvVariable, ProjectSettings } from '../../types';
import exposeSystemEnvs from './expose-system-envs';
const frameworkList = _frameworks as Framework[];
const frontendRuntimeSet = new Set(
frameworkList.map(f => f.useRuntime?.use || '@vercel/static-build')
);
@@ -2377,10 +2377,6 @@ function needsBlockingBuild(buildMatch: BuildMatch): boolean {
return typeof builder.shouldServe !== 'function';
}
async function sleep(n: number) {
return new Promise(resolve => setTimeout(resolve, n));
}
async function checkForPort(
port: number | undefined,
timeout: number

View File

@@ -1,7 +1,7 @@
import { basename, extname, join } from 'path';
import {
FileFsRef,
BuildOptions,
shouldServe as defaultShouldServe,
ShouldServeOptions,
} from '@vercel/build-utils';
import { BuildResult } from './types';
@@ -11,50 +11,32 @@ export const version = 2;
export function build({
files,
entrypoint,
config,
config: { zeroConfig, outputDirectory },
}: BuildOptions): BuildResult {
let path = entrypoint;
const outputDir = config.zeroConfig ? config.outputDirectory : '';
const outputMatch = outputDir + '/';
if (outputDir && path.startsWith(outputMatch)) {
// static output files are moved to the root directory
path = path.slice(outputMatch.length);
}
const output = {
[path]: files[entrypoint] as FileFsRef,
const path =
zeroConfig && outputDirectory
? `${outputDirectory}/${entrypoint}`
: entrypoint;
return {
output: {
[entrypoint]: files[path] as FileFsRef,
},
routes: [],
watch: [path],
};
const watch = [path];
return { output, routes: [], watch };
}
export function shouldServe({
entrypoint,
files,
requestPath,
config = {},
}: ShouldServeOptions) {
let outputPrefix = '';
const outputDir = config.zeroConfig ? config.outputDirectory : '';
const outputMatch = outputDir + '/';
if (outputDir && entrypoint.startsWith(outputMatch)) {
// static output files are moved to the root directory
entrypoint = entrypoint.slice(outputMatch.length);
outputPrefix = outputMatch;
export function shouldServe(_opts: ShouldServeOptions) {
const opts = { ..._opts };
let {
config: { zeroConfig, outputDirectory },
} = opts;
// Add the output directory prefix
if (zeroConfig && outputDirectory) {
opts.entrypoint = `${outputDirectory}/${opts.entrypoint}`;
opts.requestPath = `${outputDirectory}/${opts.requestPath}`;
}
const isMatch = (f: string) => entrypoint === f && outputPrefix + f in files;
if (isIndex(entrypoint)) {
const indexPath = join(requestPath, basename(entrypoint));
if (isMatch(indexPath)) {
return true;
}
}
return isMatch(requestPath);
}
function isIndex(path: string): boolean {
const ext = extname(path);
const name = basename(path, ext);
return name === 'index';
return defaultShouldServe(opts);
}

View File

@@ -19,7 +19,7 @@ export default async function purchaseDomainIfAvailable(
domain: string,
contextName: string
) {
const cancelWait = output.spinner(`Checking status of ${chalk.bold(domain)}`);
output.spinner(`Checking status of ${chalk.bold(domain)}`);
const buyDomainStamp = stamp();
const { available } = await getDomainStatus(client, domain);
@@ -32,7 +32,7 @@ export default async function purchaseDomainIfAvailable(
output.debug(`Domain ${domain} is available to be purchased`);
const domainPrice = await getDomainPrice(client, domain).finally(() => {
cancelWait();
output.stopSpinner();
});
if (domainPrice instanceof ERRORS.UnsupportedTLD) {

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