mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-25 11:49:15 +00:00
Compare commits
120 Commits
@now/node@
...
@now/next@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f128e69e6 | ||
|
|
f1487c92cb | ||
|
|
45066cdf44 | ||
|
|
7dde9c8207 | ||
|
|
fd964f825d | ||
|
|
1656c9874e | ||
|
|
d999a3b2ad | ||
|
|
cfb7c9e632 | ||
|
|
06e8472cf7 | ||
|
|
92404135d8 | ||
|
|
8b5cc23d7c | ||
|
|
e10b42bfdc | ||
|
|
36f7ec4836 | ||
|
|
f89d1c463e | ||
|
|
5dc652eba9 | ||
|
|
821b2bd50b | ||
|
|
8d5f6f1fa3 | ||
|
|
617f610c07 | ||
|
|
3229ba0106 | ||
|
|
d16a6e4716 | ||
|
|
4e5156fb0b | ||
|
|
3c013cc36b | ||
|
|
1ed83135be | ||
|
|
0e71ff2df1 | ||
|
|
d8e98ee476 | ||
|
|
1bc1e8a9b1 | ||
|
|
274fdeb49a | ||
|
|
d15c90b42f | ||
|
|
5be2917c47 | ||
|
|
5f08b24546 | ||
|
|
cc3026ffcb | ||
|
|
8cb6e56679 | ||
|
|
7da5ff4b1d | ||
|
|
5d1069d464 | ||
|
|
6e1a72f557 | ||
|
|
6f97a2cc7a | ||
|
|
6e402bffc3 | ||
|
|
e41d0f8e68 | ||
|
|
1a046744f2 | ||
|
|
a9bf011f2c | ||
|
|
60c76b3290 | ||
|
|
c7ff1e7044 | ||
|
|
e2deaef54a | ||
|
|
b2c7386c83 | ||
|
|
3dce84c3cf | ||
|
|
443b1ac158 | ||
|
|
be3dca3d94 | ||
|
|
cb135e4329 | ||
|
|
55c73b68bb | ||
|
|
eb793ba139 | ||
|
|
00908e8ebe | ||
|
|
5ce7ff86a4 | ||
|
|
4b34ee2993 | ||
|
|
646bd288ba | ||
|
|
8aba6f1ff8 | ||
|
|
35e5e328aa | ||
|
|
bdd4953d62 | ||
|
|
e938b298e2 | ||
|
|
b67f324e10 | ||
|
|
1dfa286af5 | ||
|
|
d4391bd4cc | ||
|
|
53eb71f26d | ||
|
|
92ffd654b5 | ||
|
|
36b83f1606 | ||
|
|
7a79f620c0 | ||
|
|
f3b286ecf3 | ||
|
|
adf31c3fcc | ||
|
|
deacdfc47c | ||
|
|
ac9badbe9e | ||
|
|
62beb0f78d | ||
|
|
5f9777f4af | ||
|
|
e00db4437a | ||
|
|
b0e4f2590d | ||
|
|
f0d58eac8c | ||
|
|
dae830d2b6 | ||
|
|
e3071e4e29 | ||
|
|
073d7ece23 | ||
|
|
071258ba33 | ||
|
|
c0e00dc69a | ||
|
|
6e5c136337 | ||
|
|
60428cd4cf | ||
|
|
c6b9d80eec | ||
|
|
953bdc10e5 | ||
|
|
becdbd2136 | ||
|
|
da9bb31259 | ||
|
|
8cbf036921 | ||
|
|
8f66e4a308 | ||
|
|
9627b612f2 | ||
|
|
8a9ded6d61 | ||
|
|
d89c772bd5 | ||
|
|
32137586b9 | ||
|
|
9a3e435175 | ||
|
|
01d5a10ebd | ||
|
|
040658fbfa | ||
|
|
51c00286a4 | ||
|
|
c3e274fc2f | ||
|
|
fe633c528a | ||
|
|
5e306d49f8 | ||
|
|
274259b7dd | ||
|
|
230e96c687 | ||
|
|
8c0c6e546d | ||
|
|
5e8541b936 | ||
|
|
300558f24e | ||
|
|
829f7d8aeb | ||
|
|
340f7db68a | ||
|
|
2fe987b5da | ||
|
|
5b3aa48cd6 | ||
|
|
080d96bfa1 | ||
|
|
7d9bf682b4 | ||
|
|
a913a4f59f | ||
|
|
2042c96d98 | ||
|
|
4ca0ff8426 | ||
|
|
554cc42d83 | ||
|
|
3f9e30d031 | ||
|
|
5a9ca8644c | ||
|
|
3f362d4b50 | ||
|
|
7360886c9a | ||
|
|
5b8a1b47b0 | ||
|
|
0506b262d2 | ||
|
|
17c4569f41 |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,3 +1,7 @@
|
||||
# Ignore test fixtures in GitHub Languages
|
||||
# See https://github.com/github/linguist#vendored-code
|
||||
packages/*/test/* linguist-vendored
|
||||
|
||||
# Go build fails with Windows line endings.
|
||||
*.go text eol=lf
|
||||
go.mod text eol=lf
|
||||
|
||||
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -2,6 +2,7 @@
|
||||
# https://help.github.com/en/articles/about-code-owners
|
||||
|
||||
* @tootallnate @leo
|
||||
/.github/workflows @AndyBitz @styfle
|
||||
/packages/frameworks @AndyBitz
|
||||
/packages/now-cli/src/commands/dev/ @tootallnate @leo @styfle @AndyBitz
|
||||
/packages/now-cli/src/util/dev/ @tootallnate @leo @styfle @AndyBitz
|
||||
|
||||
14
.github/CONTRIBUTING.md
vendored
14
.github/CONTRIBUTING.md
vendored
@@ -97,13 +97,9 @@ Sometimes you want to test changes to a Builder against an existing project, may
|
||||
|
||||
## Add a New Framework
|
||||
|
||||
You can add support for a new framework by creating a Pull Request for this repository by following the steps below.
|
||||
You can add support for a new Framework by creating a Pull Request for this repository and following the steps below:
|
||||
|
||||
1. Add the framework to the `@now/frameworks` package.
|
||||
The file is located in `packages/frameworks/frameworks.json`. You can copy the structure of an existing one and adjust the required fields. Note that the `settings` property either contains a `value` or a `placeholder`. The `value` property is used when something is not configurable, the `placeholder` is used when something is configurable and can be changed with configuration. An example would be the output directory for Hugo, it's `public` by default but can be changed through its config file, so we use `placeholder` with an explanation of what can be used.
|
||||
2. Add an example to the `examples/` directory.
|
||||
The name of the directory should equal the slug of the framework used in `@now/frameworks`.
|
||||
The `.github/EXAMPLE_README_TEMPLATE.md` file can be used to create a README for the example.
|
||||
3. Update the `@now/static-build` package
|
||||
The files `packages/now-static-build/src/frameworks.ts` has to be extended. You can add default routes that will always be applied to projects that use this framework or specify some paths that will be cached to speed up the build process.
|
||||
4. After your PR has been merged and released other users can use `now init` to get the example and deploy it to ZEIT Now.
|
||||
1. Add the Framework to the `@now/frameworks` package: The file is located in `packages/frameworks/frameworks.json`. You can copy the structure of an existing one and adjust the required fields. Note that the `settings` property either contains a `value` or a `placeholder`. The `value` property is used when something is not configurable, the `placeholder` is used when something is configurable and can be changed with configuration. An example would be the Output Directory for Hugo, it's `public` by default but can be changed through its config file, so we use `placeholder` with an explanation of what can be used.
|
||||
2. Add an example to the `examples/` directory: The name of the directory should equal the slug of the framework used in `@now/frameworks`. The `.github/EXAMPLE_README_TEMPLATE.md` file can be used to create a `README.md` file for the example.
|
||||
3. Update the `@now/static-build` package: The file `packages/now-static-build/src/frameworks.ts` has to be extended. You can add default routes that will always be applied to projects that use this Framework or specify some paths that will be cached to speed up the build process.
|
||||
4. After your Pull Request has been merged and released, other users can select the example on the ZEIT Now dashboard and deploy it.
|
||||
|
||||
2
.github/EXAMPLE_README_TEMPLATE.md
vendored
2
.github/EXAMPLE_README_TEMPLATE.md
vendored
@@ -6,7 +6,7 @@ This directory is a brief example of a [Name](site-link) site that can be deploy
|
||||
|
||||
Deploy your own [Name] project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now-examples/tree/master/example-directory)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now-examples/tree/master/example-directory)
|
||||
|
||||
### How We Created This Example
|
||||
|
||||
|
||||
18
.github/workflows/cancel.yml
vendored
Normal file
18
.github/workflows/cancel.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
name: Cancel
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
- '!master'
|
||||
|
||||
jobs:
|
||||
cancel:
|
||||
name: 'Cancel Previous Runs'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 3
|
||||
steps:
|
||||
- uses: styfle/cancel-workflow-action@0.2.0
|
||||
with:
|
||||
workflow_id: 435869
|
||||
access_token: ${{ secrets.GITHUB_WORKFLOW_TOKEN }}
|
||||
|
||||
35
.github/workflows/continuous-integration.yml
vendored
35
.github/workflows/continuous-integration.yml
vendored
@@ -12,18 +12,24 @@ jobs:
|
||||
test-unit:
|
||||
name: Unit Tests
|
||||
timeout-minutes: 15
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
node: [10, 12]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch origin master --depth=10
|
||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 10
|
||||
- run: yarn install && yarn run build
|
||||
- run: yarn install
|
||||
- run: yarn run build
|
||||
- run: yarn run test-lint
|
||||
- run: yarn run test-unit --clean false
|
||||
- uses: actions/upload-artifact@v1
|
||||
- name: Upload Artifact
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.node == 12 # only run once
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: test-unit-output
|
||||
path: packages/now-cli/.nyc_output
|
||||
@@ -36,7 +42,8 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch origin master --depth=10
|
||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
||||
- run: yarn install && yarn run build
|
||||
- run: yarn install
|
||||
- run: yarn run build
|
||||
- run: yarn test-integration-once --clean false
|
||||
|
||||
test-now-cli:
|
||||
@@ -45,15 +52,18 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
os: [ubuntu-latest]
|
||||
node: [10, 12]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch origin master --depth=10
|
||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
||||
- run: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.56.0/hugo_0.56.0_macOS-64bit.tar.gz && tar -xzf hugo_0.56.0_macOS-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
||||
- run: yarn install && yarn run build
|
||||
- name: Install Hugo
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.56.0/hugo_0.56.0_macOS-64bit.tar.gz && tar -xzf hugo_0.56.0_macOS-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
||||
- run: yarn install
|
||||
- run: yarn run build
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
@@ -72,8 +82,11 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch origin master --depth=10
|
||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
||||
- run: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.56.0/hugo_0.56.0_macOS-64bit.tar.gz && tar -xzf hugo_0.56.0_macOS-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
||||
- run: yarn install && yarn run build
|
||||
- name: Install Hugo
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.56.0/hugo_0.56.0_macOS-64bit.tar.gz && tar -xzf hugo_0.56.0_macOS-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
||||
- run: yarn install
|
||||
- run: yarn run build
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||

|
||||

|
||||
|
||||
[](https://circleci.com/gh/zeit/workflows/now/tree/master)
|
||||
[](https://github.com/zeit/now/actions?workflow=CI)
|
||||
[](https://spectrum.chat/zeit)
|
||||
|
||||
## Usage
|
||||
@@ -16,13 +16,12 @@ To quickly start a new project, run the following commands:
|
||||
```
|
||||
now init # Pick an example project to clone
|
||||
cd <PROJECT> # Change directory to the newly created project
|
||||
now dev # Run locally during development
|
||||
now # Deploy to the cloud
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
For details on how to use Now CLI, check out our [documentation](https://zeit.co/docs).
|
||||
For details on how to use Now CLI, check out our [documentation](https://zeit.co/docs/now-cli).
|
||||
|
||||
## Caught a Bug?
|
||||
|
||||
|
||||
@@ -2,17 +2,19 @@ import { NowRequest, NowResponse } from '@now/node';
|
||||
import { withApiHandler } from './_lib/util/with-api-handler';
|
||||
import frameworkList, { Framework } from '../packages/frameworks';
|
||||
|
||||
const frameworks: Framework[] = (frameworkList as Framework[]).map(
|
||||
framework => {
|
||||
delete framework.detectors;
|
||||
const frameworks = (frameworkList as Framework[]).map(frameworkItem => {
|
||||
const framework = {
|
||||
...frameworkItem,
|
||||
hasDetectors: Boolean(frameworkItem.detectors),
|
||||
detectors: undefined,
|
||||
};
|
||||
|
||||
if (framework.logo) {
|
||||
framework.logo = `https://res.cloudinary.com/zeit-inc/image/fetch/${framework.logo}`;
|
||||
}
|
||||
|
||||
return framework;
|
||||
if (framework.logo) {
|
||||
framework.logo = `https://res.cloudinary.com/zeit-inc/image/fetch/${framework.logo}`;
|
||||
}
|
||||
);
|
||||
|
||||
return framework;
|
||||
});
|
||||
|
||||
export default withApiHandler(async function(
|
||||
req: NowRequest,
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of an [AMP](https://amp.dev/) site that can be
|
||||
|
||||
Deploy your own AMP project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/amp)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/amp)
|
||||
|
||||
_Live Example: https://amp.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# Angular Example
|
||||
|
||||
@@ -8,7 +8,7 @@ This directory is a brief example of an [Angular](https://angular.io/) app that
|
||||
|
||||
Deploy your own Angular project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/angular)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/angular)
|
||||
|
||||
_Live Example: https://angular.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Assemble](http://assemble.io/) app that
|
||||
|
||||
Deploy your own Assemble project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/assemble)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/assemble)
|
||||
|
||||
_Live Example: https://assemble.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of an [Aurelia](https://aurelia.io/) app that
|
||||
|
||||
Deploy your own Aurelia project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/aurelia)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/aurelia)
|
||||
|
||||
_Live Example: https://aurelia.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Brunch](https://brunch.io/) site that ca
|
||||
|
||||
Deploy your own Brunch project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/brunch)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/brunch)
|
||||
|
||||
_Live Example: https://brunch.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Charge.js](https://charge.js.org/) site
|
||||
|
||||
Deploy your own Charge.js project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/charge)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/charge)
|
||||
|
||||
_Live Example: https://charge.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# React Example
|
||||
|
||||
@@ -8,7 +8,7 @@ This directory is a brief example of a [React](https://reactjs.org/) app with [S
|
||||
|
||||
Deploy your own React project, along with Serverless Functions, with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/create-react-app-functions)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/create-react-app-functions)
|
||||
|
||||
_Live Example: https://create-react-app.now-examples.now.sh/_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of using a Custom Build script that can be dep
|
||||
|
||||
Deploy your own Custom Built project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/custom-build)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/custom-build)
|
||||
|
||||
_Live Example: https://custom-build.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# Docusaurus Example
|
||||
|
||||
@@ -8,7 +8,7 @@ This directory is a brief example of a [Docusaurus](https://docusaurus.io/) site
|
||||
|
||||
Deploy your own Docusaurus project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/docusaurus)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/docusaurus)
|
||||
|
||||
_Live Example: https://docusaurus.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Docz](https://www.docz.site/) site that
|
||||
|
||||
Deploy your own Docz project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/docz)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/docz)
|
||||
|
||||
_Live Example: https://docz.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# Eleventy Example
|
||||
|
||||
@@ -8,7 +8,7 @@ This directory is a brief example of a [Eleventy](https://www.11ty.io/) site tha
|
||||
|
||||
Deploy your own Eleventy project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/eleventy)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/eleventy)
|
||||
|
||||
_Live Example: https://eleventy.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# Ember Example
|
||||
|
||||
@@ -8,7 +8,7 @@ This directory is a brief example of an [Ember](https://emberjs.com/) app that c
|
||||
|
||||
Deploy your own Ember project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/ember)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/ember)
|
||||
|
||||
_Live Example: https://ember.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Foundation](https://foundation.zurb.com/
|
||||
|
||||
Deploy your own Foundation project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/foundation)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/foundation)
|
||||
|
||||
_Live Example: https://foundation.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# Gatsby Example
|
||||
|
||||
@@ -8,7 +8,7 @@ This directory is a brief example of a [Gatsby](https://www.gatsbyjs.org/) app w
|
||||
|
||||
Deploy your own Gatsby project, along with Serverless Functions, with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/gatsby)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/gatsby)
|
||||
|
||||
_Live Example: https://gatsby.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# Gridsome Example
|
||||
|
||||
@@ -8,7 +8,7 @@ This directory is a brief example of a [Gridsome](https://gridsome.org/) app tha
|
||||
|
||||
Deploy your own Gridsome project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/gridsome)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/gridsome)
|
||||
|
||||
_Live Example: https://gridsome.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# Hexo Example
|
||||
|
||||
@@ -8,7 +8,7 @@ This directory is a brief example of a [Hexo](https://hexo.io/) site that can be
|
||||
|
||||
Deploy your own Hexo project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/hexo)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/hexo)
|
||||
|
||||
_Live Example: https://hexo.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Hugo](https://gohugo.io/) app that can b
|
||||
|
||||
Deploy your own Hugo project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/hugo)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/hugo)
|
||||
|
||||
_Live Example: https://hugo.now-examples.now.sh_
|
||||
|
||||
|
||||
2
examples/hugo/themes/ananke/.gitignore
vendored
2
examples/hugo/themes/ananke/.gitignore
vendored
@@ -28,3 +28,5 @@ npm-debug.log
|
||||
|
||||
/junit.xml
|
||||
partials/structure/stylesheet.html
|
||||
|
||||
!dist
|
||||
|
||||
3
examples/hugo/themes/ananke/static/dist/css/app.d98f2eb6bcd1eaedb7edf166bd16af26.css
vendored
Normal file
3
examples/hugo/themes/ananke/static/dist/css/app.d98f2eb6bcd1eaedb7edf166bd16af26.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
examples/hugo/themes/ananke/static/dist/js/app.3fc0f988d21662902933.js
vendored
Normal file
1
examples/hugo/themes/ananke/static/dist/js/app.3fc0f988d21662902933.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!function(n){function t(e){if(r[e])return r[e].exports;var o=r[e]={i:e,l:!1,exports:{}};return n[e].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var r={};t.m=n,t.c=r,t.i=function(n){return n},t.d=function(n,r,e){t.o(n,r)||Object.defineProperty(n,r,{configurable:!1,enumerable:!0,get:e})},t.n=function(n){var r=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(r,"a",r),r},t.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},t.p="",t(t.s=1)}([function(n,t){},function(n,t,r){"use strict";var e=r(0);!function(n){n&&n.__esModule}(e)}]);
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [HyperApp](https://github.com/jorgebucara
|
||||
|
||||
Deploy your own HyperApp project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/hyperapp)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/hyperapp)
|
||||
|
||||
_Live Example: https://hyperapp.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of an [Ionic React](https://ionicframework.com
|
||||
|
||||
Deploy your own Ionic React project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/ionic-react)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/ionic-react)
|
||||
|
||||
_Live Example: https://ionic-react.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Jekyll](https://jekyllrb.com/) site that
|
||||
|
||||
Deploy your own Jekyll project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/jekyll)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/jekyll)
|
||||
|
||||
_Live Example: https://jekyll.now-examples.now.sh_
|
||||
|
||||
@@ -25,18 +25,3 @@ You can deploy your new Jekyll project with a single command from your terminal
|
||||
```shell
|
||||
$ now
|
||||
```
|
||||
|
||||
### Build Command
|
||||
|
||||
The default build command is `jekyll build`.
|
||||
|
||||
If you wish to change the build command, add a `package.json` file with the following:
|
||||
|
||||
```json
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "jekyll build"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Marko.js](https://markojs.com/) app that
|
||||
|
||||
Deploy your own Marko.js project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/marko)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/marko)
|
||||
|
||||
_Live Example: https://marko.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [mdx-deck](https://github.com/jxnblk/mdx-
|
||||
|
||||
Deploy your own mdx-deck project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/mdx-deck)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/mdx-deck)
|
||||
|
||||
_Live Example: https://mdx-deck.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Metalsmith](https://metalsmith.io/) app
|
||||
|
||||
Deploy your own Metalsmith project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/metalsmith)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/metalsmith)
|
||||
|
||||
_Live Example: https://metalsmith.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Middleman](https://middlemanapp.com/) si
|
||||
|
||||
Deploy your own Middleman project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/middleman)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/middleman)
|
||||
|
||||
_Live Example: https://middleman.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Mithril](https://mithril.js.org/) app th
|
||||
|
||||
Deploy your own Mithril project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/mithril)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/mithril)
|
||||
|
||||
_Live Example: https://mithril.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [MkDocs](https://www.mkdocs.org/) site th
|
||||
|
||||
Deploy your own MkDocs project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/mkdocs)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/mkdocs)
|
||||
|
||||
_Live Example: https://mkdocs.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# Next.js Example
|
||||
|
||||
@@ -8,7 +8,7 @@ This directory is a brief example of a [Next.js](https://nextjs.org) app that ca
|
||||
|
||||
Deploy your own Next.js project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/nextjs)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/nextjs)
|
||||
|
||||
_Live Example: https://nextjs.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Nuxt.js](https://nuxtjs.org) app that ca
|
||||
|
||||
Deploy your own Nuxt.js project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/nuxtjs)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/nuxtjs)
|
||||
|
||||
_Live Example: https://nuxtjs.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Pelican](https://docs.getpelican.com/en/
|
||||
|
||||
Deploy your own Pelican project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/pelican)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/pelican)
|
||||
|
||||
_Live Example: https://pelican.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# Polymer Example
|
||||
|
||||
@@ -8,7 +8,7 @@ This directory is a brief example of a [Polymer](https://www.polymer-project.org
|
||||
|
||||
Deploy your own Polymer project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/polymer)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/polymer)
|
||||
|
||||
_Live Example: https://polymer.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# Preact Example
|
||||
|
||||
@@ -8,7 +8,7 @@ This directory is a brief example of a [Preact](https://preactjs.com/) app that
|
||||
|
||||
Deploy your own Preact project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/preact)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/preact)
|
||||
|
||||
_Live Example: https://preact.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Riot.js](https://riot.js.org/) app that
|
||||
|
||||
Deploy your own Riot.js project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/riot)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/riot)
|
||||
|
||||
_Live Example: https://riot.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# Saber Example
|
||||
|
||||
@@ -8,7 +8,7 @@ This directory is a brief example of a [Saber](https://saber.land) site that can
|
||||
|
||||
Deploy your own Saber project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/saber)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/saber)
|
||||
|
||||
_Live Example: https://saber.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Sapper](https://sapper.svelte.dev/) app
|
||||
|
||||
Deploy your own Sapper project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/sapper)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/sapper)
|
||||
|
||||
_Live Example: https://sapper.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Stencil](https://stenciljs.com/) app tha
|
||||
|
||||
Deploy your own Stencil project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/stencil)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/stencil)
|
||||
|
||||
_Live Example: https://stencil.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Storybook](https://storybook.js.org/) ap
|
||||
|
||||
Deploy your own Storybook project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/storybook)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/storybook)
|
||||
|
||||
_Live Example: https://storybook.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Svelte](https://svelte.dev/) app with [S
|
||||
|
||||
Deploy your own Svelte project, along with Serverless Functions, with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/svelte)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/svelte)
|
||||
|
||||
_Live Example: https://svelte.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# UmiJS Example
|
||||
|
||||
@@ -8,7 +8,7 @@ This directory is a brief example of a [UmiJS](https://umijs.org/) app that can
|
||||
|
||||
Deploy your own UmiJS project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/umijs)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/umijs)
|
||||
|
||||
_Live Example: https://umijs.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a vanilla site that can be deployed with ZE
|
||||
|
||||
Deploy your own vanilla website, along with Serverless Functions, with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/vanilla)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/vanilla)
|
||||
|
||||
_Live Example: https://vanilla.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
# Vue.js Example
|
||||
|
||||
@@ -8,7 +8,7 @@ This directory is a brief example of a [Vue.js](https://vuejs.org/) app that can
|
||||
|
||||
Deploy your own Vue.js project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/vue)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/vue)
|
||||
|
||||
_Live Example: https://vue.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [VuePress](https://vuepress.vuejs.org/) a
|
||||
|
||||
Deploy your own VuePress project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/vuepress)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/vuepress)
|
||||
|
||||
_Live Example: https://vuepress.now-examples.now.sh_
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory is a brief example of a [Zola](https://www.getzola.org/) site tha
|
||||
|
||||
Deploy your own Zola project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/zola)
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/zola)
|
||||
|
||||
_Live Example: https://zola.now-examples.now.sh_
|
||||
|
||||
|
||||
9
now.json
9
now.json
@@ -1,15 +1,18 @@
|
||||
{
|
||||
"redirects": [
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "/",
|
||||
"destination": "https://zeit.co/new",
|
||||
"statusCode": 307
|
||||
"destination": "/api/frameworks"
|
||||
}
|
||||
],
|
||||
"env": {
|
||||
"GITHUB_ACCESS_TOKEN": "@now-api-examples-github-token",
|
||||
"SENTRY_DSN": "@sentry-product-dsn"
|
||||
},
|
||||
"github": {
|
||||
"silent": true,
|
||||
"autoJobCancelation": true
|
||||
},
|
||||
"headers": [
|
||||
{
|
||||
"source": "/api/frameworks",
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
"@typescript-eslint/camelcase": 0,
|
||||
"@typescript-eslint/explicit-function-return-type": 0,
|
||||
"@typescript-eslint/no-empty-function": 0,
|
||||
"@typescript-eslint/no-unused-vars": 2,
|
||||
"@typescript-eslint/no-use-before-define": 0
|
||||
},
|
||||
"overrides": [
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`next build` or `build` from `package.json`"
|
||||
"placeholder": "`build` from `package.json` or `next build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "next dev --port $PORT"
|
||||
@@ -808,5 +808,22 @@
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/foundation.svg",
|
||||
"tagline": "Foundation is the most advanced responsive front-end framework in the world.",
|
||||
"description": "A Foundation app, created with the Foundation CLI."
|
||||
},
|
||||
{
|
||||
"name": "Other",
|
||||
"slug": null,
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/other.svg",
|
||||
"description": "No framework or a unoptimized framework.",
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`build` or `now-build` from `package.json` if it exists"
|
||||
},
|
||||
"devCommand": {
|
||||
"placeholder": "None"
|
||||
},
|
||||
"outputDirectory": {
|
||||
"placeholder": "`public` if it exists, or `.`"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
8
packages/frameworks/index.d.ts
vendored
8
packages/frameworks/index.d.ts
vendored
@@ -13,11 +13,11 @@ export type Setting = SettingValue | SettingPlaceholder;
|
||||
|
||||
export interface Framework {
|
||||
name: string;
|
||||
slug: string;
|
||||
slug: string | null;
|
||||
logo: string;
|
||||
demo: string;
|
||||
tagline: string;
|
||||
website: string;
|
||||
demo?: string;
|
||||
tagline?: string;
|
||||
website?: string;
|
||||
description: string;
|
||||
detectors?: {
|
||||
every?: FrameworkDetectionItem[];
|
||||
|
||||
3
packages/frameworks/logos/other.svg
Normal file
3
packages/frameworks/logos/other.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 4L16 15H4L10 4Z" stroke="#666666" stroke-dasharray="2 2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 215 B |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/frameworks",
|
||||
"version": "0.0.7",
|
||||
"version": "0.0.10",
|
||||
"main": "frameworks.json",
|
||||
"license": "UNLICENSED"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/build-utils",
|
||||
"version": "1.3.7-canary.1",
|
||||
"version": "2.0.1-canary.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,432 +0,0 @@
|
||||
import { parse as parsePath, extname } from 'path';
|
||||
import { Route, Source } from '@now/routing-utils';
|
||||
import { Builder } from './types';
|
||||
import { getIgnoreApiFilter, sortFiles } from './detect-builders';
|
||||
|
||||
function escapeName(name: string) {
|
||||
const special = '[]^$.|?*+()'.split('');
|
||||
|
||||
for (const char of special) {
|
||||
name = name.replace(new RegExp(`\\${char}`, 'g'), `\\${char}`);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
function joinPath(...segments: string[]) {
|
||||
const joinedPath = segments.join('/');
|
||||
return joinedPath.replace(/\/{2,}/g, '/');
|
||||
}
|
||||
|
||||
function concatArrayOfText(texts: string[]): string {
|
||||
if (texts.length <= 2) {
|
||||
return texts.join(' and ');
|
||||
}
|
||||
|
||||
const last = texts.pop();
|
||||
return `${texts.join(', ')}, and ${last}`;
|
||||
}
|
||||
|
||||
// Takes a filename or foldername, strips the extension
|
||||
// gets the part between the "[]" brackets.
|
||||
// It will return `null` if there are no brackets
|
||||
// and therefore no segment.
|
||||
function getSegmentName(segment: string): string | null {
|
||||
const { name } = parsePath(segment);
|
||||
|
||||
if (name.startsWith('[') && name.endsWith(']')) {
|
||||
return name.slice(1, -1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function createRouteFromPath(
|
||||
filePath: string,
|
||||
featHandleMiss: boolean,
|
||||
cleanUrls: boolean
|
||||
): { route: Source; isDynamic: boolean } {
|
||||
const parts = filePath.split('/');
|
||||
|
||||
let counter = 1;
|
||||
const query: string[] = [];
|
||||
let isDynamic = false;
|
||||
|
||||
const srcParts = parts.map((segment, i): string => {
|
||||
const name = getSegmentName(segment);
|
||||
const isLast = i === parts.length - 1;
|
||||
|
||||
if (name !== null) {
|
||||
// We can't use `URLSearchParams` because `$` would get escaped
|
||||
query.push(`${name}=$${counter++}`);
|
||||
isDynamic = true;
|
||||
return `([^/]+)`;
|
||||
} else if (isLast) {
|
||||
const { name: fileName, ext } = parsePath(segment);
|
||||
const isIndex = fileName === 'index';
|
||||
const prefix = isIndex ? '\\/' : '';
|
||||
|
||||
const names = [
|
||||
isIndex ? prefix : `${fileName}\\/`,
|
||||
prefix + escapeName(fileName),
|
||||
featHandleMiss && cleanUrls
|
||||
? ''
|
||||
: prefix + escapeName(fileName) + escapeName(ext),
|
||||
].filter(Boolean);
|
||||
|
||||
// Either filename with extension, filename without extension
|
||||
// or nothing when the filename is `index`.
|
||||
// When `cleanUrls: true` then do *not* add the filename with extension.
|
||||
return `(${names.join('|')})${isIndex ? '?' : ''}`;
|
||||
}
|
||||
|
||||
return segment;
|
||||
});
|
||||
|
||||
const { name: fileName, ext } = parsePath(filePath);
|
||||
const isIndex = fileName === 'index';
|
||||
const queryString = `${query.length ? '?' : ''}${query.join('&')}`;
|
||||
|
||||
const src = isIndex
|
||||
? `^/${srcParts.slice(0, -1).join('/')}${srcParts.slice(-1)[0]}$`
|
||||
: `^/${srcParts.join('/')}$`;
|
||||
|
||||
let route: Source;
|
||||
if (featHandleMiss) {
|
||||
const extensionless = ext ? filePath.slice(0, -ext.length) : filePath;
|
||||
route = {
|
||||
src,
|
||||
dest: `/${extensionless}${queryString}`,
|
||||
check: true,
|
||||
};
|
||||
} else {
|
||||
route = {
|
||||
src,
|
||||
dest: `/${filePath}${queryString}`,
|
||||
};
|
||||
}
|
||||
return { route, isDynamic };
|
||||
}
|
||||
|
||||
// Check if the path partially matches and has the same
|
||||
// name for the path segment at the same position
|
||||
function partiallyMatches(pathA: string, pathB: string): boolean {
|
||||
const partsA = pathA.split('/');
|
||||
const partsB = pathB.split('/');
|
||||
|
||||
const long = partsA.length > partsB.length ? partsA : partsB;
|
||||
const short = long === partsA ? partsB : partsA;
|
||||
|
||||
let index = 0;
|
||||
|
||||
for (const segmentShort of short) {
|
||||
const segmentLong = long[index];
|
||||
|
||||
const nameLong = getSegmentName(segmentLong);
|
||||
const nameShort = getSegmentName(segmentShort);
|
||||
|
||||
// If there are no segments or the paths differ we
|
||||
// return as they are not matching
|
||||
if (segmentShort !== segmentLong && (!nameLong || !nameShort)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nameLong !== nameShort) {
|
||||
return true;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Counts how often a path occurs when all placeholders
|
||||
// got resolved, so we can check if they have conflicts
|
||||
function pathOccurrences(filePath: string, files: string[]): string[] {
|
||||
const getAbsolutePath = (unresolvedPath: string): string => {
|
||||
const { dir, name } = parsePath(unresolvedPath);
|
||||
const parts = joinPath(dir, name).split('/');
|
||||
return parts.map(part => part.replace(/\[.*\]/, '1')).join('/');
|
||||
};
|
||||
|
||||
const currentAbsolutePath = getAbsolutePath(filePath);
|
||||
|
||||
return files.reduce((prev: string[], file: string): string[] => {
|
||||
const absolutePath = getAbsolutePath(file);
|
||||
|
||||
if (absolutePath === currentAbsolutePath) {
|
||||
prev.push(file);
|
||||
} else if (partiallyMatches(filePath, file)) {
|
||||
prev.push(file);
|
||||
}
|
||||
|
||||
return prev;
|
||||
}, []);
|
||||
}
|
||||
|
||||
// Checks if a placeholder with the same name is used
|
||||
// multiple times inside the same path
|
||||
function getConflictingSegment(filePath: string): string | null {
|
||||
const segments = new Set<string>();
|
||||
|
||||
for (const segment of filePath.split('/')) {
|
||||
const name = getSegmentName(segment);
|
||||
|
||||
if (name !== null && segments.has(name)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
if (name) {
|
||||
segments.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function sortFilesBySegmentCount(fileA: string, fileB: string): number {
|
||||
const lengthA = fileA.split('/').length;
|
||||
const lengthB = fileB.split('/').length;
|
||||
|
||||
if (lengthA > lengthB) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lengthA < lengthB) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Paths that have the same segment length but
|
||||
// less placeholders are preferred
|
||||
const countSegments = (prev: number, segment: string) =>
|
||||
getSegmentName(segment) ? prev + 1 : 0;
|
||||
const segmentLengthA = fileA.split('/').reduce(countSegments, 0);
|
||||
const segmentLengthB = fileB.split('/').reduce(countSegments, 0);
|
||||
|
||||
if (segmentLengthA > segmentLengthB) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (segmentLengthA < segmentLengthB) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
interface ApiRoutesResult {
|
||||
defaultRoutes: Source[] | null;
|
||||
dynamicRoutes: Source[] | null;
|
||||
error: { [key: string]: string } | null;
|
||||
}
|
||||
|
||||
interface RoutesResult {
|
||||
defaultRoutes: Route[] | null;
|
||||
redirectRoutes: Route[] | null;
|
||||
error: { [key: string]: string } | null;
|
||||
}
|
||||
|
||||
async function detectApiRoutes(
|
||||
files: string[],
|
||||
builders: Builder[],
|
||||
featHandleMiss: boolean,
|
||||
cleanUrls: boolean
|
||||
): Promise<ApiRoutesResult> {
|
||||
if (!files || files.length === 0) {
|
||||
return {
|
||||
defaultRoutes: null,
|
||||
dynamicRoutes: null,
|
||||
error: null,
|
||||
};
|
||||
}
|
||||
|
||||
// The deepest routes need to be
|
||||
// the first ones to get handled
|
||||
const sortedFiles = files
|
||||
.filter(getIgnoreApiFilter(builders))
|
||||
.sort(sortFiles)
|
||||
.sort(sortFilesBySegmentCount);
|
||||
|
||||
const defaultRoutes: Source[] = [];
|
||||
const dynamicRoutes: Source[] = [];
|
||||
|
||||
for (const file of sortedFiles) {
|
||||
// We only consider every file in the api directory
|
||||
// as we will strip extensions as well as resolving "[segments]"
|
||||
if (
|
||||
!file.startsWith('api/') &&
|
||||
!builders.some(b => b.src === file && b.config && b.config.functions)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const conflictingSegment = getConflictingSegment(file);
|
||||
|
||||
if (conflictingSegment) {
|
||||
return {
|
||||
defaultRoutes: null,
|
||||
dynamicRoutes: null,
|
||||
error: {
|
||||
code: 'conflicting_path_segment',
|
||||
message:
|
||||
`The segment "${conflictingSegment}" occurs more than ` +
|
||||
`one time in your path "${file}". Please make sure that ` +
|
||||
`every segment in a path is unique`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const occurrences = pathOccurrences(file, sortedFiles).filter(
|
||||
name => name !== file
|
||||
);
|
||||
|
||||
if (occurrences.length > 0) {
|
||||
const messagePaths = concatArrayOfText(
|
||||
occurrences.map(name => `"${name}"`)
|
||||
);
|
||||
|
||||
return {
|
||||
defaultRoutes: null,
|
||||
dynamicRoutes: null,
|
||||
error: {
|
||||
code: 'conflicting_file_path',
|
||||
message:
|
||||
`Two or more files have conflicting paths or names. ` +
|
||||
`Please make sure path segments and filenames, without their extension, are unique. ` +
|
||||
`The path "${file}" has conflicts with ${messagePaths}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const out = createRouteFromPath(file, featHandleMiss, cleanUrls);
|
||||
if (out.isDynamic) {
|
||||
dynamicRoutes.push(out.route);
|
||||
}
|
||||
defaultRoutes.push(out.route);
|
||||
}
|
||||
|
||||
return { defaultRoutes, dynamicRoutes, error: null };
|
||||
}
|
||||
|
||||
function getPublicBuilder(builders: Builder[]): Builder | null {
|
||||
const builder = builders.find(
|
||||
builder =>
|
||||
builder.use === '@now/static' &&
|
||||
/^.*\/\*\*\/\*$/.test(builder.src) &&
|
||||
builder.config &&
|
||||
builder.config.zeroConfig === true
|
||||
);
|
||||
|
||||
return builder || null;
|
||||
}
|
||||
|
||||
export function detectOutputDirectory(builders: Builder[]): string | null {
|
||||
// TODO: We eventually want to save the output directory to
|
||||
// builder.config.outputDirectory so it is only detected once
|
||||
const publicBuilder = getPublicBuilder(builders);
|
||||
return publicBuilder ? publicBuilder.src.replace('/**/*', '') : null;
|
||||
}
|
||||
|
||||
export function detectApiDirectory(builders: Builder[]): string | null {
|
||||
// TODO: We eventually want to save the api directory to
|
||||
// builder.config.apiDirectory so it is only detected once
|
||||
const found = builders.some(
|
||||
b => b.config && b.config.zeroConfig && b.src.startsWith('api/')
|
||||
);
|
||||
return found ? 'api' : null;
|
||||
}
|
||||
|
||||
export function detectApiExtensions(builders: Builder[]): Set<string> {
|
||||
return new Set<string>(
|
||||
builders
|
||||
.filter(
|
||||
b =>
|
||||
b.config && b.config.zeroConfig && b.src && b.src.startsWith('api/')
|
||||
)
|
||||
.map(b => extname(b.src))
|
||||
.filter(Boolean)
|
||||
);
|
||||
}
|
||||
|
||||
export async function detectRoutes(
|
||||
files: string[],
|
||||
builders: Builder[],
|
||||
featHandleMiss = false,
|
||||
cleanUrls = false,
|
||||
trailingSlash?: boolean
|
||||
): Promise<RoutesResult> {
|
||||
const result = await detectApiRoutes(
|
||||
files,
|
||||
builders,
|
||||
featHandleMiss,
|
||||
cleanUrls
|
||||
);
|
||||
const { dynamicRoutes, defaultRoutes: allRoutes, error } = result;
|
||||
if (error) {
|
||||
return { defaultRoutes: null, redirectRoutes: null, error };
|
||||
}
|
||||
const directory = detectOutputDirectory(builders);
|
||||
const defaultRoutes: Route[] = [];
|
||||
const redirectRoutes: Route[] = [];
|
||||
if (allRoutes && allRoutes.length > 0) {
|
||||
const hasApiRoutes = allRoutes.some(
|
||||
r => r.dest && r.dest.startsWith('/api/')
|
||||
);
|
||||
if (featHandleMiss) {
|
||||
defaultRoutes.push({ handle: 'miss' });
|
||||
const extSet = detectApiExtensions(builders);
|
||||
if (extSet.size > 0) {
|
||||
const exts = Array.from(extSet)
|
||||
.map(ext => ext.slice(1))
|
||||
.join('|');
|
||||
const extGroup = `(?:\\.(?:${exts}))`;
|
||||
if (cleanUrls) {
|
||||
redirectRoutes.push({
|
||||
src: `^/(api(?:.+)?)/index${extGroup}?/?$`,
|
||||
headers: { Location: trailingSlash ? '/$1/' : '/$1' },
|
||||
status: 308,
|
||||
});
|
||||
redirectRoutes.push({
|
||||
src: `^/api/(.+)${extGroup}/?$`,
|
||||
headers: { Location: trailingSlash ? '/api/$1/' : '/api/$1' },
|
||||
status: 308,
|
||||
});
|
||||
} else {
|
||||
defaultRoutes.push({
|
||||
src: `^/api/(.+)${extGroup}$`,
|
||||
dest: '/api/$1',
|
||||
check: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (dynamicRoutes) {
|
||||
defaultRoutes.push(...dynamicRoutes);
|
||||
}
|
||||
if (hasApiRoutes) {
|
||||
defaultRoutes.push({
|
||||
src: '^/api(/.*)?$',
|
||||
status: 404,
|
||||
continue: true,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
defaultRoutes.push(...allRoutes);
|
||||
if (hasApiRoutes) {
|
||||
defaultRoutes.push({
|
||||
status: 404,
|
||||
src: '^/api(/.*)?$',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!featHandleMiss && directory) {
|
||||
defaultRoutes.push({
|
||||
src: '/(.*)',
|
||||
dest: `/${directory}/$1`,
|
||||
});
|
||||
}
|
||||
|
||||
return { defaultRoutes, redirectRoutes, error };
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { promisify } from 'util';
|
||||
import { lstat, Stats } from 'fs-extra';
|
||||
import FileFsRef from '../file-fs-ref';
|
||||
|
||||
type GlobOptions = vanillaGlob_.IOptions;
|
||||
export type GlobOptions = vanillaGlob_.IOptions;
|
||||
|
||||
interface FsFiles {
|
||||
[filePath: string]: FileFsRef;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Lambda, createLambda, getLambdaOptionsFromFunction } from './lambda';
|
||||
import { Prerender } from './prerender';
|
||||
import download, { DownloadedFiles, isSymbolicLink } from './fs/download';
|
||||
import getWriteableDirectory from './fs/get-writable-directory';
|
||||
import glob from './fs/glob';
|
||||
import glob, { GlobOptions } from './fs/glob';
|
||||
import rename from './fs/rename';
|
||||
import {
|
||||
execAsync,
|
||||
@@ -40,6 +40,7 @@ export {
|
||||
DownloadedFiles,
|
||||
getWriteableDirectory,
|
||||
glob,
|
||||
GlobOptions,
|
||||
rename,
|
||||
execAsync,
|
||||
spawnAsync,
|
||||
@@ -63,12 +64,11 @@ export {
|
||||
};
|
||||
|
||||
export {
|
||||
detectRoutes,
|
||||
detectBuilders,
|
||||
detectOutputDirectory,
|
||||
detectApiDirectory,
|
||||
detectApiExtensions,
|
||||
} from './detect-routes';
|
||||
export { detectBuilders } from './detect-builders';
|
||||
} from './detect-builders';
|
||||
export { detectFramework } from './detect-framework';
|
||||
export { DetectorFilesystem } from './detectors/filesystem';
|
||||
export { readConfigFile } from './fs/read-config-file';
|
||||
|
||||
@@ -8,6 +8,7 @@ interface PrerenderOptions {
|
||||
lambda: Lambda;
|
||||
fallback: FileBlob | FileFsRef | FileRef | null;
|
||||
group?: number;
|
||||
bypassToken?: string | null /* optional to be non-breaking change */;
|
||||
}
|
||||
|
||||
export class Prerender {
|
||||
@@ -16,8 +17,15 @@ export class Prerender {
|
||||
public lambda: Lambda;
|
||||
public fallback: FileBlob | FileFsRef | FileRef | null;
|
||||
public group?: number;
|
||||
public bypassToken: string | null;
|
||||
|
||||
constructor({ expiration, lambda, fallback, group }: PrerenderOptions) {
|
||||
constructor({
|
||||
expiration,
|
||||
lambda,
|
||||
fallback,
|
||||
group,
|
||||
bypassToken,
|
||||
}: PrerenderOptions) {
|
||||
this.type = 'Prerender';
|
||||
this.expiration = expiration;
|
||||
this.lambda = lambda;
|
||||
@@ -32,6 +40,22 @@ export class Prerender {
|
||||
}
|
||||
this.group = group;
|
||||
|
||||
if (bypassToken == null) {
|
||||
this.bypassToken = null;
|
||||
} else if (typeof bypassToken === 'string') {
|
||||
if (bypassToken.length < 32) {
|
||||
// Enforce 128 bits of entropy for safety reasons (UUIDv4 size)
|
||||
throw new Error(
|
||||
'The `bypassToken` argument for `Prerender` must be 32 characters or more.'
|
||||
);
|
||||
}
|
||||
this.bypassToken = bypassToken;
|
||||
} else {
|
||||
throw new Error(
|
||||
'The `bypassToken` argument for `Prerender` must be a `string`.'
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof fallback === 'undefined') {
|
||||
throw new Error(
|
||||
'The `fallback` argument for `Prerender` needs to be a `FileBlob`, `FileFsRef`, `FileRef`, or null.'
|
||||
|
||||
@@ -31,6 +31,7 @@ export interface Config {
|
||||
| undefined;
|
||||
maxLambdaSize?: string;
|
||||
includeFiles?: string | string[];
|
||||
excludeFiles?: string | string[];
|
||||
bundle?: boolean;
|
||||
ldsflags?: string;
|
||||
helpers?: boolean;
|
||||
|
||||
@@ -4,7 +4,7 @@ const {
|
||||
packAndDeploy,
|
||||
testDeployment,
|
||||
} = require('../../../test/lib/deployment/test-deployment');
|
||||
const { glob, detectBuilders, detectRoutes } = require('../');
|
||||
const { glob, detectBuilders } = require('../');
|
||||
|
||||
jest.setTimeout(4 * 60 * 1000);
|
||||
|
||||
@@ -110,8 +110,7 @@ it('Test `detectBuilders` and `detectRoutes`', async () => {
|
||||
},
|
||||
];
|
||||
|
||||
const { builders } = await detectBuilders(files, pkg);
|
||||
const { defaultRoutes } = await detectRoutes(files, builders);
|
||||
const { builders, defaultRoutes } = await detectBuilders(files, pkg);
|
||||
|
||||
const nowConfig = { builds: builders, routes: defaultRoutes, probes };
|
||||
await fs.writeFile(
|
||||
@@ -126,7 +125,7 @@ it('Test `detectBuilders` and `detectRoutes`', async () => {
|
||||
expect(deployment).toBeDefined();
|
||||
});
|
||||
|
||||
it('Test `detectBuilders` and `detectRoutes` with `index` files', async () => {
|
||||
it('Test `detectBuilders` with `index` files', async () => {
|
||||
const fixture = path.join(__dirname, 'fixtures', '02-zero-config-api');
|
||||
const pkg = await fs.readJSON(path.join(fixture, 'package.json'));
|
||||
const fileList = await glob('**', fixture);
|
||||
@@ -192,8 +191,7 @@ it('Test `detectBuilders` and `detectRoutes` with `index` files', async () => {
|
||||
},
|
||||
];
|
||||
|
||||
const { builders } = await detectBuilders(files, pkg);
|
||||
const { defaultRoutes } = await detectRoutes(files, builders);
|
||||
const { builders, defaultRoutes } = await detectBuilders(files, pkg);
|
||||
|
||||
const nowConfig = { builds: builders, routes: defaultRoutes, probes };
|
||||
await fs.writeFile(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
8
packages/now-build-utils/test/unit.test.js
vendored
8
packages/now-build-utils/test/unit.test.js
vendored
@@ -11,6 +11,10 @@ const {
|
||||
} = require('../dist');
|
||||
|
||||
it('should re-create symlinks properly', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log('Skipping test on windows');
|
||||
return;
|
||||
}
|
||||
const files = await glob('**', path.join(__dirname, 'symlinks'));
|
||||
assert.equal(Object.keys(files).length, 2);
|
||||
|
||||
@@ -29,6 +33,10 @@ it('should re-create symlinks properly', async () => {
|
||||
});
|
||||
|
||||
it('should create zip files with symlinks properly', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log('Skipping test on windows');
|
||||
return;
|
||||
}
|
||||
const files = await glob('**', path.join(__dirname, 'symlinks'));
|
||||
assert.equal(Object.keys(files).length, 2);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/cgi",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.3",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
"@zeit/best@0.4.3":
|
||||
version "0.4.3"
|
||||
resolved "http://registry.npmjs.org/@zeit/best/-/best-0.4.3.tgz#eaebdfa8b24121a97b1753501ea8c9330d549b30"
|
||||
resolved "https://registry.npmjs.org/@zeit/best/-/best-0.4.3.tgz#eaebdfa8b24121a97b1753501ea8c9330d549b30"
|
||||
dependencies:
|
||||
arg "1.0.0"
|
||||
chalk "2.3.1"
|
||||
@@ -144,7 +144,7 @@ call-me-maybe@^1.0.1:
|
||||
|
||||
chalk@2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "http://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz#523fe2678aec7b04e8041909292fe8b17059b796"
|
||||
resolved "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz#523fe2678aec7b04e8041909292fe8b17059b796"
|
||||
dependencies:
|
||||
ansi-styles "^3.2.0"
|
||||
escape-string-regexp "^1.0.5"
|
||||
@@ -585,8 +585,8 @@ minimatch@^3.0.4:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
mixin-deep@^1.2.0:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
|
||||
dependencies:
|
||||
for-in "^1.0.2"
|
||||
is-extendable "^1.0.1"
|
||||
@@ -704,7 +704,7 @@ rmfr@2.0.0:
|
||||
|
||||
safe-regex@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
|
||||
resolved "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
|
||||
dependencies:
|
||||
ret "~0.1.10"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
[](https://spectrum.chat/zeit)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "now",
|
||||
"version": "17.0.0-canary.26",
|
||||
"version": "17.0.5-canary.4",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Now",
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"preinstall": "node ./scripts/preinstall.js",
|
||||
"test-unit": "nyc ava test/*unit.js --serial --fail-fast --verbose",
|
||||
"test-unit": "nyc ava test/unit.js test/dev-builder.unit.js test/dev-router.unit.js test/dev-server.unit.js --serial --fail-fast --verbose",
|
||||
"test-integration": "ava test/integration.js --serial --fail-fast",
|
||||
"test-integration-v1": "ava test/integration-v1.js --serial --fail-fast",
|
||||
"test-integration-now-dev": "ava test/dev/integration.js --serial --fail-fast --verbose",
|
||||
@@ -63,6 +63,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sentry/node": "5.5.0",
|
||||
"@sindresorhus/slugify": "0.10.0",
|
||||
"@types/ansi-escapes": "3.0.0",
|
||||
"@types/ansi-regex": "4.0.0",
|
||||
"@types/async-retry": "1.2.1",
|
||||
@@ -89,10 +90,10 @@
|
||||
"@types/tar-fs": "1.16.1",
|
||||
"@types/text-table": "0.2.0",
|
||||
"@types/universal-analytics": "0.4.2",
|
||||
"@types/which": "1.3.1",
|
||||
"@types/which": "1.3.2",
|
||||
"@types/write-json-file": "2.2.1",
|
||||
"@zeit/dockerignore": "0.0.5",
|
||||
"@zeit/fun": "0.11.0",
|
||||
"@zeit/fun": "0.11.2",
|
||||
"@zeit/ncc": "0.18.5",
|
||||
"@zeit/source-map-support": "0.6.2",
|
||||
"ajv": "6.10.2",
|
||||
@@ -108,7 +109,7 @@
|
||||
"chalk": "2.4.2",
|
||||
"chokidar": "2.1.6",
|
||||
"clipboardy": "2.1.0",
|
||||
"codecov": "3.1.0",
|
||||
"codecov": "3.6.5",
|
||||
"cpy": "7.2.0",
|
||||
"credit-card": "3.0.1",
|
||||
"date-fns": "1.29.0",
|
||||
@@ -131,7 +132,7 @@
|
||||
"http-proxy": "1.17.0",
|
||||
"ignore": "4.0.6",
|
||||
"ini": "1.3.4",
|
||||
"inquirer": "3.3.0",
|
||||
"inquirer": "7.0.4",
|
||||
"is-port-reachable": "3.0.0",
|
||||
"is-url": "1.2.2",
|
||||
"jaro-winkler": "0.2.8",
|
||||
@@ -174,10 +175,10 @@
|
||||
"universal-analytics": "0.4.20",
|
||||
"update-check": "1.5.3",
|
||||
"utility-types": "2.1.0",
|
||||
"which": "1.3.1",
|
||||
"which": "2.0.2",
|
||||
"which-promise": "1.0.0",
|
||||
"write-json-file": "2.2.0",
|
||||
"xdg-app-paths": "5.1.0",
|
||||
"yarn": "1.17.3"
|
||||
"yarn": "1.22.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,11 @@ import getAliases from '../../util/alias/get-aliases';
|
||||
import getScope from '../../util/get-scope.ts';
|
||||
import stamp from '../../util/output/stamp.ts';
|
||||
import strlen from '../../util/strlen.ts';
|
||||
import wait from '../../util/output/wait';
|
||||
|
||||
export default async function ls(ctx, opts, args, output) {
|
||||
const {
|
||||
authConfig: { token },
|
||||
config
|
||||
config,
|
||||
} = ctx;
|
||||
const { currentTeam } = config;
|
||||
const { apiUrl } = ctx;
|
||||
@@ -22,7 +21,7 @@ export default async function ls(ctx, opts, args, output) {
|
||||
apiUrl,
|
||||
token,
|
||||
currentTeam,
|
||||
debug: debugEnabled
|
||||
debug: debugEnabled,
|
||||
});
|
||||
let contextName = null;
|
||||
|
||||
@@ -51,7 +50,7 @@ export default async function ls(ctx, opts, args, output) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
cancelWait = wait(
|
||||
cancelWait = output.spinner(
|
||||
args[0]
|
||||
? `Fetching alias details for "${args[0]}" under ${chalk.bold(
|
||||
contextName
|
||||
@@ -112,13 +111,13 @@ function printAliasTable(aliases) {
|
||||
? a.deployment.url
|
||||
: chalk.gray('–'),
|
||||
a.alias,
|
||||
ms(Date.now() - new Date(a.created))
|
||||
])
|
||||
ms(Date.now() - new Date(a.created)),
|
||||
]),
|
||||
],
|
||||
{
|
||||
align: ['l', 'l', 'r'],
|
||||
hsep: ' '.repeat(4),
|
||||
stringLength: strlen
|
||||
stringLength: strlen,
|
||||
}
|
||||
).replace(/^/gm, ' ')}\n\n`;
|
||||
}
|
||||
@@ -130,13 +129,13 @@ function printPathAliasTable(rules) {
|
||||
rules.map(rule => [
|
||||
rule.pathname ? rule.pathname : chalk.cyan('[fallthrough]'),
|
||||
rule.method ? rule.method : '*',
|
||||
rule.dest
|
||||
rule.dest,
|
||||
])
|
||||
),
|
||||
{
|
||||
align: ['l', 'l', 'l', 'l'],
|
||||
hsep: ' '.repeat(6),
|
||||
stringLength: strlen
|
||||
stringLength: strlen,
|
||||
}
|
||||
).replace(/^(.*)/gm, ' $1')}\n`;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export default async function({ creditCards, clear = false, contextName }) {
|
||||
name: {
|
||||
label: rightPad('Full Name', 12),
|
||||
placeholder: 'John Appleseed',
|
||||
validateValue: data => data.trim().length > 0
|
||||
validateValue: data => data.trim().length > 0,
|
||||
},
|
||||
|
||||
cardNumber: {
|
||||
@@ -36,7 +36,7 @@ export default async function({ creditCards, clear = false, contextName }) {
|
||||
return false;
|
||||
}
|
||||
return ccValidator.isValidCardNumber(data, type);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
ccv: {
|
||||
@@ -46,7 +46,7 @@ export default async function({ creditCards, clear = false, contextName }) {
|
||||
validateValue: data => {
|
||||
const brand = state.cardNumber.brand.toLowerCase();
|
||||
return ccValidator.doesCvvMatchType(data, brand);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
expDate: {
|
||||
@@ -54,8 +54,8 @@ export default async function({ creditCards, clear = false, contextName }) {
|
||||
mask: 'expDate',
|
||||
placeholder: 'mm / yyyy',
|
||||
middleware: expDateMiddleware,
|
||||
validateValue: data => !ccValidator.isExpired(...data.split(' / '))
|
||||
}
|
||||
validateValue: data => !ccValidator.isExpired(...data.split(' / ')),
|
||||
},
|
||||
};
|
||||
|
||||
async function render() {
|
||||
@@ -80,7 +80,7 @@ export default async function({ creditCards, clear = false, contextName }) {
|
||||
mask: piece.mask,
|
||||
validateKeypress: piece.validateKeypress,
|
||||
validateValue: piece.validateValue,
|
||||
autoComplete: piece.autoComplete
|
||||
autoComplete: piece.autoComplete,
|
||||
});
|
||||
|
||||
piece.value = result;
|
||||
@@ -135,7 +135,7 @@ export default async function({ creditCards, clear = false, contextName }) {
|
||||
name: state.name.value,
|
||||
cardNumber: state.cardNumber.value,
|
||||
ccv: state.ccv.value,
|
||||
expDate: state.expDate.value
|
||||
expDate: state.expDate.value,
|
||||
});
|
||||
|
||||
stopSpinner();
|
||||
@@ -156,9 +156,9 @@ export default async function({ creditCards, clear = false, contextName }) {
|
||||
stopSpinner();
|
||||
const linesToClear = state.error ? 15 : 14;
|
||||
process.stdout.write(ansiEscapes.eraseLines(linesToClear));
|
||||
state.error = `${chalk.red(
|
||||
'> Error!'
|
||||
)} ${err.message} Please make sure the info is correct`;
|
||||
state.error = `${chalk.red('> Error!')} ${
|
||||
err.message
|
||||
} Please make sure the info is correct`;
|
||||
await render();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import Now from '../../util';
|
||||
import Client from '../../util/client';
|
||||
import getScope from '../../util/get-scope';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import wait from '../../util/output/wait';
|
||||
import createCertFromFile from '../../util/certs/create-cert-from-file';
|
||||
import createCertForCns from '../../util/certs/create-cert-for-cns';
|
||||
import { NowContext } from '../../types';
|
||||
@@ -110,7 +109,7 @@ async function add(
|
||||
(res, item) => res.concat(item.split(',')),
|
||||
[]
|
||||
);
|
||||
const cancelWait = wait(
|
||||
const cancelWait = output.spinner(
|
||||
`Generating a certificate for ${chalk.bold(cns.join(', '))}`
|
||||
);
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ export default async ctx => {
|
||||
contextName,
|
||||
output,
|
||||
stats,
|
||||
localConfig || {},
|
||||
localConfig,
|
||||
parts.latestArgs
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import ms from 'ms';
|
||||
import bytes from 'bytes';
|
||||
import { join } from 'path';
|
||||
import { write as copy } from 'clipboardy';
|
||||
import chalk from 'chalk';
|
||||
import title from 'title';
|
||||
@@ -47,8 +48,12 @@ import {
|
||||
import getProjectName from '../../util/get-project-name';
|
||||
import selectOrg from '../../util/input/select-org';
|
||||
import inputProject from '../../util/input/input-project';
|
||||
import validatePaths from '../../util/validate-paths';
|
||||
import { prependEmoji, emoji } from '../../util/emoji';
|
||||
import { inputRootDirectory } from '../../util/input/input-root-directory';
|
||||
import validatePaths, {
|
||||
validateRootDirectory,
|
||||
} from '../../util/validate-paths';
|
||||
import { readLocalConfig } from '../../util/config/files';
|
||||
|
||||
const addProcessEnv = async (log, env) => {
|
||||
let val;
|
||||
@@ -90,7 +95,6 @@ const printDeploymentStatus = async (
|
||||
},
|
||||
deployStamp,
|
||||
isClipboardEnabled,
|
||||
quiet,
|
||||
isFile
|
||||
) => {
|
||||
const isProdDeployment = target === 'production';
|
||||
@@ -131,22 +135,22 @@ const printDeploymentStatus = async (
|
||||
}
|
||||
|
||||
// copy to clipboard
|
||||
let isCopiedToClipboard = false;
|
||||
if (isClipboardEnabled && !isWildcard) {
|
||||
await copy(previewUrl).catch(error =>
|
||||
output.debug(`Error copying to clipboard: ${error}`)
|
||||
);
|
||||
}
|
||||
|
||||
// write to stdout
|
||||
if (quiet) {
|
||||
process.stdout.write(`https://${deploymentUrl}`);
|
||||
await copy(previewUrl)
|
||||
.then(() => {
|
||||
isCopiedToClipboard = true;
|
||||
})
|
||||
.catch(error => output.debug(`Error copying to clipboard: ${error}`));
|
||||
}
|
||||
|
||||
output.print(
|
||||
prependEmoji(
|
||||
`${isProdDeployment ? 'Production' : 'Preview'}: ${chalk.bold(
|
||||
previewUrl
|
||||
)} ${deployStamp()}`,
|
||||
)}${
|
||||
isCopiedToClipboard ? chalk.gray(` [copied to clipboard]`) : ''
|
||||
} ${deployStamp()}`,
|
||||
emoji('success')
|
||||
) + `\n`
|
||||
);
|
||||
@@ -235,13 +239,6 @@ export default async function main(
|
||||
const { isFile, path } = pathValidation;
|
||||
const autoConfirm = argv['--confirm'] || isFile;
|
||||
|
||||
// build `meta`
|
||||
const meta = Object.assign(
|
||||
{},
|
||||
parseMeta(localConfig.meta),
|
||||
parseMeta(argv['--meta'])
|
||||
);
|
||||
|
||||
// --no-scale
|
||||
if (argv['--no-scale']) {
|
||||
warn(`The option --no-scale is only supported on Now 1.0 deployments`);
|
||||
@@ -257,7 +254,125 @@ export default async function main(
|
||||
);
|
||||
}
|
||||
|
||||
if (localConfig && localConfig.name) {
|
||||
const client = new Client({
|
||||
apiUrl: ctx.apiUrl,
|
||||
token: ctx.authConfig.token,
|
||||
debug: debugEnabled,
|
||||
});
|
||||
|
||||
// retrieve `project` and `org` from .now
|
||||
const link = await getLinkedProject(output, client, path);
|
||||
|
||||
if (link.status === 'error') {
|
||||
return link.exitCode;
|
||||
}
|
||||
|
||||
let { org, project, status } = link;
|
||||
let newProjectName = null;
|
||||
let rootDirectory = project ? project.rootDirectory : null;
|
||||
|
||||
if (status === 'not_linked') {
|
||||
const shouldStartSetup =
|
||||
autoConfirm ||
|
||||
(await confirm(
|
||||
`Set up and deploy ${chalk.cyan(`“${toHumanPath(path)}”`)}?`,
|
||||
true
|
||||
));
|
||||
|
||||
if (!shouldStartSetup) {
|
||||
output.print(`Aborted. Project not set up.\n`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
org = await selectOrg(
|
||||
output,
|
||||
'Which scope do you want to deploy to?',
|
||||
client,
|
||||
ctx.config.currentTeam,
|
||||
autoConfirm
|
||||
);
|
||||
|
||||
// We use `localConfig` here to read the name
|
||||
// even though the `now.json` file can change
|
||||
// afterwards, this is fine since the property
|
||||
// will be deprecated and can be replaced with
|
||||
// user input.
|
||||
const detectedProjectName = getProjectName({
|
||||
argv,
|
||||
nowConfig: localConfig || {},
|
||||
isFile,
|
||||
paths,
|
||||
});
|
||||
|
||||
const projectOrNewProjectName = await inputProject(
|
||||
output,
|
||||
client,
|
||||
org,
|
||||
detectedProjectName,
|
||||
autoConfirm
|
||||
);
|
||||
|
||||
if (typeof projectOrNewProjectName === 'string') {
|
||||
newProjectName = projectOrNewProjectName;
|
||||
rootDirectory = await inputRootDirectory(path, output, autoConfirm);
|
||||
} else {
|
||||
project = projectOrNewProjectName;
|
||||
rootDirectory = project.rootDirectory;
|
||||
|
||||
// we can already link the project
|
||||
await linkFolderToProject(
|
||||
output,
|
||||
path,
|
||||
{
|
||||
projectId: project.id,
|
||||
orgId: org.id,
|
||||
},
|
||||
project.name,
|
||||
org.slug
|
||||
);
|
||||
status = 'linked';
|
||||
}
|
||||
}
|
||||
|
||||
const sourcePath = rootDirectory ? join(path, rootDirectory) : path;
|
||||
|
||||
if (
|
||||
rootDirectory &&
|
||||
(await validateRootDirectory(
|
||||
output,
|
||||
path,
|
||||
sourcePath,
|
||||
project
|
||||
? `To change your project settings, go to https://zeit.co/${org.slug}/${project.name}/settings`
|
||||
: ''
|
||||
)) === false
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// If Root Directory is used we'll try to read the config
|
||||
// from there instead and use it if it exists.
|
||||
if (rootDirectory) {
|
||||
const rootDirectoryConfig = readLocalConfig(sourcePath);
|
||||
|
||||
if (rootDirectoryConfig) {
|
||||
debug(`Read local config from root directory (${rootDirectory})`);
|
||||
localConfig = rootDirectoryConfig;
|
||||
} else if (localConfig) {
|
||||
output.print(
|
||||
`${prependEmoji(
|
||||
`The ${highlight(
|
||||
'now.json'
|
||||
)} file should be inside of the provided root directory.`,
|
||||
emoji('warning')
|
||||
)}\n`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
localConfig = localConfig || {};
|
||||
|
||||
if (localConfig.name) {
|
||||
output.print(
|
||||
`${prependEmoji(
|
||||
`The ${code('name')} property in ${highlight(
|
||||
@@ -307,6 +422,13 @@ export default async function main(
|
||||
}
|
||||
}
|
||||
|
||||
// build `meta`
|
||||
const meta = Object.assign(
|
||||
{},
|
||||
parseMeta(localConfig.meta),
|
||||
parseMeta(argv['--meta'])
|
||||
);
|
||||
|
||||
// Merge dotenv config, `env` from now.json, and `--env` / `-e` arguments
|
||||
const deploymentEnv = Object.assign(
|
||||
{},
|
||||
@@ -364,77 +486,6 @@ export default async function main(
|
||||
target = 'production';
|
||||
}
|
||||
|
||||
const client = new Client({
|
||||
apiUrl: ctx.apiUrl,
|
||||
token: ctx.authConfig.token,
|
||||
debug: debugEnabled,
|
||||
});
|
||||
|
||||
// retrieve `project` and `org` from .now
|
||||
const link = await getLinkedProject(output, client, path);
|
||||
|
||||
if (link.status === 'error') {
|
||||
return link.exitCode;
|
||||
}
|
||||
|
||||
let { org, project, status } = link;
|
||||
let newProjectName = null;
|
||||
|
||||
if (status === 'not_linked') {
|
||||
const shouldStartSetup =
|
||||
autoConfirm ||
|
||||
(await confirm(
|
||||
`Set up and deploy ${chalk.cyan(`“${toHumanPath(path)}”`)}?`,
|
||||
true
|
||||
));
|
||||
|
||||
if (!shouldStartSetup) {
|
||||
output.print(`Aborted. Project not set up.\n`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
org = await selectOrg(
|
||||
'Which scope do you want to deploy to?',
|
||||
client,
|
||||
ctx.config.currentTeam,
|
||||
autoConfirm
|
||||
);
|
||||
|
||||
const detectedProjectName = getProjectName({
|
||||
argv,
|
||||
nowConfig: localConfig,
|
||||
isFile,
|
||||
paths,
|
||||
});
|
||||
|
||||
const projectOrNewProjectName = await inputProject(
|
||||
output,
|
||||
client,
|
||||
org,
|
||||
detectedProjectName,
|
||||
autoConfirm
|
||||
);
|
||||
|
||||
if (typeof projectOrNewProjectName === 'string') {
|
||||
newProjectName = projectOrNewProjectName;
|
||||
} else {
|
||||
project = projectOrNewProjectName;
|
||||
|
||||
// we can already link the project
|
||||
await linkFolderToProject(
|
||||
output,
|
||||
path,
|
||||
{
|
||||
projectId: project.id,
|
||||
orgId: org.id,
|
||||
},
|
||||
project.name,
|
||||
org.slug
|
||||
);
|
||||
status = 'linked';
|
||||
}
|
||||
}
|
||||
|
||||
const currentTeam = org.type === 'team' ? org.id : undefined;
|
||||
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
|
||||
let deployStamp = stamp();
|
||||
@@ -462,10 +513,11 @@ export default async function main(
|
||||
output,
|
||||
now,
|
||||
contextName,
|
||||
[path],
|
||||
[sourcePath],
|
||||
createArgs,
|
||||
org,
|
||||
!project && !isFile
|
||||
!project && !isFile,
|
||||
path
|
||||
);
|
||||
|
||||
if (
|
||||
@@ -474,6 +526,10 @@ export default async function main(
|
||||
) {
|
||||
let { projectSettings, framework } = deployment;
|
||||
|
||||
if (rootDirectory) {
|
||||
projectSettings.rootDirectory = rootDirectory;
|
||||
}
|
||||
|
||||
const settings = await editProjectSettings(
|
||||
output,
|
||||
projectSettings,
|
||||
@@ -489,10 +545,11 @@ export default async function main(
|
||||
output,
|
||||
now,
|
||||
contextName,
|
||||
[path],
|
||||
[sourcePath],
|
||||
createArgs,
|
||||
org,
|
||||
false
|
||||
false,
|
||||
path
|
||||
);
|
||||
}
|
||||
|
||||
@@ -501,6 +558,19 @@ export default async function main(
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (deployment instanceof Error) {
|
||||
output.error(
|
||||
`${deployment.message ||
|
||||
'An unexpected error occurred while deploying your project'} (http://zeit.ink/P4)`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (deployment.readyState === 'CANCELED') {
|
||||
output.log('The deployment has been canceled');
|
||||
return 1;
|
||||
}
|
||||
|
||||
const deploymentResponse = await getDeploymentByIdOrHost(
|
||||
now,
|
||||
contextName,
|
||||
@@ -587,7 +657,12 @@ export default async function main(
|
||||
output.error('Build failed');
|
||||
output.error(
|
||||
`Check your logs at ${now.url}/_logs or run ${code(
|
||||
`now logs ${now.url}`
|
||||
`now logs ${now.url}`,
|
||||
{
|
||||
// Backticks are interpreted as part of the URL, causing CMD+Click
|
||||
// behavior to fail in editors like VSCode.
|
||||
backticks: false,
|
||||
}
|
||||
)}`
|
||||
);
|
||||
|
||||
@@ -616,7 +691,6 @@ export default async function main(
|
||||
deployment,
|
||||
deployStamp,
|
||||
!argv['--no-clipboard'],
|
||||
quiet,
|
||||
isFile
|
||||
);
|
||||
}
|
||||
@@ -665,6 +739,11 @@ function handleCreateDeployError(output, error) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (dataPath === '.name') {
|
||||
output.error(message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (keyword === 'type') {
|
||||
const prop = dataPath.substr(1, dataPath.length);
|
||||
|
||||
|
||||
@@ -852,7 +852,7 @@ async function sync({
|
||||
const { url } = now;
|
||||
const dcs =
|
||||
deploymentType !== 'static' && deployment.scale
|
||||
? chalk` ({bold ${Object.keys(deployment.scale).join(', ')})`
|
||||
? ` (${ chalk.bold(Object.keys(deployment.scale).join(', ')) })`
|
||||
: '';
|
||||
|
||||
if (isTTY) {
|
||||
|
||||
@@ -23,7 +23,7 @@ export default async function dev(
|
||||
output: Output
|
||||
) {
|
||||
const [dir = '.'] = args;
|
||||
const cwd = path.resolve(dir);
|
||||
let cwd = path.resolve(dir);
|
||||
const listen = parseListen(opts['--listen'] || '3000');
|
||||
const debug = opts['--debug'] || false;
|
||||
|
||||
@@ -72,6 +72,10 @@ export default async function dev(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (project.rootDirectory) {
|
||||
cwd = path.join(cwd, project.rootDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
const devServer = new DevServer(cwd, { output, debug, devCommand });
|
||||
|
||||
@@ -13,7 +13,6 @@ import param from '../../util/output/param';
|
||||
import promptBool from '../../util/input/prompt-bool';
|
||||
import purchaseDomain from '../../util/domains/purchase-domain';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import wait from '../../util/output/wait';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
@@ -27,7 +26,7 @@ export default async function buy(
|
||||
) {
|
||||
const {
|
||||
authConfig: { token },
|
||||
config
|
||||
config,
|
||||
} = ctx;
|
||||
const { currentTeam } = config;
|
||||
const { apiUrl } = ctx;
|
||||
@@ -100,7 +99,7 @@ export default async function buy(
|
||||
|
||||
let buyResult;
|
||||
const purchaseStamp = stamp();
|
||||
const stopPurchaseSpinner = wait('Purchasing');
|
||||
const stopPurchaseSpinner = output.spinner('Purchasing');
|
||||
|
||||
try {
|
||||
buyResult = await purchaseDomain(client, domainName, price);
|
||||
|
||||
@@ -9,7 +9,6 @@ import listInput from '../../util/input/list';
|
||||
import listItem from '../../util/output/list-item';
|
||||
import promptBool from '../../util/input/prompt-bool';
|
||||
import toHumanPath from '../../util/humanize-path';
|
||||
import wait from '../../util/output/wait';
|
||||
import { Output } from '../../util/output';
|
||||
import { NowContext } from '../../types';
|
||||
import success from '../../util/output/success';
|
||||
@@ -24,10 +23,10 @@ type Options = {
|
||||
};
|
||||
|
||||
type Example = {
|
||||
name: string,
|
||||
visible: boolean,
|
||||
suggestions: string[]
|
||||
}
|
||||
name: string;
|
||||
visible: boolean;
|
||||
suggestions: string[];
|
||||
};
|
||||
|
||||
const EXAMPLE_API = 'https://now-example-files.zeit.sh';
|
||||
|
||||
@@ -40,7 +39,7 @@ export default async function init(
|
||||
const [name, dir] = args;
|
||||
const force = opts['-f'] || opts['--force'];
|
||||
|
||||
const examples = await fetchExampleList();
|
||||
const examples = await fetchExampleList(output);
|
||||
|
||||
if (!examples) {
|
||||
throw new Error(`Could not fetch example list.`);
|
||||
@@ -56,22 +55,22 @@ export default async function init(
|
||||
return 0;
|
||||
}
|
||||
|
||||
return extractExample(chosen, dir, force);
|
||||
return extractExample(output, chosen, dir, force);
|
||||
}
|
||||
|
||||
if (exampleList.includes(name)) {
|
||||
return extractExample(name, dir, force);
|
||||
return extractExample(output, name, dir, force);
|
||||
}
|
||||
|
||||
const oldExample = examples.find(x => !x.visible && x.name === name);
|
||||
if (oldExample) {
|
||||
return extractExample(name, dir, force, 'v1');
|
||||
return extractExample(output, name, dir, force, 'v1');
|
||||
}
|
||||
|
||||
const found = await guess(exampleList, name);
|
||||
|
||||
if (typeof found === 'string') {
|
||||
return extractExample(found, dir, force);
|
||||
return extractExample(output, found, dir, force);
|
||||
}
|
||||
|
||||
console.log(info('No changes made.'));
|
||||
@@ -81,8 +80,8 @@ export default async function init(
|
||||
/**
|
||||
* Fetch example list json
|
||||
*/
|
||||
async function fetchExampleList() {
|
||||
const stopSpinner = wait('Fetching examples');
|
||||
async function fetchExampleList(output: Output) {
|
||||
const stopSpinner = output.spinner('Fetching examples');
|
||||
const url = `${EXAMPLE_API}/v2/list.json`;
|
||||
|
||||
try {
|
||||
@@ -93,7 +92,7 @@ async function fetchExampleList() {
|
||||
throw new Error(`Failed fetching list.json (${resp.statusText}).`);
|
||||
}
|
||||
|
||||
return await resp.json() as Example[];
|
||||
return (await resp.json()) as Example[];
|
||||
} catch (e) {
|
||||
stopSpinner();
|
||||
}
|
||||
@@ -106,22 +105,28 @@ async function chooseFromDropdown(message: string, exampleList: string[]) {
|
||||
const choices = exampleList.map(name => ({
|
||||
name,
|
||||
value: name,
|
||||
short: name
|
||||
short: name,
|
||||
}));
|
||||
|
||||
return listInput({
|
||||
message,
|
||||
separator: false,
|
||||
choices
|
||||
choices,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract example to directory
|
||||
*/
|
||||
async function extractExample(name: string, dir: string, force?: boolean, ver: string = 'v2') {
|
||||
async function extractExample(
|
||||
output: Output,
|
||||
name: string,
|
||||
dir: string,
|
||||
force?: boolean,
|
||||
ver: string = 'v2'
|
||||
) {
|
||||
const folder = prepareFolder(process.cwd(), dir || name, force);
|
||||
const stopSpinner = wait(`Fetching ${name}`);
|
||||
const stopSpinner = output.spinner(`Fetching ${name}`);
|
||||
|
||||
const url = `${EXAMPLE_API}/${ver}/download/${name}.tar.gz`;
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import createOutput from '../util/output';
|
||||
import Now from '../util';
|
||||
import logo from '../util/output/logo';
|
||||
import elapsed from '../util/output/elapsed.ts';
|
||||
import wait from '../util/output/wait';
|
||||
import { handleError } from '../util/error';
|
||||
import strlen from '../util/strlen.ts';
|
||||
import Client from '../util/client.ts';
|
||||
@@ -79,13 +78,16 @@ export default async function main(ctx) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const { authConfig: { token }, config } = ctx;
|
||||
const {
|
||||
authConfig: { token },
|
||||
config,
|
||||
} = ctx;
|
||||
const { currentTeam } = config;
|
||||
const client = new Client({
|
||||
apiUrl,
|
||||
token,
|
||||
currentTeam,
|
||||
debug: debugEnabled
|
||||
debug: debugEnabled,
|
||||
});
|
||||
let contextName = null;
|
||||
|
||||
@@ -104,7 +106,7 @@ export default async function main(ctx) {
|
||||
|
||||
// resolve the deployment, since we might have been given an alias
|
||||
const depFetchStart = Date.now();
|
||||
const cancelWait = wait(
|
||||
const cancelWait = output.spinner(
|
||||
`Fetching deployment "${id}" in ${chalk.bold(contextName)}`
|
||||
);
|
||||
|
||||
@@ -140,7 +142,7 @@ export default async function main(ctx) {
|
||||
limits,
|
||||
version,
|
||||
routes,
|
||||
readyState
|
||||
readyState,
|
||||
} = deployment;
|
||||
|
||||
const isBuilds = version === 2;
|
||||
@@ -159,7 +161,7 @@ export default async function main(ctx) {
|
||||
)}/events?types=event`
|
||||
)
|
||||
),
|
||||
isBuilds ? now.fetch(buildsUrl) : { builds: [] }
|
||||
isBuilds ? now.fetch(buildsUrl) : { builds: [] },
|
||||
]);
|
||||
|
||||
cancelWait();
|
||||
@@ -174,7 +176,9 @@ export default async function main(ctx) {
|
||||
print(` ${chalk.cyan('version')}\t${version}\n`);
|
||||
print(` ${chalk.cyan('id')}\t\t${finalId}\n`);
|
||||
print(` ${chalk.cyan('name')}\t${name}\n`);
|
||||
print(` ${chalk.cyan('readyState')}\t${stateString(state || readyState)}\n`);
|
||||
print(
|
||||
` ${chalk.cyan('readyState')}\t${stateString(state || readyState)}\n`
|
||||
);
|
||||
if (!isBuilds) {
|
||||
print(` ${chalk.cyan('type')}\t${type}\n`);
|
||||
}
|
||||
@@ -255,7 +259,7 @@ export default async function main(ctx) {
|
||||
`${table(t, {
|
||||
align: ['l', 'c', 'c', 'c'],
|
||||
hsep: ' '.repeat(8),
|
||||
stringLength: strlen
|
||||
stringLength: strlen,
|
||||
}).replace(/^(.*)/gm, ' $1')}\n`
|
||||
);
|
||||
print('\n');
|
||||
@@ -269,9 +273,9 @@ export default async function main(ctx) {
|
||||
events.forEach(data => {
|
||||
if (!data.event) return; // keepalive
|
||||
print(
|
||||
` ${chalk.gray(
|
||||
new Date(data.created).toISOString()
|
||||
)} ${data.event} ${getEventMetadata(data)}\n`
|
||||
` ${chalk.gray(new Date(data.created).toISOString())} ${
|
||||
data.event
|
||||
} ${getEventMetadata(data)}\n`
|
||||
);
|
||||
});
|
||||
print('\n');
|
||||
|
||||
@@ -8,7 +8,6 @@ import chalk from 'chalk';
|
||||
import ua from '../util/ua.ts';
|
||||
import getArgs from '../util/get-args';
|
||||
import error from '../util/output/error';
|
||||
import wait from '../util/output/wait';
|
||||
import highlight from '../util/output/highlight';
|
||||
import ok from '../util/output/ok';
|
||||
import cmd from '../util/output/cmd.ts';
|
||||
@@ -191,7 +190,7 @@ const login = async ctx => {
|
||||
let verificationToken;
|
||||
let securityCode;
|
||||
|
||||
stopSpinner = wait('Sending you an email');
|
||||
stopSpinner = output.spinner('Sending you an email');
|
||||
|
||||
try {
|
||||
const data = await executeLogin(apiUrl, email);
|
||||
@@ -216,7 +215,7 @@ const login = async ctx => {
|
||||
)}.\n`
|
||||
);
|
||||
|
||||
stopSpinner = wait('Waiting for your confirmation');
|
||||
stopSpinner = output.spinner('Waiting for your confirmation');
|
||||
|
||||
let token;
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import logo from '../util/output/logo';
|
||||
import elapsed from '../util/output/elapsed.ts';
|
||||
import { maybeURL, normalizeURL, parseInstanceURL } from '../util/url';
|
||||
import printEvents from '../util/events';
|
||||
import wait from '../util/output/wait';
|
||||
import Client from '../util/client.ts';
|
||||
import getScope from '../util/get-scope.ts';
|
||||
|
||||
@@ -165,7 +164,7 @@ export default async function main(ctx) {
|
||||
const id = deploymentIdOrURL;
|
||||
|
||||
const depFetchStart = Date.now();
|
||||
const cancelWait = wait(
|
||||
const cancelWait = output.spinner(
|
||||
`Fetching deployment "${id}" in ${chalk.bold(contextName)}`
|
||||
);
|
||||
|
||||
@@ -287,12 +286,12 @@ function printLogShort(log) {
|
||||
data = JSON.stringify(obj, null, 2);
|
||||
} else {
|
||||
data = (log.text || '')
|
||||
.replace(/\n$/, '')
|
||||
.replace(/^\n/, '')
|
||||
// eslint-disable-next-line no-control-regex
|
||||
.replace(/\x1b\[1000D/g, '')
|
||||
.replace(/\x1b\[0K/g, '')
|
||||
.replace(/\x1b\[1A/g, '');
|
||||
.replace(/\n$/, '')
|
||||
.replace(/^\n/, '')
|
||||
// eslint-disable-next-line no-control-regex
|
||||
.replace(/\x1b\[1000D/g, '')
|
||||
.replace(/\x1b\[0K/g, '')
|
||||
.replace(/\x1b\[1A/g, '');
|
||||
if (/warning/i.test(data)) {
|
||||
data = chalk.yellow(data);
|
||||
} else if (log.type === 'stderr') {
|
||||
|
||||
@@ -6,7 +6,6 @@ import table from 'text-table';
|
||||
import Now from '../util';
|
||||
import getAliases from '../util/alias/get-aliases';
|
||||
import createOutput from '../util/output';
|
||||
import wait from '../util/output/wait';
|
||||
import logo from '../util/output/logo';
|
||||
import cmd from '../util/output/cmd.ts';
|
||||
import elapsed from '../util/output/elapsed.ts';
|
||||
@@ -132,7 +131,7 @@ export default async function main(ctx) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const cancelWait = wait(
|
||||
const cancelWait = output.spinner(
|
||||
`Fetching deployment(s) ${ids
|
||||
.map(id => `"${id}"`)
|
||||
.join(' ')} in ${chalk.bold(contextName)}`
|
||||
|
||||
@@ -11,6 +11,7 @@ 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';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
@@ -144,7 +145,7 @@ async function run({ output, token, contextName, currentTeam }) {
|
||||
const elapsed = ms(new Date() - start);
|
||||
|
||||
console.log(
|
||||
`> ${plural('secret', list.length, true)} found under ${chalk.bold(
|
||||
`${plural('secret', list.length, true)} found under ${chalk.bold(
|
||||
contextName
|
||||
)} ${chalk.gray(`[${elapsed}]`)}`
|
||||
);
|
||||
@@ -189,22 +190,29 @@ async function run({ output, token, contextName, currentTeam }) {
|
||||
const theSecret = list.find(secret => secret.name === args[0]);
|
||||
|
||||
if (theSecret) {
|
||||
const yes = argv.yes || (await readConfirmation(theSecret));
|
||||
const yes =
|
||||
argv.yes || (await readConfirmation(output, theSecret, contextName));
|
||||
if (!yes) {
|
||||
console.error(error('User abort'));
|
||||
output.print(`Aborted. Secret not deleted.\n`);
|
||||
return exit(0);
|
||||
}
|
||||
} else {
|
||||
console.error(error(`No secret found by name "${args[0]}"`));
|
||||
console.error(
|
||||
error(
|
||||
`No secret found by name "${args[0]}" under ${chalk.bold(
|
||||
contextName
|
||||
)}`
|
||||
)
|
||||
);
|
||||
return exit(1);
|
||||
}
|
||||
|
||||
const secret = await secrets.rm(args[0]);
|
||||
const elapsed = ms(new Date() - start);
|
||||
console.log(
|
||||
`${chalk.cyan('> Success!')} Secret ${chalk.bold(
|
||||
`${chalk.cyan('Success!')} Secret ${chalk.bold(
|
||||
secret.name
|
||||
)} removed ${chalk.gray(`[${elapsed}]`)}`
|
||||
)} under ${chalk.bold(contextName)} removed ${chalk.gray(`[${elapsed}]`)}`
|
||||
);
|
||||
return secrets.close();
|
||||
}
|
||||
@@ -223,9 +231,11 @@ async function run({ output, token, contextName, currentTeam }) {
|
||||
const secret = await secrets.rename(args[0], args[1]);
|
||||
const elapsed = ms(new Date() - start);
|
||||
console.log(
|
||||
`${chalk.cyan('> Success!')} Secret ${chalk.bold(
|
||||
`${chalk.cyan('Success!')} Secret ${chalk.bold(
|
||||
secret.oldName
|
||||
)} renamed to ${chalk.bold(args[1])} ${chalk.gray(`[${elapsed}]`)}`
|
||||
)} renamed to ${chalk.bold(args[1])} under ${chalk.bold(
|
||||
contextName
|
||||
)} ${chalk.gray(`[${elapsed}]`)}`
|
||||
);
|
||||
return secrets.close();
|
||||
}
|
||||
@@ -243,7 +253,7 @@ async function run({ output, token, contextName, currentTeam }) {
|
||||
if (args.length > 2) {
|
||||
const example = chalk.cyan(`$ now secret add -- "${args[0]}"`);
|
||||
console.log(
|
||||
`> If your secret has spaces or starts with '-', make sure to terminate command options with double dash and wrap it in quotes. Example: \n ${example} `
|
||||
`If your secret has spaces or starts with '-', make sure to terminate command options with double dash and wrap it in quotes. Example: \n ${example} `
|
||||
);
|
||||
}
|
||||
|
||||
@@ -259,9 +269,9 @@ async function run({ output, token, contextName, currentTeam }) {
|
||||
}
|
||||
|
||||
console.log(
|
||||
`${chalk.cyan('> Success!')} Secret ${chalk.bold(
|
||||
`${chalk.cyan('Success!')} Secret ${chalk.bold(
|
||||
name.toLowerCase()
|
||||
)} added (${chalk.bold(contextName)}) ${chalk.gray(`[${elapsed}]`)}`
|
||||
)} added under ${chalk.bold(contextName)} ${chalk.gray(`[${elapsed}]`)}`
|
||||
);
|
||||
return secrets.close();
|
||||
}
|
||||
@@ -278,33 +288,19 @@ process.on('uncaughtException', err => {
|
||||
exit(1);
|
||||
});
|
||||
|
||||
function readConfirmation(secret) {
|
||||
return new Promise(resolve => {
|
||||
const time = chalk.gray(`${ms(new Date() - new Date(secret.created))} ago`);
|
||||
const tbl = table([[chalk.bold(secret.name), time]], {
|
||||
align: ['r', 'l'],
|
||||
hsep: ' '.repeat(6),
|
||||
});
|
||||
|
||||
process.stdout.write(
|
||||
'> The following secret will be removed permanently\n'
|
||||
);
|
||||
process.stdout.write(` ${tbl}\n`);
|
||||
|
||||
process.stdout.write(
|
||||
`${chalk.bold.red('> Are you sure?')} ${chalk.gray('[y/N] ')}`
|
||||
);
|
||||
|
||||
process.stdin
|
||||
.on('data', d => {
|
||||
process.stdin.pause();
|
||||
resolve(
|
||||
d
|
||||
.toString()
|
||||
.trim()
|
||||
.toLowerCase() === 'y'
|
||||
);
|
||||
})
|
||||
.resume();
|
||||
async function readConfirmation(output, secret, contextName) {
|
||||
const time = chalk.gray(`${ms(new Date() - new Date(secret.created))} ago`);
|
||||
const tbl = table([[chalk.bold(secret.name), time]], {
|
||||
align: ['r', 'l'],
|
||||
hsep: ' '.repeat(6),
|
||||
});
|
||||
|
||||
output.print(
|
||||
`The following secret will be removed permanently from ${chalk.bold(
|
||||
contextName
|
||||
)}\n`
|
||||
);
|
||||
output.print(` ${tbl}\n`);
|
||||
|
||||
return confirm(`${chalk.bold.red('Are you sure?')}`, false);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export default async function({ apiUrl, token, teams, config }) {
|
||||
validateKeypress: validateSlugKeypress,
|
||||
initialValue: slug,
|
||||
valid: team,
|
||||
forceLowerCase: true
|
||||
forceLowerCase: true,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.message === 'USER_ABORT') {
|
||||
@@ -95,7 +95,7 @@ export default async function({ apiUrl, token, teams, config }) {
|
||||
try {
|
||||
name = await textInput({
|
||||
label: `- ${teamNamePrefix}`,
|
||||
validateKeypress: validateNameKeypress
|
||||
validateKeypress: validateNameKeypress,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.message === 'USER_ABORT') {
|
||||
@@ -153,7 +153,7 @@ export default async function({ apiUrl, token, teams, config }) {
|
||||
introMsg: 'Invite your teammates! When done, press enter on an empty field',
|
||||
noopMsg: `You can invite teammates later by running ${cmd(
|
||||
'now teams invite'
|
||||
)}`
|
||||
)}`,
|
||||
});
|
||||
|
||||
gracefulExit();
|
||||
|
||||
@@ -30,7 +30,7 @@ const domains = Array.from(
|
||||
'inbox.com',
|
||||
'mail.com',
|
||||
'gmx.com',
|
||||
'icloud.com'
|
||||
'icloud.com',
|
||||
])
|
||||
);
|
||||
|
||||
@@ -56,17 +56,15 @@ const emailAutoComplete = (value, teamSlug) => {
|
||||
return false;
|
||||
};
|
||||
|
||||
export default async function(
|
||||
{
|
||||
teams,
|
||||
args,
|
||||
config,
|
||||
introMsg,
|
||||
noopMsg = 'No changes made',
|
||||
apiUrl,
|
||||
token
|
||||
} = {}
|
||||
) {
|
||||
export default async function({
|
||||
teams,
|
||||
args,
|
||||
config,
|
||||
introMsg,
|
||||
noopMsg = 'No changes made',
|
||||
apiUrl,
|
||||
token,
|
||||
} = {}) {
|
||||
const { currentTeam: currentTeamId } = config;
|
||||
|
||||
const stopSpinner = wait('Fetching teams');
|
||||
@@ -86,7 +84,11 @@ export default async function(
|
||||
|
||||
if (!currentTeam) {
|
||||
// We specifically need a team scope here
|
||||
let err = `You can't run this command under ${param(user.username || user.email)}.\nPlease select a team scope using ${cmd('now switch')} or use ${cmd('--scope')}`;
|
||||
let err = `You can't run this command under ${param(
|
||||
user.username || user.email
|
||||
)}.\nPlease select a team scope using ${cmd('now switch')} or use ${cmd(
|
||||
'--scope'
|
||||
)}`;
|
||||
return fatalError(err);
|
||||
}
|
||||
|
||||
@@ -107,7 +109,9 @@ export default async function(
|
||||
userInfo = res.name || res.username;
|
||||
} catch (err) {
|
||||
if (err.code === 'user_not_found') {
|
||||
console.error(error(`No user exists with the email address "${email}".`));
|
||||
console.error(
|
||||
error(`No user exists with the email address "${email}".`)
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -115,7 +119,11 @@ export default async function(
|
||||
}
|
||||
|
||||
stopSpinner();
|
||||
console.log(`${chalk.cyan(chars.tick)} ${email}${userInfo ? ` (${userInfo})` : ''} ${elapsed()}`);
|
||||
console.log(
|
||||
`${chalk.cyan(chars.tick)} ${email}${
|
||||
userInfo ? ` (${userInfo})` : ''
|
||||
} ${elapsed()}`
|
||||
);
|
||||
} else {
|
||||
console.log(`${chalk.red(`✖ ${email}`)} ${chalk.gray('[invalid]')}`);
|
||||
}
|
||||
@@ -135,7 +143,7 @@ export default async function(
|
||||
email = await textInput({
|
||||
label: `- ${inviteUserPrefix}`,
|
||||
validateValue: validateEmail,
|
||||
autoComplete: value => emailAutoComplete(value, currentTeam.slug)
|
||||
autoComplete: value => emailAutoComplete(value, currentTeam.slug),
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.message !== 'USER_ABORT') {
|
||||
@@ -149,7 +157,10 @@ export default async function(
|
||||
stopSpinner = wait(inviteUserPrefix + email);
|
||||
try {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const { name, username } = await teams.inviteUser({ teamId: currentTeam.id, email });
|
||||
const { name, username } = await teams.inviteUser({
|
||||
teamId: currentTeam.id,
|
||||
email,
|
||||
});
|
||||
stopSpinner();
|
||||
const userInfo = name || username;
|
||||
email = `${email}${userInfo ? ` (${userInfo})` : ''} ${elapsed()}`;
|
||||
|
||||
@@ -27,20 +27,20 @@ export default async function({ teams, config, apiUrl, token }) {
|
||||
|
||||
if (accountIsCurrent) {
|
||||
currentTeam = {
|
||||
slug: user.username || user.email
|
||||
slug: user.username || user.email,
|
||||
};
|
||||
}
|
||||
|
||||
const teamList = list.map(({ slug, name }) => ({
|
||||
name,
|
||||
value: slug,
|
||||
current: slug === currentTeam.slug ? chars.tick : ''
|
||||
current: slug === currentTeam.slug ? chars.tick : '',
|
||||
}));
|
||||
|
||||
teamList.unshift({
|
||||
name: user.email,
|
||||
value: user.username || user.email,
|
||||
current: (accountIsCurrent && chars.tick) || ''
|
||||
current: (accountIsCurrent && chars.tick) || '',
|
||||
});
|
||||
|
||||
// Let's bring the current team to the beginning of the list
|
||||
|
||||
@@ -224,6 +224,7 @@ export interface Project {
|
||||
createdAt: number;
|
||||
devCommand?: string | null;
|
||||
framework?: string | null;
|
||||
rootDirectory?: string | null;
|
||||
}
|
||||
|
||||
export interface Org {
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Output } from '../output';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
import Client from '../client';
|
||||
import createCertForAlias from '../certs/create-cert-for-alias';
|
||||
import wait from '../output/wait';
|
||||
|
||||
export type AliasRecord = {
|
||||
uid: string;
|
||||
@@ -20,7 +19,7 @@ export default async function createAlias(
|
||||
alias: string,
|
||||
externalDomain: boolean
|
||||
) {
|
||||
let cancelMessage = wait(`Creating alias`);
|
||||
let cancelMessage = output.spinner(`Creating alias`);
|
||||
const result = await performCreateAlias(
|
||||
client,
|
||||
contextName,
|
||||
@@ -41,7 +40,7 @@ export default async function createAlias(
|
||||
return cert;
|
||||
}
|
||||
|
||||
let cancelMessage = wait(`Creating alias`);
|
||||
let cancelMessage = output.spinner(`Creating alias`);
|
||||
const secondTry = await performCreateAlias(
|
||||
client,
|
||||
contextName,
|
||||
@@ -66,7 +65,7 @@ async function performCreateAlias(
|
||||
`/now/deployments/${deployment.uid}/aliases`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: { alias }
|
||||
body: { alias },
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -77,7 +76,10 @@ async function performCreateAlias(
|
||||
return { uid: error.uid, alias: error.alias } as AliasRecord;
|
||||
}
|
||||
if (error.code === 'deployment_not_found') {
|
||||
return new ERRORS.DeploymentNotFound({ context: contextName, id: deployment.uid });
|
||||
return new ERRORS.DeploymentNotFound({
|
||||
context: contextName,
|
||||
id: deployment.uid,
|
||||
});
|
||||
}
|
||||
if (error.code === 'gone') {
|
||||
return new ERRORS.DeploymentFailedAliasImpossible();
|
||||
@@ -94,7 +96,7 @@ async function performCreateAlias(
|
||||
}
|
||||
}
|
||||
if (error.status === 400) {
|
||||
return new ERRORS.DeploymentNotReady({url: deployment.url })
|
||||
return new ERRORS.DeploymentNotReady({ url: deployment.url });
|
||||
}
|
||||
|
||||
throw error;
|
||||
|
||||
@@ -3,7 +3,6 @@ import chalk from 'chalk';
|
||||
import getAppLastDeployment from '../deploy/get-app-last-deployment';
|
||||
import getAppName from '../deploy/get-app-name';
|
||||
import fetchDeploymentByIdOrHost from '../deploy/get-deployment-by-id-or-host';
|
||||
import wait from '../output/wait';
|
||||
import Client from '../client';
|
||||
import { Output } from '../output';
|
||||
import { User, Config } from '../../types';
|
||||
@@ -17,7 +16,7 @@ export default async function getDeploymentForAlias(
|
||||
contextName: string,
|
||||
localConfig: Config
|
||||
) {
|
||||
const cancelWait = wait(
|
||||
const cancelWait = output.spinner(
|
||||
`Fetching deployment to alias in ${chalk.bold(contextName)}`
|
||||
);
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import * as ERRORS from '../errors-ts';
|
||||
import Client from '../client';
|
||||
import createCertForAlias from '../certs/create-cert-for-alias';
|
||||
import setupDomain from '../domains/setup-domain';
|
||||
import wait from '../output/wait';
|
||||
|
||||
const NOW_SH_REGEX = /\.now\.sh$/;
|
||||
|
||||
@@ -34,6 +33,7 @@ export default async function upsertPathAlias(
|
||||
}
|
||||
|
||||
const result = await performUpsertPathAlias(
|
||||
output,
|
||||
client,
|
||||
alias,
|
||||
rules,
|
||||
@@ -51,23 +51,26 @@ export default async function upsertPathAlias(
|
||||
return cert;
|
||||
}
|
||||
|
||||
return performUpsertPathAlias(client, alias, rules, contextName);
|
||||
return performUpsertPathAlias(output, client, alias, rules, contextName);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function performUpsertPathAlias(
|
||||
output: Output,
|
||||
client: Client,
|
||||
alias: string,
|
||||
rules: PathRule[],
|
||||
contextName: string
|
||||
) {
|
||||
const cancelMessage = wait(`Updating path alias rules for ${alias}`);
|
||||
const cancelMessage = output.spinner(
|
||||
`Updating path alias rules for ${alias}`
|
||||
);
|
||||
try {
|
||||
const record = await client.fetch<AliasRecord>(`/now/aliases`, {
|
||||
body: { alias, rules },
|
||||
method: 'POST'
|
||||
method: 'POST',
|
||||
});
|
||||
cancelMessage();
|
||||
return record;
|
||||
|
||||
@@ -5,7 +5,6 @@ import createCertForCns from './create-cert-for-cns';
|
||||
import getWildcardCnsForAlias from './get-wildcard-cns-for-alias';
|
||||
import joinWords from '../output/join-words';
|
||||
import stamp from '../output/stamp';
|
||||
import wait from '../output/wait';
|
||||
|
||||
export default async function createCertificateForAlias(
|
||||
output: Output,
|
||||
@@ -15,7 +14,7 @@ export default async function createCertificateForAlias(
|
||||
shouldBeWildcard: boolean
|
||||
) {
|
||||
const cns = shouldBeWildcard ? getWildcardCnsForAlias(alias) : [alias];
|
||||
const cancelMessage = wait(`Generating a certificate...`);
|
||||
const cancelMessage = output.spinner(`Generating a certificate...`);
|
||||
const certStamp = stamp();
|
||||
const cert = await createCertForCns(client, cns, context);
|
||||
if (cert instanceof NowError) {
|
||||
@@ -25,9 +24,9 @@ export default async function createCertificateForAlias(
|
||||
|
||||
cancelMessage();
|
||||
output.log(
|
||||
`Certificate for ${joinWords(
|
||||
cert.cns
|
||||
)} (${cert.uid}) created ${certStamp()}`
|
||||
`Certificate for ${joinWords(cert.cns)} (${
|
||||
cert.uid
|
||||
}) created ${certStamp()}`
|
||||
);
|
||||
return cert;
|
||||
}
|
||||
|
||||
@@ -11,10 +11,11 @@ export default async function createDeploy(
|
||||
paths,
|
||||
createArgs,
|
||||
org,
|
||||
isSettingUpProject
|
||||
isSettingUpProject,
|
||||
cwd
|
||||
) {
|
||||
try {
|
||||
return await now.create(paths, createArgs, org, isSettingUpProject);
|
||||
return await now.create(paths, createArgs, org, isSettingUpProject, cwd);
|
||||
} catch (error) {
|
||||
if (error.code === 'rate_limited') {
|
||||
throw new ERRORS_TS.DeploymentsRateLimited(error.message);
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Output } from '../output';
|
||||
import Client from '../client';
|
||||
import createCertForCns from '../certs/create-cert-for-cns';
|
||||
import setupDomain from '../domains/setup-domain';
|
||||
import wait from '../output/wait';
|
||||
import { InvalidDomain } from '../errors-ts';
|
||||
|
||||
export default async function generateCertForDeploy(
|
||||
@@ -23,7 +22,9 @@ export default async function generateCertForDeploy(
|
||||
return new InvalidDomain(deployURL);
|
||||
}
|
||||
|
||||
const cancelSetupWait = wait(`Setting custom suffix domain ${domain}`);
|
||||
const cancelSetupWait = output.spinner(
|
||||
`Setting custom suffix domain ${domain}`
|
||||
);
|
||||
const result = await setupDomain(output, client, domain, contextName);
|
||||
cancelSetupWait();
|
||||
if (result instanceof NowError) {
|
||||
@@ -31,7 +32,7 @@ export default async function generateCertForDeploy(
|
||||
}
|
||||
|
||||
// Generate the certificate with the given parameters
|
||||
const cancelCertWait = wait(
|
||||
const cancelCertWait = output.spinner(
|
||||
`Generating a wildcard certificate for ${domain}`
|
||||
);
|
||||
const cert = await createCertForCns(
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
DeploymentOptions,
|
||||
NowClientOptions,
|
||||
} from 'now-client';
|
||||
import wait from '../output/wait';
|
||||
import { Output } from '../output';
|
||||
// @ts-ignore
|
||||
import Now from '../../util';
|
||||
@@ -50,6 +49,7 @@ function printInspectUrl(
|
||||
export default async function processDeployment({
|
||||
isLegacy,
|
||||
org,
|
||||
cwd,
|
||||
projectName,
|
||||
isSettingUpProject,
|
||||
skipAutoDetectionConfirmation,
|
||||
@@ -70,6 +70,7 @@ export default async function processDeployment({
|
||||
projectName: string;
|
||||
isSettingUpProject: boolean;
|
||||
skipAutoDetectionConfirmation?: boolean;
|
||||
cwd?: string;
|
||||
}) {
|
||||
if (isLegacy) return processLegacyDeployment(args);
|
||||
|
||||
@@ -82,6 +83,7 @@ export default async function processDeployment({
|
||||
deployStamp,
|
||||
force,
|
||||
nowConfig,
|
||||
quiet,
|
||||
} = args;
|
||||
|
||||
const { debug } = output;
|
||||
@@ -104,7 +106,7 @@ export default async function processDeployment({
|
||||
let buildSpinner = null;
|
||||
let deploySpinner = null;
|
||||
|
||||
let deployingSpinner = wait(
|
||||
let deployingSpinner = output.spinner(
|
||||
isSettingUpProject
|
||||
? `Setting up project`
|
||||
: `Deploying ${chalk.bold(`${org.slug}/${projectName}`)}`,
|
||||
@@ -167,7 +169,7 @@ export default async function processDeployment({
|
||||
|
||||
await linkFolderToProject(
|
||||
output,
|
||||
paths[0],
|
||||
cwd || paths[0],
|
||||
{
|
||||
orgId: org.id,
|
||||
projectId: event.payload.projectId,
|
||||
@@ -178,11 +180,15 @@ export default async function processDeployment({
|
||||
|
||||
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'
|
||||
? wait('Queued', 0)
|
||||
: wait('Building', 0);
|
||||
? output.spinner('Queued', 0)
|
||||
: output.spinner('Building', 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,10 +198,17 @@ export default async function processDeployment({
|
||||
}
|
||||
|
||||
if (buildSpinner === null) {
|
||||
buildSpinner = wait('Building', 0);
|
||||
buildSpinner = output.spinner('Building', 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.type === 'canceled') {
|
||||
if (buildSpinner) {
|
||||
buildSpinner();
|
||||
}
|
||||
return event.payload;
|
||||
}
|
||||
|
||||
if (event.type === 'ready') {
|
||||
if (queuedSpinner) {
|
||||
queuedSpinner();
|
||||
@@ -204,7 +217,7 @@ export default async function processDeployment({
|
||||
buildSpinner();
|
||||
}
|
||||
|
||||
deploySpinner = wait('Completing', 0);
|
||||
deploySpinner = output.spinner('Completing', 0);
|
||||
}
|
||||
|
||||
// Handle error events
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
import pkg from '../../../package.json';
|
||||
|
||||
import { NoBuilderCacheError } from '../errors-ts';
|
||||
import wait from '../output/wait';
|
||||
import { Output } from '../output';
|
||||
import { getDistTag } from '../get-dist-tag';
|
||||
|
||||
@@ -246,7 +245,7 @@ export async function installBuilders(
|
||||
return;
|
||||
}
|
||||
|
||||
const stopSpinner = wait(
|
||||
const stopSpinner = output.spinner(
|
||||
`Installing ${pluralize(
|
||||
'Runtime',
|
||||
packagesToInstall.length
|
||||
|
||||
@@ -3,19 +3,10 @@
|
||||
import ms from 'ms';
|
||||
import bytes from 'bytes';
|
||||
import { promisify } from 'util';
|
||||
import { delimiter, dirname, extname, join } from 'path';
|
||||
import { delimiter, dirname, join } from 'path';
|
||||
import { fork, ChildProcess } from 'child_process';
|
||||
import { createFunction } from '@zeit/fun';
|
||||
import {
|
||||
Builder,
|
||||
File,
|
||||
Lambda,
|
||||
FileBlob,
|
||||
FileFsRef,
|
||||
detectApiDirectory,
|
||||
detectApiExtensions,
|
||||
} from '@now/build-utils';
|
||||
import which from 'which';
|
||||
import { Builder, File, Lambda, FileBlob, FileFsRef } from '@now/build-utils';
|
||||
import plural from 'pluralize';
|
||||
import minimatch from 'minimatch';
|
||||
import _treeKill from 'tree-kill';
|
||||
@@ -52,12 +43,6 @@ interface BuildMessageResult extends BuildMessage {
|
||||
|
||||
const treeKill = promisify(_treeKill);
|
||||
|
||||
let nodeBinPromise: Promise<string>;
|
||||
|
||||
async function getNodeBin(): Promise<string> {
|
||||
return which.sync('node', { nothrow: true }) || process.execPath;
|
||||
}
|
||||
|
||||
async function createBuildProcess(
|
||||
match: BuildMatch,
|
||||
buildEnv: EnvConfig,
|
||||
@@ -65,13 +50,8 @@ async function createBuildProcess(
|
||||
output: Output,
|
||||
yarnPath?: string
|
||||
): Promise<ChildProcess> {
|
||||
if (!nodeBinPromise) {
|
||||
nodeBinPromise = getNodeBin();
|
||||
}
|
||||
const [execPath, modulePath] = await Promise.all([
|
||||
nodeBinPromise,
|
||||
builderModulePathPromise,
|
||||
]);
|
||||
const { execPath } = process;
|
||||
const modulePath = await builderModulePathPromise;
|
||||
|
||||
// Ensure that `node` is in the builder's `PATH`
|
||||
let PATH = `${dirname(execPath)}${delimiter}${process.env.PATH}`;
|
||||
@@ -128,7 +108,7 @@ export async function executeBuild(
|
||||
const {
|
||||
builderWithPkg: { runInProcess, builder, package: pkg },
|
||||
} = match;
|
||||
const { src: entrypoint } = match;
|
||||
const { entrypoint } = match;
|
||||
const { env, debug, buildEnv, yarnPath, cwd: workPath } = devServer;
|
||||
|
||||
const startTime = Date.now();
|
||||
@@ -282,8 +262,8 @@ export async function executeBuild(
|
||||
}
|
||||
|
||||
const { output } = result;
|
||||
|
||||
const { cleanUrls } = nowConfig;
|
||||
|
||||
// Mimic fmeta-util and perform file renaming
|
||||
Object.entries(output).forEach(([path, value]) => {
|
||||
if (cleanUrls && path.endsWith('.html')) {
|
||||
@@ -294,6 +274,11 @@ export async function executeBuild(
|
||||
}
|
||||
}
|
||||
|
||||
const extensionless = devServer.getExtensionlessFile(path);
|
||||
if (extensionless) {
|
||||
path = extensionless;
|
||||
}
|
||||
|
||||
delete output[path];
|
||||
output[path] = value;
|
||||
});
|
||||
@@ -403,6 +388,7 @@ export async function getBuildMatches(
|
||||
cwd: string,
|
||||
yarnDir: string,
|
||||
output: Output,
|
||||
devServer: DevServer,
|
||||
fileList: string[]
|
||||
): Promise<BuildMatch[]> {
|
||||
const matches: BuildMatch[] = [];
|
||||
@@ -415,9 +401,6 @@ export async function getBuildMatches(
|
||||
|
||||
const noMatches: Builder[] = [];
|
||||
const builds = nowConfig.builds || [{ src: '**', use: '@now/static' }];
|
||||
const apiDir = detectApiDirectory(builds || []);
|
||||
const apiExtensions = detectApiExtensions(builds || []);
|
||||
const apiMatch = apiDir + '/';
|
||||
|
||||
for (const buildConfig of builds) {
|
||||
let { src, use } = buildConfig;
|
||||
@@ -436,10 +419,13 @@ export async function getBuildMatches(
|
||||
// We need to escape brackets since `glob` will
|
||||
// try to find a group otherwise
|
||||
src = src.replace(/(\[|\])/g, '[$1]');
|
||||
const ext = extname(src);
|
||||
if (apiDir && src.startsWith(apiMatch) && apiExtensions.has(ext)) {
|
||||
// lambda function files are trimmed of their file extension
|
||||
src = src.slice(0, -ext.length);
|
||||
|
||||
// lambda function files are trimmed of their file extension
|
||||
const mapToEntrypoint = new Map<string, string>();
|
||||
const extensionless = devServer.getExtensionlessFile(src);
|
||||
if (extensionless) {
|
||||
mapToEntrypoint.set(extensionless, src);
|
||||
src = extensionless;
|
||||
}
|
||||
|
||||
const files = fileList
|
||||
@@ -456,6 +442,7 @@ export async function getBuildMatches(
|
||||
matches.push({
|
||||
...buildConfig,
|
||||
src,
|
||||
entrypoint: mapToEntrypoint.get(src) || src,
|
||||
builderWithPkg,
|
||||
buildOutput: {},
|
||||
buildResults: new Map(),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user