mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-24 03:39:11 +00:00
Compare commits
89 Commits
@now/pytho
...
@now/next@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -2,6 +2,7 @@
|
|||||||
# https://help.github.com/en/articles/about-code-owners
|
# https://help.github.com/en/articles/about-code-owners
|
||||||
|
|
||||||
* @tootallnate @leo
|
* @tootallnate @leo
|
||||||
|
/.github/workflows @AndyBitz @styfle
|
||||||
/packages/frameworks @AndyBitz
|
/packages/frameworks @AndyBitz
|
||||||
/packages/now-cli/src/commands/dev/ @tootallnate @leo @styfle @AndyBitz
|
/packages/now-cli/src/commands/dev/ @tootallnate @leo @styfle @AndyBitz
|
||||||
/packages/now-cli/src/util/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
|
## 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.
|
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.
|
||||||
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.
|
||||||
2. Add an example to the `examples/` directory.
|
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.
|
||||||
The name of the directory should equal the slug of the framework used in `@now/frameworks`.
|
4. After your Pull Request has been merged and released, other users can select the example on the ZEIT Now dashboard and deploy it.
|
||||||
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.
|
|
||||||
|
|||||||
17
.github/workflows/cancel.yml
vendored
Normal file
17
.github/workflows/cancel.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
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@master
|
||||||
|
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:
|
test-unit:
|
||||||
name: Unit Tests
|
name: Unit Tests
|
||||||
timeout-minutes: 15
|
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:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: git fetch origin master --depth=10
|
- run: git fetch origin master --depth=10
|
||||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
- run: git fetch origin ${{ github.ref }} --depth=10
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
- run: yarn install
|
||||||
node-version: 10
|
- run: yarn run build
|
||||||
- run: yarn install && yarn run build
|
|
||||||
- run: yarn run test-lint
|
- run: yarn run test-lint
|
||||||
- run: yarn run test-unit --clean false
|
- 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:
|
with:
|
||||||
name: test-unit-output
|
name: test-unit-output
|
||||||
path: packages/now-cli/.nyc_output
|
path: packages/now-cli/.nyc_output
|
||||||
@@ -36,7 +42,8 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: git fetch origin master --depth=10
|
- run: git fetch origin master --depth=10
|
||||||
- run: git fetch origin ${{ github.ref }} --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
|
- run: yarn test-integration-once --clean false
|
||||||
|
|
||||||
test-now-cli:
|
test-now-cli:
|
||||||
@@ -45,15 +52,18 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest]
|
os: [ubuntu-latest]
|
||||||
node: [10, 12]
|
node: [10, 12]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: git fetch origin master --depth=10
|
- run: git fetch origin master --depth=10
|
||||||
- run: git fetch origin ${{ github.ref }} --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/
|
- name: Install Hugo
|
||||||
- run: yarn install && yarn run build
|
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
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
@@ -72,8 +82,11 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: git fetch origin master --depth=10
|
- run: git fetch origin master --depth=10
|
||||||
- run: git fetch origin ${{ github.ref }} --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/
|
- name: Install Hugo
|
||||||
- run: yarn install && yarn run build
|
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
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
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)
|
[](https://spectrum.chat/zeit)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -16,7 +16,6 @@ To quickly start a new project, run the following commands:
|
|||||||
```
|
```
|
||||||
now init # Pick an example project to clone
|
now init # Pick an example project to clone
|
||||||
cd <PROJECT> # Change directory to the newly created project
|
cd <PROJECT> # Change directory to the newly created project
|
||||||
now dev # Run locally during development
|
|
||||||
now # Deploy to the cloud
|
now # Deploy to the cloud
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,19 @@ import { NowRequest, NowResponse } from '@now/node';
|
|||||||
import { withApiHandler } from './_lib/util/with-api-handler';
|
import { withApiHandler } from './_lib/util/with-api-handler';
|
||||||
import frameworkList, { Framework } from '../packages/frameworks';
|
import frameworkList, { Framework } from '../packages/frameworks';
|
||||||
|
|
||||||
const frameworks: Framework[] = (frameworkList as Framework[]).map(
|
const frameworks = (frameworkList as Framework[]).map(frameworkItem => {
|
||||||
framework => {
|
const framework = {
|
||||||
delete framework.detectors;
|
...frameworkItem,
|
||||||
|
hasDetectors: Boolean(frameworkItem.detectors),
|
||||||
|
detectors: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
if (framework.logo) {
|
if (framework.logo) {
|
||||||
framework.logo = `https://res.cloudinary.com/zeit-inc/image/fetch/${framework.logo}`;
|
framework.logo = `https://res.cloudinary.com/zeit-inc/image/fetch/${framework.logo}`;
|
||||||
}
|
|
||||||
|
|
||||||
return framework;
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
return framework;
|
||||||
|
});
|
||||||
|
|
||||||
export default withApiHandler(async function(
|
export default withApiHandler(async function(
|
||||||
req: NowRequest,
|
req: NowRequest,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
# Angular Example
|
# Angular Example
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
# React Example
|
# React Example
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
# Docusaurus Example
|
# Docusaurus Example
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
# Eleventy Example
|
# Eleventy Example
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
# Ember Example
|
# Ember Example
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
# Gatsby Example
|
# Gatsby Example
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
# Gridsome Example
|
# Gridsome Example
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
# Hexo Example
|
# Hexo Example
|
||||||
|
|
||||||
|
|||||||
2
examples/hugo/themes/ananke/.gitignore
vendored
2
examples/hugo/themes/ananke/.gitignore
vendored
@@ -28,3 +28,5 @@ npm-debug.log
|
|||||||
|
|
||||||
/junit.xml
|
/junit.xml
|
||||||
partials/structure/stylesheet.html
|
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)}]);
|
||||||
@@ -25,18 +25,3 @@ You can deploy your new Jekyll project with a single command from your terminal
|
|||||||
```shell
|
```shell
|
||||||
$ now
|
$ 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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
# Next.js Example
|
# Next.js Example
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
# Polymer Example
|
# Polymer Example
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
# Preact Example
|
# Preact Example
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
# Saber Example
|
# Saber Example
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
# UmiJS Example
|
# UmiJS Example
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
# Vue.js Example
|
# Vue.js Example
|
||||||
|
|
||||||
|
|||||||
9
now.json
9
now.json
@@ -1,15 +1,18 @@
|
|||||||
{
|
{
|
||||||
"redirects": [
|
"rewrites": [
|
||||||
{
|
{
|
||||||
"source": "/",
|
"source": "/",
|
||||||
"destination": "https://zeit.co/new",
|
"destination": "/api/frameworks"
|
||||||
"statusCode": 307
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"GITHUB_ACCESS_TOKEN": "@now-api-examples-github-token",
|
"GITHUB_ACCESS_TOKEN": "@now-api-examples-github-token",
|
||||||
"SENTRY_DSN": "@sentry-product-dsn"
|
"SENTRY_DSN": "@sentry-product-dsn"
|
||||||
},
|
},
|
||||||
|
"github": {
|
||||||
|
"silent": true,
|
||||||
|
"autoJobCancelation": true
|
||||||
|
},
|
||||||
"headers": [
|
"headers": [
|
||||||
{
|
{
|
||||||
"source": "/api/frameworks",
|
"source": "/api/frameworks",
|
||||||
|
|||||||
@@ -91,6 +91,7 @@
|
|||||||
"@typescript-eslint/camelcase": 0,
|
"@typescript-eslint/camelcase": 0,
|
||||||
"@typescript-eslint/explicit-function-return-type": 0,
|
"@typescript-eslint/explicit-function-return-type": 0,
|
||||||
"@typescript-eslint/no-empty-function": 0,
|
"@typescript-eslint/no-empty-function": 0,
|
||||||
|
"@typescript-eslint/no-unused-vars": 2,
|
||||||
"@typescript-eslint/no-use-before-define": 0
|
"@typescript-eslint/no-use-before-define": 0
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"buildCommand": {
|
"buildCommand": {
|
||||||
"placeholder": "`next build` or `build` from `package.json`"
|
"placeholder": "`build` from `package.json` or `next build`"
|
||||||
},
|
},
|
||||||
"devCommand": {
|
"devCommand": {
|
||||||
"value": "next dev --port $PORT"
|
"value": "next dev --port $PORT"
|
||||||
@@ -808,5 +808,22 @@
|
|||||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/foundation.svg",
|
"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.",
|
"tagline": "Foundation is the most advanced responsive front-end framework in the world.",
|
||||||
"description": "A Foundation app, created with the Foundation CLI."
|
"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 {
|
export interface Framework {
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string | null;
|
||||||
logo: string;
|
logo: string;
|
||||||
demo: string;
|
demo?: string;
|
||||||
tagline: string;
|
tagline?: string;
|
||||||
website: string;
|
website?: string;
|
||||||
description: string;
|
description: string;
|
||||||
detectors?: {
|
detectors?: {
|
||||||
every?: FrameworkDetectionItem[];
|
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",
|
"name": "@now/frameworks",
|
||||||
"version": "0.0.7",
|
"version": "0.0.10-canary.0",
|
||||||
"main": "frameworks.json",
|
"main": "frameworks.json",
|
||||||
"license": "UNLICENSED"
|
"license": "UNLICENSED"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@now/build-utils",
|
"name": "@now/build-utils",
|
||||||
"version": "1.3.7-canary.1",
|
"version": "2.0.0-canary.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.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 { lstat, Stats } from 'fs-extra';
|
||||||
import FileFsRef from '../file-fs-ref';
|
import FileFsRef from '../file-fs-ref';
|
||||||
|
|
||||||
type GlobOptions = vanillaGlob_.IOptions;
|
export type GlobOptions = vanillaGlob_.IOptions;
|
||||||
|
|
||||||
interface FsFiles {
|
interface FsFiles {
|
||||||
[filePath: string]: FileFsRef;
|
[filePath: string]: FileFsRef;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Lambda, createLambda, getLambdaOptionsFromFunction } from './lambda';
|
|||||||
import { Prerender } from './prerender';
|
import { Prerender } from './prerender';
|
||||||
import download, { DownloadedFiles, isSymbolicLink } from './fs/download';
|
import download, { DownloadedFiles, isSymbolicLink } from './fs/download';
|
||||||
import getWriteableDirectory from './fs/get-writable-directory';
|
import getWriteableDirectory from './fs/get-writable-directory';
|
||||||
import glob from './fs/glob';
|
import glob, { GlobOptions } from './fs/glob';
|
||||||
import rename from './fs/rename';
|
import rename from './fs/rename';
|
||||||
import {
|
import {
|
||||||
execAsync,
|
execAsync,
|
||||||
@@ -40,6 +40,7 @@ export {
|
|||||||
DownloadedFiles,
|
DownloadedFiles,
|
||||||
getWriteableDirectory,
|
getWriteableDirectory,
|
||||||
glob,
|
glob,
|
||||||
|
GlobOptions,
|
||||||
rename,
|
rename,
|
||||||
execAsync,
|
execAsync,
|
||||||
spawnAsync,
|
spawnAsync,
|
||||||
@@ -63,12 +64,11 @@ export {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
detectRoutes,
|
detectBuilders,
|
||||||
detectOutputDirectory,
|
detectOutputDirectory,
|
||||||
detectApiDirectory,
|
detectApiDirectory,
|
||||||
detectApiExtensions,
|
detectApiExtensions,
|
||||||
} from './detect-routes';
|
} from './detect-builders';
|
||||||
export { detectBuilders } from './detect-builders';
|
|
||||||
export { detectFramework } from './detect-framework';
|
export { detectFramework } from './detect-framework';
|
||||||
export { DetectorFilesystem } from './detectors/filesystem';
|
export { DetectorFilesystem } from './detectors/filesystem';
|
||||||
export { readConfigFile } from './fs/read-config-file';
|
export { readConfigFile } from './fs/read-config-file';
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export interface Config {
|
|||||||
| undefined;
|
| undefined;
|
||||||
maxLambdaSize?: string;
|
maxLambdaSize?: string;
|
||||||
includeFiles?: string | string[];
|
includeFiles?: string | string[];
|
||||||
|
excludeFiles?: string | string[];
|
||||||
bundle?: boolean;
|
bundle?: boolean;
|
||||||
ldsflags?: string;
|
ldsflags?: string;
|
||||||
helpers?: boolean;
|
helpers?: boolean;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const {
|
|||||||
packAndDeploy,
|
packAndDeploy,
|
||||||
testDeployment,
|
testDeployment,
|
||||||
} = require('../../../test/lib/deployment/test-deployment');
|
} = require('../../../test/lib/deployment/test-deployment');
|
||||||
const { glob, detectBuilders, detectRoutes } = require('../');
|
const { glob, detectBuilders } = require('../');
|
||||||
|
|
||||||
jest.setTimeout(4 * 60 * 1000);
|
jest.setTimeout(4 * 60 * 1000);
|
||||||
|
|
||||||
@@ -110,8 +110,7 @@ it('Test `detectBuilders` and `detectRoutes`', async () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files, pkg);
|
const { builders, defaultRoutes } = await detectBuilders(files, pkg);
|
||||||
const { defaultRoutes } = await detectRoutes(files, builders);
|
|
||||||
|
|
||||||
const nowConfig = { builds: builders, routes: defaultRoutes, probes };
|
const nowConfig = { builds: builders, routes: defaultRoutes, probes };
|
||||||
await fs.writeFile(
|
await fs.writeFile(
|
||||||
@@ -126,7 +125,7 @@ it('Test `detectBuilders` and `detectRoutes`', async () => {
|
|||||||
expect(deployment).toBeDefined();
|
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 fixture = path.join(__dirname, 'fixtures', '02-zero-config-api');
|
||||||
const pkg = await fs.readJSON(path.join(fixture, 'package.json'));
|
const pkg = await fs.readJSON(path.join(fixture, 'package.json'));
|
||||||
const fileList = await glob('**', fixture);
|
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 { builders, defaultRoutes } = await detectBuilders(files, pkg);
|
||||||
const { defaultRoutes } = await detectRoutes(files, builders);
|
|
||||||
|
|
||||||
const nowConfig = { builds: builders, routes: defaultRoutes, probes };
|
const nowConfig = { builds: builders, routes: defaultRoutes, probes };
|
||||||
await fs.writeFile(
|
await fs.writeFile(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Source, Route } from '@now/routing-utils';
|
import { Source, Route } from '@now/routing-utils';
|
||||||
import { detectBuilders, detectRoutes } from '../src';
|
import { detectBuilders } from '../src';
|
||||||
import {
|
import {
|
||||||
detectOutputDirectory,
|
detectOutputDirectory,
|
||||||
detectApiDirectory,
|
detectApiDirectory,
|
||||||
@@ -151,6 +151,27 @@ describe('Test `detectBuilders`', () => {
|
|||||||
expect(builders!.length).toBe(2);
|
expect(builders!.length).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('api go with test files', async () => {
|
||||||
|
const files = [
|
||||||
|
'api/index.go',
|
||||||
|
'api/index_test.go',
|
||||||
|
'api/test.go',
|
||||||
|
'api/testing_another.go',
|
||||||
|
'api/readme.md',
|
||||||
|
'api/config/staging.go',
|
||||||
|
'api/config/staging_test.go',
|
||||||
|
'api/config/production.go',
|
||||||
|
'api/config/production_test.go',
|
||||||
|
'api/src/controllers/health.go',
|
||||||
|
'api/src/controllers/user.module.go',
|
||||||
|
'api/src/controllers/user.module_test.go',
|
||||||
|
];
|
||||||
|
|
||||||
|
const { builders } = await detectBuilders(files);
|
||||||
|
expect(builders!.length).toBe(7);
|
||||||
|
expect(builders!.some(b => b.src.endsWith('_test.go'))).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('just public', async () => {
|
it('just public', async () => {
|
||||||
const files = ['public/index.html', 'public/favicon.ico', 'README.md'];
|
const files = ['public/index.html', 'public/favicon.ico', 'README.md'];
|
||||||
|
|
||||||
@@ -478,7 +499,7 @@ describe('Test `detectBuilders`', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(errors!.length).toBe(1);
|
expect(errors!.length).toBe(1);
|
||||||
expect(errors![0].code).toBe('invalid_function_source');
|
expect(errors![0].code).toBe('unused_function');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('do not allow empty functions', async () => {
|
it('do not allow empty functions', async () => {
|
||||||
@@ -627,14 +648,14 @@ describe('Test `detectBuilders`', () => {
|
|||||||
|
|
||||||
const files = ['dist/index.html', 'dist/style.css'];
|
const files = ['dist/index.html', 'dist/style.css'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files, null, { projectSettings });
|
const { builders, defaultRoutes } = await detectBuilders(files, null, {
|
||||||
|
projectSettings,
|
||||||
|
});
|
||||||
|
|
||||||
expect(builders!.length).toBe(1);
|
expect(builders!.length).toBe(1);
|
||||||
expect(builders![0].src).toBe('dist/**/*');
|
expect(builders![0].src).toBe('dist/**/*');
|
||||||
expect(builders![0].use).toBe('@now/static');
|
expect(builders![0].use).toBe('@now/static');
|
||||||
|
|
||||||
const { defaultRoutes } = await detectRoutes(files, builders!);
|
|
||||||
|
|
||||||
expect(defaultRoutes!.length).toBe(1);
|
expect(defaultRoutes!.length).toBe(1);
|
||||||
expect((defaultRoutes![0] as any).src).toBe('/(.*)');
|
expect((defaultRoutes![0] as any).src).toBe('/(.*)');
|
||||||
expect((defaultRoutes![0] as any).dest).toBe('/dist/$1');
|
expect((defaultRoutes![0] as any).dest).toBe('/dist/$1');
|
||||||
@@ -647,14 +668,14 @@ describe('Test `detectBuilders`', () => {
|
|||||||
|
|
||||||
const files = ['api/user.ts', 'output/index.html', 'output/style.css'];
|
const files = ['api/user.ts', 'output/index.html', 'output/style.css'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files, null, { projectSettings });
|
const { builders, defaultRoutes } = await detectBuilders(files, null, {
|
||||||
|
projectSettings,
|
||||||
|
});
|
||||||
|
|
||||||
expect(builders!.length).toBe(2);
|
expect(builders!.length).toBe(2);
|
||||||
expect(builders![1].src).toBe('output/**/*');
|
expect(builders![1].src).toBe('output/**/*');
|
||||||
expect(builders![1].use).toBe('@now/static');
|
expect(builders![1].use).toBe('@now/static');
|
||||||
|
|
||||||
const { defaultRoutes } = await detectRoutes(files, builders!);
|
|
||||||
|
|
||||||
expect(defaultRoutes!.length).toBe(3);
|
expect(defaultRoutes!.length).toBe(3);
|
||||||
expect((defaultRoutes![1] as any).status).toBe(404);
|
expect((defaultRoutes![1] as any).status).toBe(404);
|
||||||
expect((defaultRoutes![2] as any).src).toBe('/(.*)');
|
expect((defaultRoutes![2] as any).src).toBe('/(.*)');
|
||||||
@@ -745,14 +766,60 @@ describe('Test `detectBuilders`', () => {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('All static if `buildCommand` is an empty string', async () => {
|
||||||
|
const files = ['index.html'];
|
||||||
|
const projectSettings = { buildCommand: '' };
|
||||||
|
const { builders, errors } = await detectBuilders(files, null, {
|
||||||
|
projectSettings,
|
||||||
|
});
|
||||||
|
expect(errors).toBe(null);
|
||||||
|
expect(builders).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('All static if `outputDirectory` is an empty string', async () => {
|
||||||
|
const files = ['index.html'];
|
||||||
|
const projectSettings = { outputDirectory: '' };
|
||||||
|
const { builders, errors } = await detectBuilders(files, null, {
|
||||||
|
projectSettings,
|
||||||
|
});
|
||||||
|
expect(errors).toBe(null);
|
||||||
|
expect(builders).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('All static if `buildCommand` is an empty string with an `outputDirectory`', async () => {
|
||||||
|
const files = ['out/index.html'];
|
||||||
|
const projectSettings = { buildCommand: '', outputDirectory: 'out' };
|
||||||
|
const { builders, errors } = await detectBuilders(files, null, {
|
||||||
|
projectSettings,
|
||||||
|
});
|
||||||
|
expect(errors).toBe(null);
|
||||||
|
expect(builders![0]!.use).toBe('@now/static');
|
||||||
|
expect(builders![0]!.src).toBe('out/**/*');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('do not require build script when `buildCommand` is an empty string', async () => {
|
||||||
|
const files = ['index.html', 'about.html', 'package.json'];
|
||||||
|
const projectSettings = { buildCommand: '', outputDirectory: '' };
|
||||||
|
const pkg = {
|
||||||
|
scripts: {
|
||||||
|
build: 'false',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const { builders, errors } = await detectBuilders(files, pkg, {
|
||||||
|
projectSettings,
|
||||||
|
});
|
||||||
|
expect(builders).toBe(null);
|
||||||
|
expect(errors).toBe(null);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Test `detectRoutes`', async () => {
|
it('Test `detectRoutes`', async () => {
|
||||||
{
|
{
|
||||||
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
|
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes } = await detectBuilders(files);
|
||||||
const { defaultRoutes } = await detectRoutes(files, builders!);
|
|
||||||
expect(defaultRoutes!.length).toBe(3);
|
expect(defaultRoutes!.length).toBe(3);
|
||||||
expect((defaultRoutes![0] as any).dest).toBe('/api/team.js');
|
expect((defaultRoutes![0] as any).dest).toBe('/api/team.js');
|
||||||
expect((defaultRoutes![1] as any).dest).toBe('/api/user.go');
|
expect((defaultRoutes![1] as any).dest).toBe('/api/user.go');
|
||||||
@@ -763,41 +830,36 @@ it('Test `detectRoutes`', async () => {
|
|||||||
{
|
{
|
||||||
const files = ['api/user.go', 'api/user.js'];
|
const files = ['api/user.go', 'api/user.js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { errors } = await detectBuilders(files);
|
||||||
const { error } = await detectRoutes(files, builders!);
|
expect(errors![0]!.code).toBe('conflicting_file_path');
|
||||||
expect(error!.code).toBe('conflicting_file_path');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const files = ['api/[user].go', 'api/[team]/[id].js'];
|
const files = ['api/[user].go', 'api/[team]/[id].js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { errors } = await detectBuilders(files);
|
||||||
const { error } = await detectRoutes(files, builders!);
|
expect(errors![0]!.code).toBe('conflicting_file_path');
|
||||||
expect(error!.code).toBe('conflicting_file_path');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const files = ['api/[team]/[team].js'];
|
const files = ['api/[team]/[team].js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { errors } = await detectBuilders(files);
|
||||||
const { error } = await detectRoutes(files, builders!);
|
expect(errors![0]!.code).toBe('conflicting_path_segment');
|
||||||
expect(error!.code).toBe('conflicting_path_segment');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const files = ['api/date/index.js', 'api/date/index.go'];
|
const files = ['api/date/index.js', 'api/date/index.go'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes, errors } = await detectBuilders(files);
|
||||||
const { defaultRoutes, error } = await detectRoutes(files, builders!);
|
|
||||||
expect(defaultRoutes).toBe(null);
|
expect(defaultRoutes).toBe(null);
|
||||||
expect(error!.code).toBe('conflicting_file_path');
|
expect(errors![0]!.code).toBe('conflicting_file_path');
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
|
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes } = await detectBuilders(files);
|
||||||
const { defaultRoutes } = await detectRoutes(files, builders!);
|
|
||||||
expect(defaultRoutes!.length).toBe(3);
|
expect(defaultRoutes!.length).toBe(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -808,8 +870,7 @@ it('Test `detectRoutes`', async () => {
|
|||||||
'api/[endpoint]/[id].js',
|
'api/[endpoint]/[id].js',
|
||||||
];
|
];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes } = await detectBuilders(files);
|
||||||
const { defaultRoutes } = await detectRoutes(files, builders!);
|
|
||||||
expect((defaultRoutes![2] as any).status).toBe(404);
|
expect((defaultRoutes![2] as any).status).toBe(404);
|
||||||
expect((defaultRoutes![2] as any).src).toBe('^/api(/.*)?$');
|
expect((defaultRoutes![2] as any).src).toBe('^/api(/.*)?$');
|
||||||
expect((defaultRoutes![3] as any).src).toBe('/(.*)');
|
expect((defaultRoutes![3] as any).src).toBe('/(.*)');
|
||||||
@@ -824,8 +885,7 @@ it('Test `detectRoutes`', async () => {
|
|||||||
};
|
};
|
||||||
const files = ['public/index.html', 'api/[endpoint].js'];
|
const files = ['public/index.html', 'api/[endpoint].js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files, pkg);
|
const { defaultRoutes } = await detectBuilders(files, pkg);
|
||||||
const { defaultRoutes } = await detectRoutes(files, builders!);
|
|
||||||
expect((defaultRoutes![1] as any).status).toBe(404);
|
expect((defaultRoutes![1] as any).status).toBe(404);
|
||||||
expect((defaultRoutes![1] as any).src).toBe('^/api(/.*)?$');
|
expect((defaultRoutes![1] as any).src).toBe('^/api(/.*)?$');
|
||||||
expect(defaultRoutes!.length).toBe(2);
|
expect(defaultRoutes!.length).toBe(2);
|
||||||
@@ -834,8 +894,7 @@ it('Test `detectRoutes`', async () => {
|
|||||||
{
|
{
|
||||||
const files = ['public/index.html'];
|
const files = ['public/index.html'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes } = await detectBuilders(files);
|
||||||
const { defaultRoutes } = await detectRoutes(files, builders!);
|
|
||||||
|
|
||||||
expect(defaultRoutes!.length).toBe(1);
|
expect(defaultRoutes!.length).toBe(1);
|
||||||
}
|
}
|
||||||
@@ -843,8 +902,7 @@ it('Test `detectRoutes`', async () => {
|
|||||||
{
|
{
|
||||||
const files = ['api/date/index.js', 'api/date.js'];
|
const files = ['api/date/index.js', 'api/date.js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes } = await detectBuilders(files);
|
||||||
const { defaultRoutes } = await detectRoutes(files, builders!);
|
|
||||||
|
|
||||||
expect(defaultRoutes!.length).toBe(3);
|
expect(defaultRoutes!.length).toBe(3);
|
||||||
expect((defaultRoutes![0] as any).src).toBe(
|
expect((defaultRoutes![0] as any).src).toBe(
|
||||||
@@ -860,8 +918,7 @@ it('Test `detectRoutes`', async () => {
|
|||||||
{
|
{
|
||||||
const files = ['api/date.js', 'api/[date]/index.js'];
|
const files = ['api/date.js', 'api/[date]/index.js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes } = await detectBuilders(files);
|
||||||
const { defaultRoutes } = await detectRoutes(files, builders!);
|
|
||||||
|
|
||||||
expect(defaultRoutes!.length).toBe(3);
|
expect(defaultRoutes!.length).toBe(3);
|
||||||
expect((defaultRoutes![0] as any).src).toBe(
|
expect((defaultRoutes![0] as any).src).toBe(
|
||||||
@@ -885,8 +942,7 @@ it('Test `detectRoutes`', async () => {
|
|||||||
'api/food.ts',
|
'api/food.ts',
|
||||||
'api/ts/gold.ts',
|
'api/ts/gold.ts',
|
||||||
];
|
];
|
||||||
const { builders } = await detectBuilders(files);
|
const { builders, defaultRoutes } = await detectBuilders(files);
|
||||||
const { defaultRoutes } = await detectRoutes(files, builders!);
|
|
||||||
|
|
||||||
expect(builders!.length).toBe(4);
|
expect(builders!.length).toBe(4);
|
||||||
expect(builders![0].use).toBe('@now/node');
|
expect(builders![0].use).toBe('@now/node');
|
||||||
@@ -901,8 +957,7 @@ it('Test `detectRoutes`', async () => {
|
|||||||
const functions = { 'api/user.php': { runtime: 'now-php@0.0.8' } };
|
const functions = { 'api/user.php': { runtime: 'now-php@0.0.8' } };
|
||||||
const files = ['api/user.php'];
|
const files = ['api/user.php'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files, null, { functions });
|
const { defaultRoutes } = await detectBuilders(files, null, { functions });
|
||||||
const { defaultRoutes } = await detectRoutes(files, builders!);
|
|
||||||
|
|
||||||
expect(defaultRoutes!.length).toBe(2);
|
expect(defaultRoutes!.length).toBe(2);
|
||||||
expect((defaultRoutes![0] as any).dest).toBe('/api/user.php');
|
expect((defaultRoutes![0] as any).dest).toBe('/api/user.php');
|
||||||
@@ -915,12 +970,9 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
|||||||
{
|
{
|
||||||
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
|
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes } = await detectBuilders(files, null, {
|
||||||
const { defaultRoutes } = await detectRoutes(
|
featHandleMiss,
|
||||||
files,
|
});
|
||||||
builders!,
|
|
||||||
featHandleMiss
|
|
||||||
);
|
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
{ handle: 'miss' },
|
{ handle: 'miss' },
|
||||||
{
|
{
|
||||||
@@ -939,49 +991,40 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
|||||||
{
|
{
|
||||||
const files = ['api/user.go', 'api/user.js'];
|
const files = ['api/user.go', 'api/user.js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { errors } = await detectBuilders(files, null, { featHandleMiss });
|
||||||
const { error } = await detectRoutes(files, builders!, featHandleMiss);
|
expect(errors![0]!.code).toBe('conflicting_file_path');
|
||||||
expect(error!.code).toBe('conflicting_file_path');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const files = ['api/[user].go', 'api/[team]/[id].js'];
|
const files = ['api/[user].go', 'api/[team]/[id].js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { errors } = await detectBuilders(files, null, { featHandleMiss });
|
||||||
const { error } = await detectRoutes(files, builders!, featHandleMiss);
|
expect(errors![0]!.code).toBe('conflicting_file_path');
|
||||||
expect(error!.code).toBe('conflicting_file_path');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const files = ['api/[team]/[team].js'];
|
const files = ['api/[team]/[team].js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { errors } = await detectBuilders(files, null, { featHandleMiss });
|
||||||
const { error } = await detectRoutes(files, builders!, featHandleMiss);
|
expect(errors![0]!.code).toBe('conflicting_path_segment');
|
||||||
expect(error!.code).toBe('conflicting_path_segment');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const files = ['api/date/index.js', 'api/date/index.go'];
|
const files = ['api/date/index.js', 'api/date/index.go'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes, errors } = await detectBuilders(files, null, {
|
||||||
const { defaultRoutes, error } = await detectRoutes(
|
featHandleMiss,
|
||||||
files,
|
});
|
||||||
builders!,
|
|
||||||
featHandleMiss
|
|
||||||
);
|
|
||||||
expect(defaultRoutes).toBe(null);
|
expect(defaultRoutes).toBe(null);
|
||||||
expect(error!.code).toBe('conflicting_file_path');
|
expect(errors![0]!.code).toBe('conflicting_file_path');
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
|
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes } = await detectBuilders(files, null, {
|
||||||
const { defaultRoutes } = await detectRoutes(
|
featHandleMiss,
|
||||||
files,
|
});
|
||||||
builders!,
|
|
||||||
featHandleMiss
|
|
||||||
);
|
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
{ handle: 'miss' },
|
{ handle: 'miss' },
|
||||||
{
|
{
|
||||||
@@ -1014,12 +1057,9 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
|||||||
'api/[endpoint]/[id].js',
|
'api/[endpoint]/[id].js',
|
||||||
];
|
];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes } = await detectBuilders(files, null, {
|
||||||
const { defaultRoutes } = await detectRoutes(
|
featHandleMiss,
|
||||||
files,
|
});
|
||||||
builders!,
|
|
||||||
featHandleMiss
|
|
||||||
);
|
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
{ handle: 'miss' },
|
{ handle: 'miss' },
|
||||||
{
|
{
|
||||||
@@ -1058,12 +1098,9 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
|||||||
|
|
||||||
const files = ['public/index.html', 'api/[endpoint].js'];
|
const files = ['public/index.html', 'api/[endpoint].js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files, pkg);
|
const { defaultRoutes } = await detectBuilders(files, pkg, {
|
||||||
const { defaultRoutes } = await detectRoutes(
|
featHandleMiss,
|
||||||
files,
|
});
|
||||||
builders!,
|
|
||||||
featHandleMiss
|
|
||||||
);
|
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
{ handle: 'miss' },
|
{ handle: 'miss' },
|
||||||
{
|
{
|
||||||
@@ -1087,24 +1124,18 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
|||||||
{
|
{
|
||||||
const files = ['public/index.html'];
|
const files = ['public/index.html'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes } = await detectBuilders(files, null, {
|
||||||
const { defaultRoutes } = await detectRoutes(
|
featHandleMiss,
|
||||||
files,
|
});
|
||||||
builders!,
|
|
||||||
featHandleMiss
|
|
||||||
);
|
|
||||||
expect(defaultRoutes).toStrictEqual([]);
|
expect(defaultRoutes).toStrictEqual([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const files = ['api/date/index.js', 'api/date.js'];
|
const files = ['api/date/index.js', 'api/date.js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes } = await detectBuilders(files, null, {
|
||||||
const { defaultRoutes } = await detectRoutes(
|
featHandleMiss,
|
||||||
files,
|
});
|
||||||
builders!,
|
|
||||||
featHandleMiss
|
|
||||||
);
|
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
{ handle: 'miss' },
|
{ handle: 'miss' },
|
||||||
{
|
{
|
||||||
@@ -1123,12 +1154,9 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
|||||||
{
|
{
|
||||||
const files = ['api/date.js', 'api/[date]/index.js'];
|
const files = ['api/date.js', 'api/[date]/index.js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes } = await detectBuilders(files, null, {
|
||||||
const { defaultRoutes } = await detectRoutes(
|
featHandleMiss,
|
||||||
files,
|
});
|
||||||
builders!,
|
|
||||||
featHandleMiss
|
|
||||||
);
|
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
{ handle: 'miss' },
|
{ handle: 'miss' },
|
||||||
{
|
{
|
||||||
@@ -1158,12 +1186,9 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
|||||||
'api/food.ts',
|
'api/food.ts',
|
||||||
'api/ts/gold.ts',
|
'api/ts/gold.ts',
|
||||||
];
|
];
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes } = await detectBuilders(files, null, {
|
||||||
const { defaultRoutes } = await detectRoutes(
|
featHandleMiss,
|
||||||
files,
|
});
|
||||||
builders!,
|
|
||||||
featHandleMiss
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
{ handle: 'miss' },
|
{ handle: 'miss' },
|
||||||
@@ -1181,12 +1206,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
|||||||
const functions = { 'api/user.php': { runtime: 'now-php@0.0.8' } };
|
const functions = { 'api/user.php': { runtime: 'now-php@0.0.8' } };
|
||||||
const files = ['api/user.php'];
|
const files = ['api/user.php'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files, null, { functions });
|
const { defaultRoutes } = await detectBuilders(files, null, {
|
||||||
const { defaultRoutes } = await detectRoutes(
|
functions,
|
||||||
files,
|
featHandleMiss,
|
||||||
builders!,
|
});
|
||||||
featHandleMiss
|
|
||||||
);
|
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
{ handle: 'miss' },
|
{ handle: 'miss' },
|
||||||
{
|
{
|
||||||
@@ -1200,8 +1223,11 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async () => {
|
it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async () => {
|
||||||
const featHandleMiss = true;
|
const options = {
|
||||||
const cleanUrls = true;
|
featHandleMiss: true,
|
||||||
|
cleanUrls: true,
|
||||||
|
};
|
||||||
|
|
||||||
const testHeaders = (redirectRoutes: Route[] | null) => {
|
const testHeaders = (redirectRoutes: Route[] | null) => {
|
||||||
if (!redirectRoutes || redirectRoutes.length === 0) {
|
if (!redirectRoutes || redirectRoutes.length === 0) {
|
||||||
throw new Error('Expected one redirect but found none');
|
throw new Error('Expected one redirect but found none');
|
||||||
@@ -1213,12 +1239,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
|||||||
{
|
{
|
||||||
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
|
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
null,
|
||||||
featHandleMiss,
|
options
|
||||||
cleanUrls
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
@@ -1260,65 +1284,43 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
|||||||
{
|
{
|
||||||
const files = ['api/user.go', 'api/user.js'];
|
const files = ['api/user.go', 'api/user.js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { errors } = await detectBuilders(files, null, options);
|
||||||
const { error } = await detectRoutes(
|
expect(errors![0]!.code).toBe('conflicting_file_path');
|
||||||
files,
|
|
||||||
builders!,
|
|
||||||
featHandleMiss,
|
|
||||||
cleanUrls
|
|
||||||
);
|
|
||||||
expect(error!.code).toBe('conflicting_file_path');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const files = ['api/[user].go', 'api/[team]/[id].js'];
|
const files = ['api/[user].go', 'api/[team]/[id].js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { errors } = await detectBuilders(files, null, options);
|
||||||
const { error } = await detectRoutes(
|
expect(errors![0]!.code).toBe('conflicting_file_path');
|
||||||
files,
|
|
||||||
builders!,
|
|
||||||
featHandleMiss,
|
|
||||||
cleanUrls
|
|
||||||
);
|
|
||||||
expect(error!.code).toBe('conflicting_file_path');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const files = ['api/[team]/[team].js'];
|
const files = ['api/[team]/[team].js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { errors } = await detectBuilders(files, null, options);
|
||||||
const { error } = await detectRoutes(
|
expect(errors![0]!.code).toBe('conflicting_path_segment');
|
||||||
files,
|
|
||||||
builders!,
|
|
||||||
featHandleMiss,
|
|
||||||
cleanUrls
|
|
||||||
);
|
|
||||||
expect(error!.code).toBe('conflicting_path_segment');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const files = ['api/date/index.js', 'api/date/index.go'];
|
const files = ['api/date/index.js', 'api/date/index.go'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes, errors } = await detectBuilders(
|
||||||
const { defaultRoutes, error } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
null,
|
||||||
featHandleMiss,
|
options
|
||||||
cleanUrls
|
|
||||||
);
|
);
|
||||||
expect(defaultRoutes).toBe(null);
|
expect(defaultRoutes).toBe(null);
|
||||||
expect(error!.code).toBe('conflicting_file_path');
|
expect(errors![0]!.code).toBe('conflicting_file_path');
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
|
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
null,
|
||||||
featHandleMiss,
|
options
|
||||||
cleanUrls
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
@@ -1348,12 +1350,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
|||||||
'api/[endpoint]/[id].js',
|
'api/[endpoint]/[id].js',
|
||||||
];
|
];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
null,
|
||||||
featHandleMiss,
|
options
|
||||||
cleanUrls
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
@@ -1389,12 +1389,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
|||||||
|
|
||||||
const files = ['public/index.html', 'api/[endpoint].js'];
|
const files = ['public/index.html', 'api/[endpoint].js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files, pkg);
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
pkg,
|
||||||
featHandleMiss,
|
options
|
||||||
cleanUrls
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
@@ -1415,25 +1413,17 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
|||||||
{
|
{
|
||||||
const files = ['public/index.html'];
|
const files = ['public/index.html'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes } = await detectBuilders(files, null, options);
|
||||||
const { defaultRoutes } = await detectRoutes(
|
|
||||||
files,
|
|
||||||
builders!,
|
|
||||||
featHandleMiss,
|
|
||||||
cleanUrls
|
|
||||||
);
|
|
||||||
expect(defaultRoutes).toStrictEqual([]);
|
expect(defaultRoutes).toStrictEqual([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const files = ['api/date/index.js', 'api/date.js'];
|
const files = ['api/date/index.js', 'api/date.js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
null,
|
||||||
featHandleMiss,
|
options
|
||||||
cleanUrls
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
@@ -1449,12 +1439,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
|||||||
{
|
{
|
||||||
const files = ['api/date.js', 'api/[date]/index.js'];
|
const files = ['api/date.js', 'api/[date]/index.js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
null,
|
||||||
featHandleMiss,
|
options
|
||||||
cleanUrls
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
@@ -1481,12 +1469,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
|||||||
'api/food.ts',
|
'api/food.ts',
|
||||||
'api/ts/gold.ts',
|
'api/ts/gold.ts',
|
||||||
];
|
];
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
null,
|
||||||
featHandleMiss,
|
options
|
||||||
cleanUrls
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
@@ -1500,12 +1486,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
|||||||
const functions = { 'api/user.php': { runtime: 'now-php@0.0.8' } };
|
const functions = { 'api/user.php': { runtime: 'now-php@0.0.8' } };
|
||||||
const files = ['api/user.php'];
|
const files = ['api/user.php'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files, null, { functions });
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
null,
|
||||||
featHandleMiss,
|
{ functions, ...options }
|
||||||
cleanUrls
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
@@ -1516,9 +1500,12 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingSlash=true`', async () => {
|
it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingSlash=true`', async () => {
|
||||||
const featHandleMiss = true;
|
const options = {
|
||||||
const cleanUrls = true;
|
featHandleMiss: true,
|
||||||
const trailingSlash = true;
|
cleanUrls: true,
|
||||||
|
trailingSlash: true,
|
||||||
|
};
|
||||||
|
|
||||||
const testHeaders = (redirectRoutes: Route[] | null) => {
|
const testHeaders = (redirectRoutes: Route[] | null) => {
|
||||||
if (!redirectRoutes || redirectRoutes.length === 0) {
|
if (!redirectRoutes || redirectRoutes.length === 0) {
|
||||||
throw new Error('Expected one redirect but found none');
|
throw new Error('Expected one redirect but found none');
|
||||||
@@ -1530,13 +1517,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
|||||||
{
|
{
|
||||||
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
|
const files = ['api/user.go', 'api/team.js', 'api/package.json'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
null,
|
||||||
featHandleMiss,
|
options
|
||||||
cleanUrls,
|
|
||||||
trailingSlash
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
@@ -1581,13 +1565,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
|||||||
{
|
{
|
||||||
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
|
const files = ['api/[endpoint].js', 'api/[endpoint]/[id].js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
null,
|
||||||
featHandleMiss,
|
options
|
||||||
cleanUrls,
|
|
||||||
trailingSlash
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
@@ -1617,13 +1598,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
|||||||
'api/[endpoint]/[id].js',
|
'api/[endpoint]/[id].js',
|
||||||
];
|
];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
null,
|
||||||
featHandleMiss,
|
options
|
||||||
cleanUrls,
|
|
||||||
trailingSlash
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
@@ -1659,13 +1637,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
|||||||
|
|
||||||
const files = ['public/index.html', 'api/[endpoint].js'];
|
const files = ['public/index.html', 'api/[endpoint].js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files, pkg);
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
pkg,
|
||||||
featHandleMiss,
|
options
|
||||||
cleanUrls,
|
|
||||||
trailingSlash
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
@@ -1686,13 +1661,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
|||||||
{
|
{
|
||||||
const files = ['api/date/index.js', 'api/date.js'];
|
const files = ['api/date/index.js', 'api/date.js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
null,
|
||||||
featHandleMiss,
|
options
|
||||||
cleanUrls,
|
|
||||||
trailingSlash
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
@@ -1708,13 +1680,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
|||||||
{
|
{
|
||||||
const files = ['api/date.js', 'api/[date]/index.js'];
|
const files = ['api/date.js', 'api/[date]/index.js'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
null,
|
||||||
featHandleMiss,
|
options
|
||||||
cleanUrls,
|
|
||||||
trailingSlash
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
@@ -1741,13 +1710,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
|||||||
'api/food.ts',
|
'api/food.ts',
|
||||||
'api/ts/gold.ts',
|
'api/ts/gold.ts',
|
||||||
];
|
];
|
||||||
const { builders } = await detectBuilders(files);
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
null,
|
||||||
featHandleMiss,
|
options
|
||||||
cleanUrls,
|
|
||||||
trailingSlash
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
@@ -1761,13 +1727,10 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
|
|||||||
const functions = { 'api/user.php': { runtime: 'now-php@0.0.8' } };
|
const functions = { 'api/user.php': { runtime: 'now-php@0.0.8' } };
|
||||||
const files = ['api/user.php'];
|
const files = ['api/user.php'];
|
||||||
|
|
||||||
const { builders } = await detectBuilders(files, null, { functions });
|
const { defaultRoutes, redirectRoutes } = await detectBuilders(
|
||||||
const { defaultRoutes, redirectRoutes } = await detectRoutes(
|
|
||||||
files,
|
files,
|
||||||
builders!,
|
null,
|
||||||
featHandleMiss,
|
{ functions, ...options }
|
||||||
cleanUrls,
|
|
||||||
trailingSlash
|
|
||||||
);
|
);
|
||||||
testHeaders(redirectRoutes);
|
testHeaders(redirectRoutes);
|
||||||
expect(defaultRoutes).toStrictEqual([
|
expect(defaultRoutes).toStrictEqual([
|
||||||
|
|||||||
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');
|
} = require('../dist');
|
||||||
|
|
||||||
it('should re-create symlinks properly', async () => {
|
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'));
|
const files = await glob('**', path.join(__dirname, 'symlinks'));
|
||||||
assert.equal(Object.keys(files).length, 2);
|
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 () => {
|
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'));
|
const files = await glob('**', path.join(__dirname, 'symlinks'));
|
||||||
assert.equal(Object.keys(files).length, 2);
|
assert.equal(Object.keys(files).length, 2);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@now/cgi",
|
"name": "@now/cgi",
|
||||||
"version": "1.0.1",
|
"version": "1.0.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
"@zeit/best@0.4.3":
|
"@zeit/best@0.4.3":
|
||||||
version "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:
|
dependencies:
|
||||||
arg "1.0.0"
|
arg "1.0.0"
|
||||||
chalk "2.3.1"
|
chalk "2.3.1"
|
||||||
@@ -144,7 +144,7 @@ call-me-maybe@^1.0.1:
|
|||||||
|
|
||||||
chalk@2.3.1:
|
chalk@2.3.1:
|
||||||
version "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:
|
dependencies:
|
||||||
ansi-styles "^3.2.0"
|
ansi-styles "^3.2.0"
|
||||||
escape-string-regexp "^1.0.5"
|
escape-string-regexp "^1.0.5"
|
||||||
@@ -585,8 +585,8 @@ minimatch@^3.0.4:
|
|||||||
brace-expansion "^1.1.7"
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
mixin-deep@^1.2.0:
|
mixin-deep@^1.2.0:
|
||||||
version "1.3.1"
|
version "1.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"
|
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
|
||||||
dependencies:
|
dependencies:
|
||||||
for-in "^1.0.2"
|
for-in "^1.0.2"
|
||||||
is-extendable "^1.0.1"
|
is-extendable "^1.0.1"
|
||||||
@@ -704,7 +704,7 @@ rmfr@2.0.0:
|
|||||||
|
|
||||||
safe-regex@^1.1.0:
|
safe-regex@^1.1.0:
|
||||||
version "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:
|
dependencies:
|
||||||
ret "~0.1.10"
|
ret "~0.1.10"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
[](https://spectrum.chat/zeit)
|
[](https://spectrum.chat/zeit)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "now",
|
"name": "now",
|
||||||
"version": "17.0.0-canary.26",
|
"version": "17.0.4-canary.2",
|
||||||
"preferGlobal": true,
|
"preferGlobal": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"description": "The command-line interface for Now",
|
"description": "The command-line interface for Now",
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"preinstall": "node ./scripts/preinstall.js",
|
"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": "ava test/integration.js --serial --fail-fast",
|
||||||
"test-integration-v1": "ava test/integration-v1.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",
|
"test-integration-now-dev": "ava test/dev/integration.js --serial --fail-fast --verbose",
|
||||||
@@ -63,6 +63,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sentry/node": "5.5.0",
|
"@sentry/node": "5.5.0",
|
||||||
|
"@sindresorhus/slugify": "0.10.0",
|
||||||
"@types/ansi-escapes": "3.0.0",
|
"@types/ansi-escapes": "3.0.0",
|
||||||
"@types/ansi-regex": "4.0.0",
|
"@types/ansi-regex": "4.0.0",
|
||||||
"@types/async-retry": "1.2.1",
|
"@types/async-retry": "1.2.1",
|
||||||
@@ -89,10 +90,10 @@
|
|||||||
"@types/tar-fs": "1.16.1",
|
"@types/tar-fs": "1.16.1",
|
||||||
"@types/text-table": "0.2.0",
|
"@types/text-table": "0.2.0",
|
||||||
"@types/universal-analytics": "0.4.2",
|
"@types/universal-analytics": "0.4.2",
|
||||||
"@types/which": "1.3.1",
|
"@types/which": "1.3.2",
|
||||||
"@types/write-json-file": "2.2.1",
|
"@types/write-json-file": "2.2.1",
|
||||||
"@zeit/dockerignore": "0.0.5",
|
"@zeit/dockerignore": "0.0.5",
|
||||||
"@zeit/fun": "0.11.0",
|
"@zeit/fun": "0.11.2",
|
||||||
"@zeit/ncc": "0.18.5",
|
"@zeit/ncc": "0.18.5",
|
||||||
"@zeit/source-map-support": "0.6.2",
|
"@zeit/source-map-support": "0.6.2",
|
||||||
"ajv": "6.10.2",
|
"ajv": "6.10.2",
|
||||||
@@ -131,7 +132,7 @@
|
|||||||
"http-proxy": "1.17.0",
|
"http-proxy": "1.17.0",
|
||||||
"ignore": "4.0.6",
|
"ignore": "4.0.6",
|
||||||
"ini": "1.3.4",
|
"ini": "1.3.4",
|
||||||
"inquirer": "3.3.0",
|
"inquirer": "7.0.4",
|
||||||
"is-port-reachable": "3.0.0",
|
"is-port-reachable": "3.0.0",
|
||||||
"is-url": "1.2.2",
|
"is-url": "1.2.2",
|
||||||
"jaro-winkler": "0.2.8",
|
"jaro-winkler": "0.2.8",
|
||||||
@@ -174,10 +175,10 @@
|
|||||||
"universal-analytics": "0.4.20",
|
"universal-analytics": "0.4.20",
|
||||||
"update-check": "1.5.3",
|
"update-check": "1.5.3",
|
||||||
"utility-types": "2.1.0",
|
"utility-types": "2.1.0",
|
||||||
"which": "1.3.1",
|
"which": "2.0.2",
|
||||||
"which-promise": "1.0.0",
|
"which-promise": "1.0.0",
|
||||||
"write-json-file": "2.2.0",
|
"write-json-file": "2.2.0",
|
||||||
"xdg-app-paths": "5.1.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 getScope from '../../util/get-scope.ts';
|
||||||
import stamp from '../../util/output/stamp.ts';
|
import stamp from '../../util/output/stamp.ts';
|
||||||
import strlen from '../../util/strlen.ts';
|
import strlen from '../../util/strlen.ts';
|
||||||
import wait from '../../util/output/wait';
|
|
||||||
|
|
||||||
export default async function ls(ctx, opts, args, output) {
|
export default async function ls(ctx, opts, args, output) {
|
||||||
const {
|
const {
|
||||||
authConfig: { token },
|
authConfig: { token },
|
||||||
config
|
config,
|
||||||
} = ctx;
|
} = ctx;
|
||||||
const { currentTeam } = config;
|
const { currentTeam } = config;
|
||||||
const { apiUrl } = ctx;
|
const { apiUrl } = ctx;
|
||||||
@@ -22,7 +21,7 @@ export default async function ls(ctx, opts, args, output) {
|
|||||||
apiUrl,
|
apiUrl,
|
||||||
token,
|
token,
|
||||||
currentTeam,
|
currentTeam,
|
||||||
debug: debugEnabled
|
debug: debugEnabled,
|
||||||
});
|
});
|
||||||
let contextName = null;
|
let contextName = null;
|
||||||
|
|
||||||
@@ -51,7 +50,7 @@ export default async function ls(ctx, opts, args, output) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelWait = wait(
|
cancelWait = output.spinner(
|
||||||
args[0]
|
args[0]
|
||||||
? `Fetching alias details for "${args[0]}" under ${chalk.bold(
|
? `Fetching alias details for "${args[0]}" under ${chalk.bold(
|
||||||
contextName
|
contextName
|
||||||
@@ -112,13 +111,13 @@ function printAliasTable(aliases) {
|
|||||||
? a.deployment.url
|
? a.deployment.url
|
||||||
: chalk.gray('–'),
|
: chalk.gray('–'),
|
||||||
a.alias,
|
a.alias,
|
||||||
ms(Date.now() - new Date(a.created))
|
ms(Date.now() - new Date(a.created)),
|
||||||
])
|
]),
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
align: ['l', 'l', 'r'],
|
align: ['l', 'l', 'r'],
|
||||||
hsep: ' '.repeat(4),
|
hsep: ' '.repeat(4),
|
||||||
stringLength: strlen
|
stringLength: strlen,
|
||||||
}
|
}
|
||||||
).replace(/^/gm, ' ')}\n\n`;
|
).replace(/^/gm, ' ')}\n\n`;
|
||||||
}
|
}
|
||||||
@@ -130,13 +129,13 @@ function printPathAliasTable(rules) {
|
|||||||
rules.map(rule => [
|
rules.map(rule => [
|
||||||
rule.pathname ? rule.pathname : chalk.cyan('[fallthrough]'),
|
rule.pathname ? rule.pathname : chalk.cyan('[fallthrough]'),
|
||||||
rule.method ? rule.method : '*',
|
rule.method ? rule.method : '*',
|
||||||
rule.dest
|
rule.dest,
|
||||||
])
|
])
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
align: ['l', 'l', 'l', 'l'],
|
align: ['l', 'l', 'l', 'l'],
|
||||||
hsep: ' '.repeat(6),
|
hsep: ' '.repeat(6),
|
||||||
stringLength: strlen
|
stringLength: strlen,
|
||||||
}
|
}
|
||||||
).replace(/^(.*)/gm, ' $1')}\n`;
|
).replace(/^(.*)/gm, ' $1')}\n`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default async function({ creditCards, clear = false, contextName }) {
|
|||||||
name: {
|
name: {
|
||||||
label: rightPad('Full Name', 12),
|
label: rightPad('Full Name', 12),
|
||||||
placeholder: 'John Appleseed',
|
placeholder: 'John Appleseed',
|
||||||
validateValue: data => data.trim().length > 0
|
validateValue: data => data.trim().length > 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
cardNumber: {
|
cardNumber: {
|
||||||
@@ -36,7 +36,7 @@ export default async function({ creditCards, clear = false, contextName }) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return ccValidator.isValidCardNumber(data, type);
|
return ccValidator.isValidCardNumber(data, type);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
ccv: {
|
ccv: {
|
||||||
@@ -46,7 +46,7 @@ export default async function({ creditCards, clear = false, contextName }) {
|
|||||||
validateValue: data => {
|
validateValue: data => {
|
||||||
const brand = state.cardNumber.brand.toLowerCase();
|
const brand = state.cardNumber.brand.toLowerCase();
|
||||||
return ccValidator.doesCvvMatchType(data, brand);
|
return ccValidator.doesCvvMatchType(data, brand);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
expDate: {
|
expDate: {
|
||||||
@@ -54,8 +54,8 @@ export default async function({ creditCards, clear = false, contextName }) {
|
|||||||
mask: 'expDate',
|
mask: 'expDate',
|
||||||
placeholder: 'mm / yyyy',
|
placeholder: 'mm / yyyy',
|
||||||
middleware: expDateMiddleware,
|
middleware: expDateMiddleware,
|
||||||
validateValue: data => !ccValidator.isExpired(...data.split(' / '))
|
validateValue: data => !ccValidator.isExpired(...data.split(' / ')),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async function render() {
|
async function render() {
|
||||||
@@ -80,7 +80,7 @@ export default async function({ creditCards, clear = false, contextName }) {
|
|||||||
mask: piece.mask,
|
mask: piece.mask,
|
||||||
validateKeypress: piece.validateKeypress,
|
validateKeypress: piece.validateKeypress,
|
||||||
validateValue: piece.validateValue,
|
validateValue: piece.validateValue,
|
||||||
autoComplete: piece.autoComplete
|
autoComplete: piece.autoComplete,
|
||||||
});
|
});
|
||||||
|
|
||||||
piece.value = result;
|
piece.value = result;
|
||||||
@@ -135,7 +135,7 @@ export default async function({ creditCards, clear = false, contextName }) {
|
|||||||
name: state.name.value,
|
name: state.name.value,
|
||||||
cardNumber: state.cardNumber.value,
|
cardNumber: state.cardNumber.value,
|
||||||
ccv: state.ccv.value,
|
ccv: state.ccv.value,
|
||||||
expDate: state.expDate.value
|
expDate: state.expDate.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
stopSpinner();
|
stopSpinner();
|
||||||
@@ -156,9 +156,9 @@ export default async function({ creditCards, clear = false, contextName }) {
|
|||||||
stopSpinner();
|
stopSpinner();
|
||||||
const linesToClear = state.error ? 15 : 14;
|
const linesToClear = state.error ? 15 : 14;
|
||||||
process.stdout.write(ansiEscapes.eraseLines(linesToClear));
|
process.stdout.write(ansiEscapes.eraseLines(linesToClear));
|
||||||
state.error = `${chalk.red(
|
state.error = `${chalk.red('> Error!')} ${
|
||||||
'> Error!'
|
err.message
|
||||||
)} ${err.message} Please make sure the info is correct`;
|
} Please make sure the info is correct`;
|
||||||
await render();
|
await render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import Now from '../../util';
|
|||||||
import Client from '../../util/client';
|
import Client from '../../util/client';
|
||||||
import getScope from '../../util/get-scope';
|
import getScope from '../../util/get-scope';
|
||||||
import stamp from '../../util/output/stamp';
|
import stamp from '../../util/output/stamp';
|
||||||
import wait from '../../util/output/wait';
|
|
||||||
import createCertFromFile from '../../util/certs/create-cert-from-file';
|
import createCertFromFile from '../../util/certs/create-cert-from-file';
|
||||||
import createCertForCns from '../../util/certs/create-cert-for-cns';
|
import createCertForCns from '../../util/certs/create-cert-for-cns';
|
||||||
import { NowContext } from '../../types';
|
import { NowContext } from '../../types';
|
||||||
@@ -110,7 +109,7 @@ async function add(
|
|||||||
(res, item) => res.concat(item.split(',')),
|
(res, item) => res.concat(item.split(',')),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
const cancelWait = wait(
|
const cancelWait = output.spinner(
|
||||||
`Generating a certificate for ${chalk.bold(cns.join(', '))}`
|
`Generating a certificate for ${chalk.bold(cns.join(', '))}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ export default async ctx => {
|
|||||||
contextName,
|
contextName,
|
||||||
output,
|
output,
|
||||||
stats,
|
stats,
|
||||||
localConfig || {},
|
localConfig,
|
||||||
parts.latestArgs
|
parts.latestArgs
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import ms from 'ms';
|
import ms from 'ms';
|
||||||
import bytes from 'bytes';
|
import bytes from 'bytes';
|
||||||
|
import { join } from 'path';
|
||||||
import { write as copy } from 'clipboardy';
|
import { write as copy } from 'clipboardy';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import title from 'title';
|
import title from 'title';
|
||||||
@@ -47,8 +48,12 @@ import {
|
|||||||
import getProjectName from '../../util/get-project-name';
|
import getProjectName from '../../util/get-project-name';
|
||||||
import selectOrg from '../../util/input/select-org';
|
import selectOrg from '../../util/input/select-org';
|
||||||
import inputProject from '../../util/input/input-project';
|
import inputProject from '../../util/input/input-project';
|
||||||
import validatePaths from '../../util/validate-paths';
|
|
||||||
import { prependEmoji, emoji } from '../../util/emoji';
|
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) => {
|
const addProcessEnv = async (log, env) => {
|
||||||
let val;
|
let val;
|
||||||
@@ -90,7 +95,6 @@ const printDeploymentStatus = async (
|
|||||||
},
|
},
|
||||||
deployStamp,
|
deployStamp,
|
||||||
isClipboardEnabled,
|
isClipboardEnabled,
|
||||||
quiet,
|
|
||||||
isFile
|
isFile
|
||||||
) => {
|
) => {
|
||||||
const isProdDeployment = target === 'production';
|
const isProdDeployment = target === 'production';
|
||||||
@@ -131,22 +135,22 @@ const printDeploymentStatus = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// copy to clipboard
|
// copy to clipboard
|
||||||
|
let isCopiedToClipboard = false;
|
||||||
if (isClipboardEnabled && !isWildcard) {
|
if (isClipboardEnabled && !isWildcard) {
|
||||||
await copy(previewUrl).catch(error =>
|
await copy(previewUrl)
|
||||||
output.debug(`Error copying to clipboard: ${error}`)
|
.then(() => {
|
||||||
);
|
isCopiedToClipboard = true;
|
||||||
}
|
})
|
||||||
|
.catch(error => output.debug(`Error copying to clipboard: ${error}`));
|
||||||
// write to stdout
|
|
||||||
if (quiet) {
|
|
||||||
process.stdout.write(`https://${deploymentUrl}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
output.print(
|
output.print(
|
||||||
prependEmoji(
|
prependEmoji(
|
||||||
`${isProdDeployment ? 'Production' : 'Preview'}: ${chalk.bold(
|
`${isProdDeployment ? 'Production' : 'Preview'}: ${chalk.bold(
|
||||||
previewUrl
|
previewUrl
|
||||||
)} ${deployStamp()}`,
|
)}${
|
||||||
|
isCopiedToClipboard ? chalk.gray(` [copied to clipboard]`) : ''
|
||||||
|
} ${deployStamp()}`,
|
||||||
emoji('success')
|
emoji('success')
|
||||||
) + `\n`
|
) + `\n`
|
||||||
);
|
);
|
||||||
@@ -235,13 +239,6 @@ export default async function main(
|
|||||||
const { isFile, path } = pathValidation;
|
const { isFile, path } = pathValidation;
|
||||||
const autoConfirm = argv['--confirm'] || isFile;
|
const autoConfirm = argv['--confirm'] || isFile;
|
||||||
|
|
||||||
// build `meta`
|
|
||||||
const meta = Object.assign(
|
|
||||||
{},
|
|
||||||
parseMeta(localConfig.meta),
|
|
||||||
parseMeta(argv['--meta'])
|
|
||||||
);
|
|
||||||
|
|
||||||
// --no-scale
|
// --no-scale
|
||||||
if (argv['--no-scale']) {
|
if (argv['--no-scale']) {
|
||||||
warn(`The option --no-scale is only supported on Now 1.0 deployments`);
|
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(
|
output.print(
|
||||||
`${prependEmoji(
|
`${prependEmoji(
|
||||||
`The ${code('name')} property in ${highlight(
|
`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
|
// Merge dotenv config, `env` from now.json, and `--env` / `-e` arguments
|
||||||
const deploymentEnv = Object.assign(
|
const deploymentEnv = Object.assign(
|
||||||
{},
|
{},
|
||||||
@@ -364,77 +486,6 @@ export default async function main(
|
|||||||
target = 'production';
|
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 currentTeam = org.type === 'team' ? org.id : undefined;
|
||||||
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
|
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
|
||||||
let deployStamp = stamp();
|
let deployStamp = stamp();
|
||||||
@@ -462,10 +513,11 @@ export default async function main(
|
|||||||
output,
|
output,
|
||||||
now,
|
now,
|
||||||
contextName,
|
contextName,
|
||||||
[path],
|
[sourcePath],
|
||||||
createArgs,
|
createArgs,
|
||||||
org,
|
org,
|
||||||
!project && !isFile
|
!project && !isFile,
|
||||||
|
path
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -474,6 +526,10 @@ export default async function main(
|
|||||||
) {
|
) {
|
||||||
let { projectSettings, framework } = deployment;
|
let { projectSettings, framework } = deployment;
|
||||||
|
|
||||||
|
if (rootDirectory) {
|
||||||
|
projectSettings.rootDirectory = rootDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
const settings = await editProjectSettings(
|
const settings = await editProjectSettings(
|
||||||
output,
|
output,
|
||||||
projectSettings,
|
projectSettings,
|
||||||
@@ -489,10 +545,11 @@ export default async function main(
|
|||||||
output,
|
output,
|
||||||
now,
|
now,
|
||||||
contextName,
|
contextName,
|
||||||
[path],
|
[sourcePath],
|
||||||
createArgs,
|
createArgs,
|
||||||
org,
|
org,
|
||||||
false
|
false,
|
||||||
|
path
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,6 +558,14 @@ export default async function main(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (deployment instanceof Error) {
|
||||||
|
output.error(
|
||||||
|
`${deployment.message ||
|
||||||
|
'An unexpected error occurred while deploying your project'} (http://zeit.ink/P4)`
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
const deploymentResponse = await getDeploymentByIdOrHost(
|
const deploymentResponse = await getDeploymentByIdOrHost(
|
||||||
now,
|
now,
|
||||||
contextName,
|
contextName,
|
||||||
@@ -616,7 +681,6 @@ export default async function main(
|
|||||||
deployment,
|
deployment,
|
||||||
deployStamp,
|
deployStamp,
|
||||||
!argv['--no-clipboard'],
|
!argv['--no-clipboard'],
|
||||||
quiet,
|
|
||||||
isFile
|
isFile
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -665,6 +729,11 @@ function handleCreateDeployError(output, error) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dataPath === '.name') {
|
||||||
|
output.error(message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (keyword === 'type') {
|
if (keyword === 'type') {
|
||||||
const prop = dataPath.substr(1, dataPath.length);
|
const prop = dataPath.substr(1, dataPath.length);
|
||||||
|
|
||||||
|
|||||||
@@ -852,7 +852,7 @@ async function sync({
|
|||||||
const { url } = now;
|
const { url } = now;
|
||||||
const dcs =
|
const dcs =
|
||||||
deploymentType !== 'static' && deployment.scale
|
deploymentType !== 'static' && deployment.scale
|
||||||
? chalk` ({bold ${Object.keys(deployment.scale).join(', ')})`
|
? ` (${ chalk.bold(Object.keys(deployment.scale).join(', ')) })`
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
if (isTTY) {
|
if (isTTY) {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default async function dev(
|
|||||||
output: Output
|
output: Output
|
||||||
) {
|
) {
|
||||||
const [dir = '.'] = args;
|
const [dir = '.'] = args;
|
||||||
const cwd = path.resolve(dir);
|
let cwd = path.resolve(dir);
|
||||||
const listen = parseListen(opts['--listen'] || '3000');
|
const listen = parseListen(opts['--listen'] || '3000');
|
||||||
const debug = opts['--debug'] || false;
|
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 });
|
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 promptBool from '../../util/input/prompt-bool';
|
||||||
import purchaseDomain from '../../util/domains/purchase-domain';
|
import purchaseDomain from '../../util/domains/purchase-domain';
|
||||||
import stamp from '../../util/output/stamp';
|
import stamp from '../../util/output/stamp';
|
||||||
import wait from '../../util/output/wait';
|
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
'--debug': boolean;
|
'--debug': boolean;
|
||||||
@@ -27,7 +26,7 @@ export default async function buy(
|
|||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
authConfig: { token },
|
authConfig: { token },
|
||||||
config
|
config,
|
||||||
} = ctx;
|
} = ctx;
|
||||||
const { currentTeam } = config;
|
const { currentTeam } = config;
|
||||||
const { apiUrl } = ctx;
|
const { apiUrl } = ctx;
|
||||||
@@ -100,7 +99,7 @@ export default async function buy(
|
|||||||
|
|
||||||
let buyResult;
|
let buyResult;
|
||||||
const purchaseStamp = stamp();
|
const purchaseStamp = stamp();
|
||||||
const stopPurchaseSpinner = wait('Purchasing');
|
const stopPurchaseSpinner = output.spinner('Purchasing');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyResult = await purchaseDomain(client, domainName, price);
|
buyResult = await purchaseDomain(client, domainName, price);
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import listInput from '../../util/input/list';
|
|||||||
import listItem from '../../util/output/list-item';
|
import listItem from '../../util/output/list-item';
|
||||||
import promptBool from '../../util/input/prompt-bool';
|
import promptBool from '../../util/input/prompt-bool';
|
||||||
import toHumanPath from '../../util/humanize-path';
|
import toHumanPath from '../../util/humanize-path';
|
||||||
import wait from '../../util/output/wait';
|
|
||||||
import { Output } from '../../util/output';
|
import { Output } from '../../util/output';
|
||||||
import { NowContext } from '../../types';
|
import { NowContext } from '../../types';
|
||||||
import success from '../../util/output/success';
|
import success from '../../util/output/success';
|
||||||
@@ -24,10 +23,10 @@ type Options = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type Example = {
|
type Example = {
|
||||||
name: string,
|
name: string;
|
||||||
visible: boolean,
|
visible: boolean;
|
||||||
suggestions: string[]
|
suggestions: string[];
|
||||||
}
|
};
|
||||||
|
|
||||||
const EXAMPLE_API = 'https://now-example-files.zeit.sh';
|
const EXAMPLE_API = 'https://now-example-files.zeit.sh';
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@ export default async function init(
|
|||||||
const [name, dir] = args;
|
const [name, dir] = args;
|
||||||
const force = opts['-f'] || opts['--force'];
|
const force = opts['-f'] || opts['--force'];
|
||||||
|
|
||||||
const examples = await fetchExampleList();
|
const examples = await fetchExampleList(output);
|
||||||
|
|
||||||
if (!examples) {
|
if (!examples) {
|
||||||
throw new Error(`Could not fetch example list.`);
|
throw new Error(`Could not fetch example list.`);
|
||||||
@@ -56,22 +55,22 @@ export default async function init(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return extractExample(chosen, dir, force);
|
return extractExample(output, chosen, dir, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exampleList.includes(name)) {
|
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);
|
const oldExample = examples.find(x => !x.visible && x.name === name);
|
||||||
if (oldExample) {
|
if (oldExample) {
|
||||||
return extractExample(name, dir, force, 'v1');
|
return extractExample(output, name, dir, force, 'v1');
|
||||||
}
|
}
|
||||||
|
|
||||||
const found = await guess(exampleList, name);
|
const found = await guess(exampleList, name);
|
||||||
|
|
||||||
if (typeof found === 'string') {
|
if (typeof found === 'string') {
|
||||||
return extractExample(found, dir, force);
|
return extractExample(output, found, dir, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(info('No changes made.'));
|
console.log(info('No changes made.'));
|
||||||
@@ -81,8 +80,8 @@ export default async function init(
|
|||||||
/**
|
/**
|
||||||
* Fetch example list json
|
* Fetch example list json
|
||||||
*/
|
*/
|
||||||
async function fetchExampleList() {
|
async function fetchExampleList(output: Output) {
|
||||||
const stopSpinner = wait('Fetching examples');
|
const stopSpinner = output.spinner('Fetching examples');
|
||||||
const url = `${EXAMPLE_API}/v2/list.json`;
|
const url = `${EXAMPLE_API}/v2/list.json`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -93,7 +92,7 @@ async function fetchExampleList() {
|
|||||||
throw new Error(`Failed fetching list.json (${resp.statusText}).`);
|
throw new Error(`Failed fetching list.json (${resp.statusText}).`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await resp.json() as Example[];
|
return (await resp.json()) as Example[];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
stopSpinner();
|
stopSpinner();
|
||||||
}
|
}
|
||||||
@@ -106,22 +105,28 @@ async function chooseFromDropdown(message: string, exampleList: string[]) {
|
|||||||
const choices = exampleList.map(name => ({
|
const choices = exampleList.map(name => ({
|
||||||
name,
|
name,
|
||||||
value: name,
|
value: name,
|
||||||
short: name
|
short: name,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return listInput({
|
return listInput({
|
||||||
message,
|
message,
|
||||||
separator: false,
|
separator: false,
|
||||||
choices
|
choices,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract example to directory
|
* 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 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`;
|
const url = `${EXAMPLE_API}/${ver}/download/${name}.tar.gz`;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import createOutput from '../util/output';
|
|||||||
import Now from '../util';
|
import Now from '../util';
|
||||||
import logo from '../util/output/logo';
|
import logo from '../util/output/logo';
|
||||||
import elapsed from '../util/output/elapsed.ts';
|
import elapsed from '../util/output/elapsed.ts';
|
||||||
import wait from '../util/output/wait';
|
|
||||||
import { handleError } from '../util/error';
|
import { handleError } from '../util/error';
|
||||||
import strlen from '../util/strlen.ts';
|
import strlen from '../util/strlen.ts';
|
||||||
import Client from '../util/client.ts';
|
import Client from '../util/client.ts';
|
||||||
@@ -79,13 +78,16 @@ export default async function main(ctx) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { authConfig: { token }, config } = ctx;
|
const {
|
||||||
|
authConfig: { token },
|
||||||
|
config,
|
||||||
|
} = ctx;
|
||||||
const { currentTeam } = config;
|
const { currentTeam } = config;
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
apiUrl,
|
apiUrl,
|
||||||
token,
|
token,
|
||||||
currentTeam,
|
currentTeam,
|
||||||
debug: debugEnabled
|
debug: debugEnabled,
|
||||||
});
|
});
|
||||||
let contextName = null;
|
let contextName = null;
|
||||||
|
|
||||||
@@ -104,7 +106,7 @@ export default async function main(ctx) {
|
|||||||
|
|
||||||
// resolve the deployment, since we might have been given an alias
|
// resolve the deployment, since we might have been given an alias
|
||||||
const depFetchStart = Date.now();
|
const depFetchStart = Date.now();
|
||||||
const cancelWait = wait(
|
const cancelWait = output.spinner(
|
||||||
`Fetching deployment "${id}" in ${chalk.bold(contextName)}`
|
`Fetching deployment "${id}" in ${chalk.bold(contextName)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -140,7 +142,7 @@ export default async function main(ctx) {
|
|||||||
limits,
|
limits,
|
||||||
version,
|
version,
|
||||||
routes,
|
routes,
|
||||||
readyState
|
readyState,
|
||||||
} = deployment;
|
} = deployment;
|
||||||
|
|
||||||
const isBuilds = version === 2;
|
const isBuilds = version === 2;
|
||||||
@@ -159,7 +161,7 @@ export default async function main(ctx) {
|
|||||||
)}/events?types=event`
|
)}/events?types=event`
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
isBuilds ? now.fetch(buildsUrl) : { builds: [] }
|
isBuilds ? now.fetch(buildsUrl) : { builds: [] },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
cancelWait();
|
cancelWait();
|
||||||
@@ -174,7 +176,9 @@ export default async function main(ctx) {
|
|||||||
print(` ${chalk.cyan('version')}\t${version}\n`);
|
print(` ${chalk.cyan('version')}\t${version}\n`);
|
||||||
print(` ${chalk.cyan('id')}\t\t${finalId}\n`);
|
print(` ${chalk.cyan('id')}\t\t${finalId}\n`);
|
||||||
print(` ${chalk.cyan('name')}\t${name}\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) {
|
if (!isBuilds) {
|
||||||
print(` ${chalk.cyan('type')}\t${type}\n`);
|
print(` ${chalk.cyan('type')}\t${type}\n`);
|
||||||
}
|
}
|
||||||
@@ -255,7 +259,7 @@ export default async function main(ctx) {
|
|||||||
`${table(t, {
|
`${table(t, {
|
||||||
align: ['l', 'c', 'c', 'c'],
|
align: ['l', 'c', 'c', 'c'],
|
||||||
hsep: ' '.repeat(8),
|
hsep: ' '.repeat(8),
|
||||||
stringLength: strlen
|
stringLength: strlen,
|
||||||
}).replace(/^(.*)/gm, ' $1')}\n`
|
}).replace(/^(.*)/gm, ' $1')}\n`
|
||||||
);
|
);
|
||||||
print('\n');
|
print('\n');
|
||||||
@@ -269,9 +273,9 @@ export default async function main(ctx) {
|
|||||||
events.forEach(data => {
|
events.forEach(data => {
|
||||||
if (!data.event) return; // keepalive
|
if (!data.event) return; // keepalive
|
||||||
print(
|
print(
|
||||||
` ${chalk.gray(
|
` ${chalk.gray(new Date(data.created).toISOString())} ${
|
||||||
new Date(data.created).toISOString()
|
data.event
|
||||||
)} ${data.event} ${getEventMetadata(data)}\n`
|
} ${getEventMetadata(data)}\n`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
print('\n');
|
print('\n');
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import chalk from 'chalk';
|
|||||||
import ua from '../util/ua.ts';
|
import ua from '../util/ua.ts';
|
||||||
import getArgs from '../util/get-args';
|
import getArgs from '../util/get-args';
|
||||||
import error from '../util/output/error';
|
import error from '../util/output/error';
|
||||||
import wait from '../util/output/wait';
|
|
||||||
import highlight from '../util/output/highlight';
|
import highlight from '../util/output/highlight';
|
||||||
import ok from '../util/output/ok';
|
import ok from '../util/output/ok';
|
||||||
import cmd from '../util/output/cmd.ts';
|
import cmd from '../util/output/cmd.ts';
|
||||||
@@ -191,7 +190,7 @@ const login = async ctx => {
|
|||||||
let verificationToken;
|
let verificationToken;
|
||||||
let securityCode;
|
let securityCode;
|
||||||
|
|
||||||
stopSpinner = wait('Sending you an email');
|
stopSpinner = output.spinner('Sending you an email');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await executeLogin(apiUrl, email);
|
const data = await executeLogin(apiUrl, email);
|
||||||
@@ -216,7 +215,7 @@ const login = async ctx => {
|
|||||||
)}.\n`
|
)}.\n`
|
||||||
);
|
);
|
||||||
|
|
||||||
stopSpinner = wait('Waiting for your confirmation');
|
stopSpinner = output.spinner('Waiting for your confirmation');
|
||||||
|
|
||||||
let token;
|
let token;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import logo from '../util/output/logo';
|
|||||||
import elapsed from '../util/output/elapsed.ts';
|
import elapsed from '../util/output/elapsed.ts';
|
||||||
import { maybeURL, normalizeURL, parseInstanceURL } from '../util/url';
|
import { maybeURL, normalizeURL, parseInstanceURL } from '../util/url';
|
||||||
import printEvents from '../util/events';
|
import printEvents from '../util/events';
|
||||||
import wait from '../util/output/wait';
|
|
||||||
import Client from '../util/client.ts';
|
import Client from '../util/client.ts';
|
||||||
import getScope from '../util/get-scope.ts';
|
import getScope from '../util/get-scope.ts';
|
||||||
|
|
||||||
@@ -165,7 +164,7 @@ export default async function main(ctx) {
|
|||||||
const id = deploymentIdOrURL;
|
const id = deploymentIdOrURL;
|
||||||
|
|
||||||
const depFetchStart = Date.now();
|
const depFetchStart = Date.now();
|
||||||
const cancelWait = wait(
|
const cancelWait = output.spinner(
|
||||||
`Fetching deployment "${id}" in ${chalk.bold(contextName)}`
|
`Fetching deployment "${id}" in ${chalk.bold(contextName)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -287,12 +286,12 @@ function printLogShort(log) {
|
|||||||
data = JSON.stringify(obj, null, 2);
|
data = JSON.stringify(obj, null, 2);
|
||||||
} else {
|
} else {
|
||||||
data = (log.text || '')
|
data = (log.text || '')
|
||||||
.replace(/\n$/, '')
|
.replace(/\n$/, '')
|
||||||
.replace(/^\n/, '')
|
.replace(/^\n/, '')
|
||||||
// eslint-disable-next-line no-control-regex
|
// eslint-disable-next-line no-control-regex
|
||||||
.replace(/\x1b\[1000D/g, '')
|
.replace(/\x1b\[1000D/g, '')
|
||||||
.replace(/\x1b\[0K/g, '')
|
.replace(/\x1b\[0K/g, '')
|
||||||
.replace(/\x1b\[1A/g, '');
|
.replace(/\x1b\[1A/g, '');
|
||||||
if (/warning/i.test(data)) {
|
if (/warning/i.test(data)) {
|
||||||
data = chalk.yellow(data);
|
data = chalk.yellow(data);
|
||||||
} else if (log.type === 'stderr') {
|
} else if (log.type === 'stderr') {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import table from 'text-table';
|
|||||||
import Now from '../util';
|
import Now from '../util';
|
||||||
import getAliases from '../util/alias/get-aliases';
|
import getAliases from '../util/alias/get-aliases';
|
||||||
import createOutput from '../util/output';
|
import createOutput from '../util/output';
|
||||||
import wait from '../util/output/wait';
|
|
||||||
import logo from '../util/output/logo';
|
import logo from '../util/output/logo';
|
||||||
import cmd from '../util/output/cmd.ts';
|
import cmd from '../util/output/cmd.ts';
|
||||||
import elapsed from '../util/output/elapsed.ts';
|
import elapsed from '../util/output/elapsed.ts';
|
||||||
@@ -132,7 +131,7 @@ export default async function main(ctx) {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cancelWait = wait(
|
const cancelWait = output.spinner(
|
||||||
`Fetching deployment(s) ${ids
|
`Fetching deployment(s) ${ids
|
||||||
.map(id => `"${id}"`)
|
.map(id => `"${id}"`)
|
||||||
.join(' ')} in ${chalk.bold(contextName)}`
|
.join(' ')} in ${chalk.bold(contextName)}`
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import logo from '../util/output/logo';
|
|||||||
import Client from '../util/client.ts';
|
import Client from '../util/client.ts';
|
||||||
import getScope from '../util/get-scope.ts';
|
import getScope from '../util/get-scope.ts';
|
||||||
import createOutput from '../util/output';
|
import createOutput from '../util/output';
|
||||||
|
import confirm from '../util/input/confirm';
|
||||||
|
|
||||||
const help = () => {
|
const help = () => {
|
||||||
console.log(`
|
console.log(`
|
||||||
@@ -144,7 +145,7 @@ async function run({ output, token, contextName, currentTeam }) {
|
|||||||
const elapsed = ms(new Date() - start);
|
const elapsed = ms(new Date() - start);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`> ${plural('secret', list.length, true)} found under ${chalk.bold(
|
`${plural('secret', list.length, true)} found under ${chalk.bold(
|
||||||
contextName
|
contextName
|
||||||
)} ${chalk.gray(`[${elapsed}]`)}`
|
)} ${chalk.gray(`[${elapsed}]`)}`
|
||||||
);
|
);
|
||||||
@@ -189,22 +190,29 @@ async function run({ output, token, contextName, currentTeam }) {
|
|||||||
const theSecret = list.find(secret => secret.name === args[0]);
|
const theSecret = list.find(secret => secret.name === args[0]);
|
||||||
|
|
||||||
if (theSecret) {
|
if (theSecret) {
|
||||||
const yes = argv.yes || (await readConfirmation(theSecret));
|
const yes =
|
||||||
|
argv.yes || (await readConfirmation(output, theSecret, contextName));
|
||||||
if (!yes) {
|
if (!yes) {
|
||||||
console.error(error('User abort'));
|
output.print(`Aborted. Secret not deleted.\n`);
|
||||||
return exit(0);
|
return exit(0);
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
return exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const secret = await secrets.rm(args[0]);
|
const secret = await secrets.rm(args[0]);
|
||||||
const elapsed = ms(new Date() - start);
|
const elapsed = ms(new Date() - start);
|
||||||
console.log(
|
console.log(
|
||||||
`${chalk.cyan('> Success!')} Secret ${chalk.bold(
|
`${chalk.cyan('Success!')} Secret ${chalk.bold(
|
||||||
secret.name
|
secret.name
|
||||||
)} removed ${chalk.gray(`[${elapsed}]`)}`
|
)} under ${chalk.bold(contextName)} removed ${chalk.gray(`[${elapsed}]`)}`
|
||||||
);
|
);
|
||||||
return secrets.close();
|
return secrets.close();
|
||||||
}
|
}
|
||||||
@@ -223,9 +231,11 @@ async function run({ output, token, contextName, currentTeam }) {
|
|||||||
const secret = await secrets.rename(args[0], args[1]);
|
const secret = await secrets.rename(args[0], args[1]);
|
||||||
const elapsed = ms(new Date() - start);
|
const elapsed = ms(new Date() - start);
|
||||||
console.log(
|
console.log(
|
||||||
`${chalk.cyan('> Success!')} Secret ${chalk.bold(
|
`${chalk.cyan('Success!')} Secret ${chalk.bold(
|
||||||
secret.oldName
|
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();
|
return secrets.close();
|
||||||
}
|
}
|
||||||
@@ -243,7 +253,7 @@ async function run({ output, token, contextName, currentTeam }) {
|
|||||||
if (args.length > 2) {
|
if (args.length > 2) {
|
||||||
const example = chalk.cyan(`$ now secret add -- "${args[0]}"`);
|
const example = chalk.cyan(`$ now secret add -- "${args[0]}"`);
|
||||||
console.log(
|
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(
|
console.log(
|
||||||
`${chalk.cyan('> Success!')} Secret ${chalk.bold(
|
`${chalk.cyan('Success!')} Secret ${chalk.bold(
|
||||||
name.toLowerCase()
|
name.toLowerCase()
|
||||||
)} added (${chalk.bold(contextName)}) ${chalk.gray(`[${elapsed}]`)}`
|
)} added under ${chalk.bold(contextName)} ${chalk.gray(`[${elapsed}]`)}`
|
||||||
);
|
);
|
||||||
return secrets.close();
|
return secrets.close();
|
||||||
}
|
}
|
||||||
@@ -278,33 +288,19 @@ process.on('uncaughtException', err => {
|
|||||||
exit(1);
|
exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
function readConfirmation(secret) {
|
async function readConfirmation(output, secret, contextName) {
|
||||||
return new Promise(resolve => {
|
const time = chalk.gray(`${ms(new Date() - new Date(secret.created))} ago`);
|
||||||
const time = chalk.gray(`${ms(new Date() - new Date(secret.created))} ago`);
|
const tbl = table([[chalk.bold(secret.name), time]], {
|
||||||
const tbl = table([[chalk.bold(secret.name), time]], {
|
align: ['r', 'l'],
|
||||||
align: ['r', 'l'],
|
hsep: ' '.repeat(6),
|
||||||
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();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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,
|
validateKeypress: validateSlugKeypress,
|
||||||
initialValue: slug,
|
initialValue: slug,
|
||||||
valid: team,
|
valid: team,
|
||||||
forceLowerCase: true
|
forceLowerCase: true,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.message === 'USER_ABORT') {
|
if (err.message === 'USER_ABORT') {
|
||||||
@@ -95,7 +95,7 @@ export default async function({ apiUrl, token, teams, config }) {
|
|||||||
try {
|
try {
|
||||||
name = await textInput({
|
name = await textInput({
|
||||||
label: `- ${teamNamePrefix}`,
|
label: `- ${teamNamePrefix}`,
|
||||||
validateKeypress: validateNameKeypress
|
validateKeypress: validateNameKeypress,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.message === 'USER_ABORT') {
|
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',
|
introMsg: 'Invite your teammates! When done, press enter on an empty field',
|
||||||
noopMsg: `You can invite teammates later by running ${cmd(
|
noopMsg: `You can invite teammates later by running ${cmd(
|
||||||
'now teams invite'
|
'now teams invite'
|
||||||
)}`
|
)}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
gracefulExit();
|
gracefulExit();
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const domains = Array.from(
|
|||||||
'inbox.com',
|
'inbox.com',
|
||||||
'mail.com',
|
'mail.com',
|
||||||
'gmx.com',
|
'gmx.com',
|
||||||
'icloud.com'
|
'icloud.com',
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -56,17 +56,15 @@ const emailAutoComplete = (value, teamSlug) => {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function(
|
export default async function({
|
||||||
{
|
teams,
|
||||||
teams,
|
args,
|
||||||
args,
|
config,
|
||||||
config,
|
introMsg,
|
||||||
introMsg,
|
noopMsg = 'No changes made',
|
||||||
noopMsg = 'No changes made',
|
apiUrl,
|
||||||
apiUrl,
|
token,
|
||||||
token
|
} = {}) {
|
||||||
} = {}
|
|
||||||
) {
|
|
||||||
const { currentTeam: currentTeamId } = config;
|
const { currentTeam: currentTeamId } = config;
|
||||||
|
|
||||||
const stopSpinner = wait('Fetching teams');
|
const stopSpinner = wait('Fetching teams');
|
||||||
@@ -86,7 +84,11 @@ export default async function(
|
|||||||
|
|
||||||
if (!currentTeam) {
|
if (!currentTeam) {
|
||||||
// We specifically need a team scope here
|
// 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);
|
return fatalError(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +109,9 @@ export default async function(
|
|||||||
userInfo = res.name || res.username;
|
userInfo = res.name || res.username;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code === 'user_not_found') {
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +119,11 @@ export default async function(
|
|||||||
}
|
}
|
||||||
|
|
||||||
stopSpinner();
|
stopSpinner();
|
||||||
console.log(`${chalk.cyan(chars.tick)} ${email}${userInfo ? ` (${userInfo})` : ''} ${elapsed()}`);
|
console.log(
|
||||||
|
`${chalk.cyan(chars.tick)} ${email}${
|
||||||
|
userInfo ? ` (${userInfo})` : ''
|
||||||
|
} ${elapsed()}`
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
console.log(`${chalk.red(`✖ ${email}`)} ${chalk.gray('[invalid]')}`);
|
console.log(`${chalk.red(`✖ ${email}`)} ${chalk.gray('[invalid]')}`);
|
||||||
}
|
}
|
||||||
@@ -135,7 +143,7 @@ export default async function(
|
|||||||
email = await textInput({
|
email = await textInput({
|
||||||
label: `- ${inviteUserPrefix}`,
|
label: `- ${inviteUserPrefix}`,
|
||||||
validateValue: validateEmail,
|
validateValue: validateEmail,
|
||||||
autoComplete: value => emailAutoComplete(value, currentTeam.slug)
|
autoComplete: value => emailAutoComplete(value, currentTeam.slug),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.message !== 'USER_ABORT') {
|
if (err.message !== 'USER_ABORT') {
|
||||||
@@ -149,7 +157,10 @@ export default async function(
|
|||||||
stopSpinner = wait(inviteUserPrefix + email);
|
stopSpinner = wait(inviteUserPrefix + email);
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// 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();
|
stopSpinner();
|
||||||
const userInfo = name || username;
|
const userInfo = name || username;
|
||||||
email = `${email}${userInfo ? ` (${userInfo})` : ''} ${elapsed()}`;
|
email = `${email}${userInfo ? ` (${userInfo})` : ''} ${elapsed()}`;
|
||||||
|
|||||||
@@ -27,20 +27,20 @@ export default async function({ teams, config, apiUrl, token }) {
|
|||||||
|
|
||||||
if (accountIsCurrent) {
|
if (accountIsCurrent) {
|
||||||
currentTeam = {
|
currentTeam = {
|
||||||
slug: user.username || user.email
|
slug: user.username || user.email,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const teamList = list.map(({ slug, name }) => ({
|
const teamList = list.map(({ slug, name }) => ({
|
||||||
name,
|
name,
|
||||||
value: slug,
|
value: slug,
|
||||||
current: slug === currentTeam.slug ? chars.tick : ''
|
current: slug === currentTeam.slug ? chars.tick : '',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
teamList.unshift({
|
teamList.unshift({
|
||||||
name: user.email,
|
name: user.email,
|
||||||
value: user.username || 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
|
// Let's bring the current team to the beginning of the list
|
||||||
|
|||||||
@@ -224,6 +224,7 @@ export interface Project {
|
|||||||
createdAt: number;
|
createdAt: number;
|
||||||
devCommand?: string | null;
|
devCommand?: string | null;
|
||||||
framework?: string | null;
|
framework?: string | null;
|
||||||
|
rootDirectory?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Org {
|
export interface Org {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { Output } from '../output';
|
|||||||
import * as ERRORS from '../errors-ts';
|
import * as ERRORS from '../errors-ts';
|
||||||
import Client from '../client';
|
import Client from '../client';
|
||||||
import createCertForAlias from '../certs/create-cert-for-alias';
|
import createCertForAlias from '../certs/create-cert-for-alias';
|
||||||
import wait from '../output/wait';
|
|
||||||
|
|
||||||
export type AliasRecord = {
|
export type AliasRecord = {
|
||||||
uid: string;
|
uid: string;
|
||||||
@@ -20,7 +19,7 @@ export default async function createAlias(
|
|||||||
alias: string,
|
alias: string,
|
||||||
externalDomain: boolean
|
externalDomain: boolean
|
||||||
) {
|
) {
|
||||||
let cancelMessage = wait(`Creating alias`);
|
let cancelMessage = output.spinner(`Creating alias`);
|
||||||
const result = await performCreateAlias(
|
const result = await performCreateAlias(
|
||||||
client,
|
client,
|
||||||
contextName,
|
contextName,
|
||||||
@@ -41,7 +40,7 @@ export default async function createAlias(
|
|||||||
return cert;
|
return cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cancelMessage = wait(`Creating alias`);
|
let cancelMessage = output.spinner(`Creating alias`);
|
||||||
const secondTry = await performCreateAlias(
|
const secondTry = await performCreateAlias(
|
||||||
client,
|
client,
|
||||||
contextName,
|
contextName,
|
||||||
@@ -66,7 +65,7 @@ async function performCreateAlias(
|
|||||||
`/now/deployments/${deployment.uid}/aliases`,
|
`/now/deployments/${deployment.uid}/aliases`,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: { alias }
|
body: { alias },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -77,7 +76,10 @@ async function performCreateAlias(
|
|||||||
return { uid: error.uid, alias: error.alias } as AliasRecord;
|
return { uid: error.uid, alias: error.alias } as AliasRecord;
|
||||||
}
|
}
|
||||||
if (error.code === 'deployment_not_found') {
|
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') {
|
if (error.code === 'gone') {
|
||||||
return new ERRORS.DeploymentFailedAliasImpossible();
|
return new ERRORS.DeploymentFailedAliasImpossible();
|
||||||
@@ -94,7 +96,7 @@ async function performCreateAlias(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (error.status === 400) {
|
if (error.status === 400) {
|
||||||
return new ERRORS.DeploymentNotReady({url: deployment.url })
|
return new ERRORS.DeploymentNotReady({ url: deployment.url });
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import chalk from 'chalk';
|
|||||||
import getAppLastDeployment from '../deploy/get-app-last-deployment';
|
import getAppLastDeployment from '../deploy/get-app-last-deployment';
|
||||||
import getAppName from '../deploy/get-app-name';
|
import getAppName from '../deploy/get-app-name';
|
||||||
import fetchDeploymentByIdOrHost from '../deploy/get-deployment-by-id-or-host';
|
import fetchDeploymentByIdOrHost from '../deploy/get-deployment-by-id-or-host';
|
||||||
import wait from '../output/wait';
|
|
||||||
import Client from '../client';
|
import Client from '../client';
|
||||||
import { Output } from '../output';
|
import { Output } from '../output';
|
||||||
import { User, Config } from '../../types';
|
import { User, Config } from '../../types';
|
||||||
@@ -17,7 +16,7 @@ export default async function getDeploymentForAlias(
|
|||||||
contextName: string,
|
contextName: string,
|
||||||
localConfig: Config
|
localConfig: Config
|
||||||
) {
|
) {
|
||||||
const cancelWait = wait(
|
const cancelWait = output.spinner(
|
||||||
`Fetching deployment to alias in ${chalk.bold(contextName)}`
|
`Fetching deployment to alias in ${chalk.bold(contextName)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import * as ERRORS from '../errors-ts';
|
|||||||
import Client from '../client';
|
import Client from '../client';
|
||||||
import createCertForAlias from '../certs/create-cert-for-alias';
|
import createCertForAlias from '../certs/create-cert-for-alias';
|
||||||
import setupDomain from '../domains/setup-domain';
|
import setupDomain from '../domains/setup-domain';
|
||||||
import wait from '../output/wait';
|
|
||||||
|
|
||||||
const NOW_SH_REGEX = /\.now\.sh$/;
|
const NOW_SH_REGEX = /\.now\.sh$/;
|
||||||
|
|
||||||
@@ -34,6 +33,7 @@ export default async function upsertPathAlias(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = await performUpsertPathAlias(
|
const result = await performUpsertPathAlias(
|
||||||
|
output,
|
||||||
client,
|
client,
|
||||||
alias,
|
alias,
|
||||||
rules,
|
rules,
|
||||||
@@ -51,23 +51,26 @@ export default async function upsertPathAlias(
|
|||||||
return cert;
|
return cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
return performUpsertPathAlias(client, alias, rules, contextName);
|
return performUpsertPathAlias(output, client, alias, rules, contextName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function performUpsertPathAlias(
|
async function performUpsertPathAlias(
|
||||||
|
output: Output,
|
||||||
client: Client,
|
client: Client,
|
||||||
alias: string,
|
alias: string,
|
||||||
rules: PathRule[],
|
rules: PathRule[],
|
||||||
contextName: string
|
contextName: string
|
||||||
) {
|
) {
|
||||||
const cancelMessage = wait(`Updating path alias rules for ${alias}`);
|
const cancelMessage = output.spinner(
|
||||||
|
`Updating path alias rules for ${alias}`
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
const record = await client.fetch<AliasRecord>(`/now/aliases`, {
|
const record = await client.fetch<AliasRecord>(`/now/aliases`, {
|
||||||
body: { alias, rules },
|
body: { alias, rules },
|
||||||
method: 'POST'
|
method: 'POST',
|
||||||
});
|
});
|
||||||
cancelMessage();
|
cancelMessage();
|
||||||
return record;
|
return record;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import createCertForCns from './create-cert-for-cns';
|
|||||||
import getWildcardCnsForAlias from './get-wildcard-cns-for-alias';
|
import getWildcardCnsForAlias from './get-wildcard-cns-for-alias';
|
||||||
import joinWords from '../output/join-words';
|
import joinWords from '../output/join-words';
|
||||||
import stamp from '../output/stamp';
|
import stamp from '../output/stamp';
|
||||||
import wait from '../output/wait';
|
|
||||||
|
|
||||||
export default async function createCertificateForAlias(
|
export default async function createCertificateForAlias(
|
||||||
output: Output,
|
output: Output,
|
||||||
@@ -15,7 +14,7 @@ export default async function createCertificateForAlias(
|
|||||||
shouldBeWildcard: boolean
|
shouldBeWildcard: boolean
|
||||||
) {
|
) {
|
||||||
const cns = shouldBeWildcard ? getWildcardCnsForAlias(alias) : [alias];
|
const cns = shouldBeWildcard ? getWildcardCnsForAlias(alias) : [alias];
|
||||||
const cancelMessage = wait(`Generating a certificate...`);
|
const cancelMessage = output.spinner(`Generating a certificate...`);
|
||||||
const certStamp = stamp();
|
const certStamp = stamp();
|
||||||
const cert = await createCertForCns(client, cns, context);
|
const cert = await createCertForCns(client, cns, context);
|
||||||
if (cert instanceof NowError) {
|
if (cert instanceof NowError) {
|
||||||
@@ -25,9 +24,9 @@ export default async function createCertificateForAlias(
|
|||||||
|
|
||||||
cancelMessage();
|
cancelMessage();
|
||||||
output.log(
|
output.log(
|
||||||
`Certificate for ${joinWords(
|
`Certificate for ${joinWords(cert.cns)} (${
|
||||||
cert.cns
|
cert.uid
|
||||||
)} (${cert.uid}) created ${certStamp()}`
|
}) created ${certStamp()}`
|
||||||
);
|
);
|
||||||
return cert;
|
return cert;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,11 @@ export default async function createDeploy(
|
|||||||
paths,
|
paths,
|
||||||
createArgs,
|
createArgs,
|
||||||
org,
|
org,
|
||||||
isSettingUpProject
|
isSettingUpProject,
|
||||||
|
cwd
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
return await now.create(paths, createArgs, org, isSettingUpProject);
|
return await now.create(paths, createArgs, org, isSettingUpProject, cwd);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === 'rate_limited') {
|
if (error.code === 'rate_limited') {
|
||||||
throw new ERRORS_TS.DeploymentsRateLimited(error.message);
|
throw new ERRORS_TS.DeploymentsRateLimited(error.message);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { Output } from '../output';
|
|||||||
import Client from '../client';
|
import Client from '../client';
|
||||||
import createCertForCns from '../certs/create-cert-for-cns';
|
import createCertForCns from '../certs/create-cert-for-cns';
|
||||||
import setupDomain from '../domains/setup-domain';
|
import setupDomain from '../domains/setup-domain';
|
||||||
import wait from '../output/wait';
|
|
||||||
import { InvalidDomain } from '../errors-ts';
|
import { InvalidDomain } from '../errors-ts';
|
||||||
|
|
||||||
export default async function generateCertForDeploy(
|
export default async function generateCertForDeploy(
|
||||||
@@ -23,7 +22,9 @@ export default async function generateCertForDeploy(
|
|||||||
return new InvalidDomain(deployURL);
|
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);
|
const result = await setupDomain(output, client, domain, contextName);
|
||||||
cancelSetupWait();
|
cancelSetupWait();
|
||||||
if (result instanceof NowError) {
|
if (result instanceof NowError) {
|
||||||
@@ -31,7 +32,7 @@ export default async function generateCertForDeploy(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate the certificate with the given parameters
|
// Generate the certificate with the given parameters
|
||||||
const cancelCertWait = wait(
|
const cancelCertWait = output.spinner(
|
||||||
`Generating a wildcard certificate for ${domain}`
|
`Generating a wildcard certificate for ${domain}`
|
||||||
);
|
);
|
||||||
const cert = await createCertForCns(
|
const cert = await createCertForCns(
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
DeploymentOptions,
|
DeploymentOptions,
|
||||||
NowClientOptions,
|
NowClientOptions,
|
||||||
} from 'now-client';
|
} from 'now-client';
|
||||||
import wait from '../output/wait';
|
|
||||||
import { Output } from '../output';
|
import { Output } from '../output';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import Now from '../../util';
|
import Now from '../../util';
|
||||||
@@ -50,6 +49,7 @@ function printInspectUrl(
|
|||||||
export default async function processDeployment({
|
export default async function processDeployment({
|
||||||
isLegacy,
|
isLegacy,
|
||||||
org,
|
org,
|
||||||
|
cwd,
|
||||||
projectName,
|
projectName,
|
||||||
isSettingUpProject,
|
isSettingUpProject,
|
||||||
skipAutoDetectionConfirmation,
|
skipAutoDetectionConfirmation,
|
||||||
@@ -70,6 +70,7 @@ export default async function processDeployment({
|
|||||||
projectName: string;
|
projectName: string;
|
||||||
isSettingUpProject: boolean;
|
isSettingUpProject: boolean;
|
||||||
skipAutoDetectionConfirmation?: boolean;
|
skipAutoDetectionConfirmation?: boolean;
|
||||||
|
cwd?: string;
|
||||||
}) {
|
}) {
|
||||||
if (isLegacy) return processLegacyDeployment(args);
|
if (isLegacy) return processLegacyDeployment(args);
|
||||||
|
|
||||||
@@ -82,6 +83,7 @@ export default async function processDeployment({
|
|||||||
deployStamp,
|
deployStamp,
|
||||||
force,
|
force,
|
||||||
nowConfig,
|
nowConfig,
|
||||||
|
quiet,
|
||||||
} = args;
|
} = args;
|
||||||
|
|
||||||
const { debug } = output;
|
const { debug } = output;
|
||||||
@@ -104,7 +106,7 @@ export default async function processDeployment({
|
|||||||
let buildSpinner = null;
|
let buildSpinner = null;
|
||||||
let deploySpinner = null;
|
let deploySpinner = null;
|
||||||
|
|
||||||
let deployingSpinner = wait(
|
let deployingSpinner = output.spinner(
|
||||||
isSettingUpProject
|
isSettingUpProject
|
||||||
? `Setting up project`
|
? `Setting up project`
|
||||||
: `Deploying ${chalk.bold(`${org.slug}/${projectName}`)}`,
|
: `Deploying ${chalk.bold(`${org.slug}/${projectName}`)}`,
|
||||||
@@ -167,7 +169,7 @@ export default async function processDeployment({
|
|||||||
|
|
||||||
await linkFolderToProject(
|
await linkFolderToProject(
|
||||||
output,
|
output,
|
||||||
paths[0],
|
cwd || paths[0],
|
||||||
{
|
{
|
||||||
orgId: org.id,
|
orgId: org.id,
|
||||||
projectId: event.payload.projectId,
|
projectId: event.payload.projectId,
|
||||||
@@ -178,11 +180,15 @@ export default async function processDeployment({
|
|||||||
|
|
||||||
printInspectUrl(output, event.payload.url, deployStamp, org.slug);
|
printInspectUrl(output, event.payload.url, deployStamp, org.slug);
|
||||||
|
|
||||||
|
if (quiet) {
|
||||||
|
process.stdout.write(`https://${event.payload.url}`);
|
||||||
|
}
|
||||||
|
|
||||||
if (queuedSpinner === null) {
|
if (queuedSpinner === null) {
|
||||||
queuedSpinner =
|
queuedSpinner =
|
||||||
event.payload.readyState === 'QUEUED'
|
event.payload.readyState === 'QUEUED'
|
||||||
? wait('Queued', 0)
|
? output.spinner('Queued', 0)
|
||||||
: wait('Building', 0);
|
: output.spinner('Building', 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +198,7 @@ export default async function processDeployment({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (buildSpinner === null) {
|
if (buildSpinner === null) {
|
||||||
buildSpinner = wait('Building', 0);
|
buildSpinner = output.spinner('Building', 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +210,7 @@ export default async function processDeployment({
|
|||||||
buildSpinner();
|
buildSpinner();
|
||||||
}
|
}
|
||||||
|
|
||||||
deploySpinner = wait('Completing', 0);
|
deploySpinner = output.spinner('Completing', 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle error events
|
// Handle error events
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import {
|
|||||||
import pkg from '../../../package.json';
|
import pkg from '../../../package.json';
|
||||||
|
|
||||||
import { NoBuilderCacheError } from '../errors-ts';
|
import { NoBuilderCacheError } from '../errors-ts';
|
||||||
import wait from '../output/wait';
|
|
||||||
import { Output } from '../output';
|
import { Output } from '../output';
|
||||||
import { getDistTag } from '../get-dist-tag';
|
import { getDistTag } from '../get-dist-tag';
|
||||||
|
|
||||||
@@ -246,7 +245,7 @@ export async function installBuilders(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stopSpinner = wait(
|
const stopSpinner = output.spinner(
|
||||||
`Installing ${pluralize(
|
`Installing ${pluralize(
|
||||||
'Runtime',
|
'Runtime',
|
||||||
packagesToInstall.length
|
packagesToInstall.length
|
||||||
|
|||||||
@@ -3,19 +3,10 @@
|
|||||||
import ms from 'ms';
|
import ms from 'ms';
|
||||||
import bytes from 'bytes';
|
import bytes from 'bytes';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import { delimiter, dirname, extname, join } from 'path';
|
import { delimiter, dirname, join } from 'path';
|
||||||
import { fork, ChildProcess } from 'child_process';
|
import { fork, ChildProcess } from 'child_process';
|
||||||
import { createFunction } from '@zeit/fun';
|
import { createFunction } from '@zeit/fun';
|
||||||
import {
|
import { Builder, File, Lambda, FileBlob, FileFsRef } from '@now/build-utils';
|
||||||
Builder,
|
|
||||||
File,
|
|
||||||
Lambda,
|
|
||||||
FileBlob,
|
|
||||||
FileFsRef,
|
|
||||||
detectApiDirectory,
|
|
||||||
detectApiExtensions,
|
|
||||||
} from '@now/build-utils';
|
|
||||||
import which from 'which';
|
|
||||||
import plural from 'pluralize';
|
import plural from 'pluralize';
|
||||||
import minimatch from 'minimatch';
|
import minimatch from 'minimatch';
|
||||||
import _treeKill from 'tree-kill';
|
import _treeKill from 'tree-kill';
|
||||||
@@ -52,12 +43,6 @@ interface BuildMessageResult extends BuildMessage {
|
|||||||
|
|
||||||
const treeKill = promisify(_treeKill);
|
const treeKill = promisify(_treeKill);
|
||||||
|
|
||||||
let nodeBinPromise: Promise<string>;
|
|
||||||
|
|
||||||
async function getNodeBin(): Promise<string> {
|
|
||||||
return which.sync('node', { nothrow: true }) || process.execPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createBuildProcess(
|
async function createBuildProcess(
|
||||||
match: BuildMatch,
|
match: BuildMatch,
|
||||||
buildEnv: EnvConfig,
|
buildEnv: EnvConfig,
|
||||||
@@ -65,13 +50,8 @@ async function createBuildProcess(
|
|||||||
output: Output,
|
output: Output,
|
||||||
yarnPath?: string
|
yarnPath?: string
|
||||||
): Promise<ChildProcess> {
|
): Promise<ChildProcess> {
|
||||||
if (!nodeBinPromise) {
|
const { execPath } = process;
|
||||||
nodeBinPromise = getNodeBin();
|
const modulePath = await builderModulePathPromise;
|
||||||
}
|
|
||||||
const [execPath, modulePath] = await Promise.all([
|
|
||||||
nodeBinPromise,
|
|
||||||
builderModulePathPromise,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Ensure that `node` is in the builder's `PATH`
|
// Ensure that `node` is in the builder's `PATH`
|
||||||
let PATH = `${dirname(execPath)}${delimiter}${process.env.PATH}`;
|
let PATH = `${dirname(execPath)}${delimiter}${process.env.PATH}`;
|
||||||
@@ -128,7 +108,7 @@ export async function executeBuild(
|
|||||||
const {
|
const {
|
||||||
builderWithPkg: { runInProcess, builder, package: pkg },
|
builderWithPkg: { runInProcess, builder, package: pkg },
|
||||||
} = match;
|
} = match;
|
||||||
const { src: entrypoint } = match;
|
const { entrypoint } = match;
|
||||||
const { env, debug, buildEnv, yarnPath, cwd: workPath } = devServer;
|
const { env, debug, buildEnv, yarnPath, cwd: workPath } = devServer;
|
||||||
|
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
@@ -282,8 +262,8 @@ export async function executeBuild(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { output } = result;
|
const { output } = result;
|
||||||
|
|
||||||
const { cleanUrls } = nowConfig;
|
const { cleanUrls } = nowConfig;
|
||||||
|
|
||||||
// Mimic fmeta-util and perform file renaming
|
// Mimic fmeta-util and perform file renaming
|
||||||
Object.entries(output).forEach(([path, value]) => {
|
Object.entries(output).forEach(([path, value]) => {
|
||||||
if (cleanUrls && path.endsWith('.html')) {
|
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];
|
delete output[path];
|
||||||
output[path] = value;
|
output[path] = value;
|
||||||
});
|
});
|
||||||
@@ -403,6 +388,7 @@ export async function getBuildMatches(
|
|||||||
cwd: string,
|
cwd: string,
|
||||||
yarnDir: string,
|
yarnDir: string,
|
||||||
output: Output,
|
output: Output,
|
||||||
|
devServer: DevServer,
|
||||||
fileList: string[]
|
fileList: string[]
|
||||||
): Promise<BuildMatch[]> {
|
): Promise<BuildMatch[]> {
|
||||||
const matches: BuildMatch[] = [];
|
const matches: BuildMatch[] = [];
|
||||||
@@ -415,9 +401,6 @@ export async function getBuildMatches(
|
|||||||
|
|
||||||
const noMatches: Builder[] = [];
|
const noMatches: Builder[] = [];
|
||||||
const builds = nowConfig.builds || [{ src: '**', use: '@now/static' }];
|
const builds = nowConfig.builds || [{ src: '**', use: '@now/static' }];
|
||||||
const apiDir = detectApiDirectory(builds || []);
|
|
||||||
const apiExtensions = detectApiExtensions(builds || []);
|
|
||||||
const apiMatch = apiDir + '/';
|
|
||||||
|
|
||||||
for (const buildConfig of builds) {
|
for (const buildConfig of builds) {
|
||||||
let { src, use } = buildConfig;
|
let { src, use } = buildConfig;
|
||||||
@@ -436,10 +419,13 @@ export async function getBuildMatches(
|
|||||||
// We need to escape brackets since `glob` will
|
// We need to escape brackets since `glob` will
|
||||||
// try to find a group otherwise
|
// try to find a group otherwise
|
||||||
src = src.replace(/(\[|\])/g, '[$1]');
|
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
|
||||||
// lambda function files are trimmed of their file extension
|
const mapToEntrypoint = new Map<string, string>();
|
||||||
src = src.slice(0, -ext.length);
|
const extensionless = devServer.getExtensionlessFile(src);
|
||||||
|
if (extensionless) {
|
||||||
|
mapToEntrypoint.set(extensionless, src);
|
||||||
|
src = extensionless;
|
||||||
}
|
}
|
||||||
|
|
||||||
const files = fileList
|
const files = fileList
|
||||||
@@ -456,6 +442,7 @@ export async function getBuildMatches(
|
|||||||
matches.push({
|
matches.push({
|
||||||
...buildConfig,
|
...buildConfig,
|
||||||
src,
|
src,
|
||||||
|
entrypoint: mapToEntrypoint.get(src) || src,
|
||||||
builderWithPkg,
|
builderWithPkg,
|
||||||
buildOutput: {},
|
buildOutput: {},
|
||||||
buildResults: new Map(),
|
buildResults: new Map(),
|
||||||
|
|||||||
@@ -11,22 +11,21 @@ import { randomBytes } from 'crypto';
|
|||||||
import serveHandler from 'serve-handler';
|
import serveHandler from 'serve-handler';
|
||||||
import { watch, FSWatcher } from 'chokidar';
|
import { watch, FSWatcher } from 'chokidar';
|
||||||
import { parse as parseDotenv } from 'dotenv';
|
import { parse as parseDotenv } from 'dotenv';
|
||||||
import { basename, dirname, extname, join, delimiter } from 'path';
|
import { basename, dirname, extname, join } from 'path';
|
||||||
import { getTransformedRoutes, HandleValue } from '@now/routing-utils';
|
import { getTransformedRoutes, HandleValue } from '@now/routing-utils';
|
||||||
import directoryTemplate from 'serve-handler/src/directory';
|
import directoryTemplate from 'serve-handler/src/directory';
|
||||||
import getPort from 'get-port';
|
import getPort from 'get-port';
|
||||||
import { ChildProcess } from 'child_process';
|
import { ChildProcess } from 'child_process';
|
||||||
import isPortReachable from 'is-port-reachable';
|
import isPortReachable from 'is-port-reachable';
|
||||||
|
import which from 'which';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Builder,
|
Builder,
|
||||||
FileFsRef,
|
FileFsRef,
|
||||||
PackageJson,
|
PackageJson,
|
||||||
detectBuilders,
|
detectBuilders,
|
||||||
detectRoutes,
|
|
||||||
detectApiDirectory,
|
detectApiDirectory,
|
||||||
detectApiExtensions,
|
detectApiExtensions,
|
||||||
execAsync,
|
|
||||||
spawnCommand,
|
spawnCommand,
|
||||||
} from '@now/build-utils';
|
} from '@now/build-utils';
|
||||||
|
|
||||||
@@ -112,6 +111,8 @@ export default class DevServer {
|
|||||||
public address: string;
|
public address: string;
|
||||||
|
|
||||||
private cachedNowConfig: NowConfig | null;
|
private cachedNowConfig: NowConfig | null;
|
||||||
|
private apiDir: string | null;
|
||||||
|
private apiExtensions: Set<string>;
|
||||||
private server: http.Server;
|
private server: http.Server;
|
||||||
private stopping: boolean;
|
private stopping: boolean;
|
||||||
private serverUrlPrinted: boolean;
|
private serverUrlPrinted: boolean;
|
||||||
@@ -145,6 +146,8 @@ export default class DevServer {
|
|||||||
this.yarnPath = '/';
|
this.yarnPath = '/';
|
||||||
|
|
||||||
this.cachedNowConfig = null;
|
this.cachedNowConfig = null;
|
||||||
|
this.apiDir = null;
|
||||||
|
this.apiExtensions = new Set<string>();
|
||||||
this.server = http.createServer(this.devServerHandler);
|
this.server = http.createServer(this.devServerHandler);
|
||||||
this.server.timeout = 0; // Disable timeout
|
this.server.timeout = 0; // Disable timeout
|
||||||
this.serverUrlPrinted = false;
|
this.serverUrlPrinted = false;
|
||||||
@@ -291,6 +294,10 @@ export default class DevServer {
|
|||||||
const name = relative(this.cwd, fsPath);
|
const name = relative(this.cwd, fsPath);
|
||||||
try {
|
try {
|
||||||
this.files[name] = await FileFsRef.fromFsPath({ fsPath });
|
this.files[name] = await FileFsRef.fromFsPath({ fsPath });
|
||||||
|
const extensionless = this.getExtensionlessFile(name);
|
||||||
|
if (extensionless) {
|
||||||
|
this.files[extensionless] = await FileFsRef.fromFsPath({ fsPath });
|
||||||
|
}
|
||||||
fileChanged(name, changed, removed);
|
fileChanged(name, changed, removed);
|
||||||
this.output.debug(`File created: ${name}`);
|
this.output.debug(`File created: ${name}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -311,6 +318,11 @@ export default class DevServer {
|
|||||||
const name = relative(this.cwd, fsPath);
|
const name = relative(this.cwd, fsPath);
|
||||||
this.output.debug(`File deleted: ${name}`);
|
this.output.debug(`File deleted: ${name}`);
|
||||||
fileRemoved(name, this.files, changed, removed);
|
fileRemoved(name, this.files, changed, removed);
|
||||||
|
const extensionless = this.getExtensionlessFile(name);
|
||||||
|
if (extensionless) {
|
||||||
|
this.output.debug(`File deleted: ${extensionless}`);
|
||||||
|
fileRemoved(extensionless, this.files, changed, removed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleFileModified(
|
async handleFileModified(
|
||||||
@@ -343,6 +355,7 @@ export default class DevServer {
|
|||||||
this.cwd,
|
this.cwd,
|
||||||
this.yarnPath,
|
this.yarnPath,
|
||||||
this.output,
|
this.output,
|
||||||
|
this,
|
||||||
fileList
|
fileList
|
||||||
);
|
);
|
||||||
const sources = matches.map(m => m.src);
|
const sources = matches.map(m => m.src);
|
||||||
@@ -534,10 +547,19 @@ export default class DevServer {
|
|||||||
const featHandleMiss = true; // enable for zero config
|
const featHandleMiss = true; // enable for zero config
|
||||||
const { projectSettings, cleanUrls, trailingSlash } = config;
|
const { projectSettings, cleanUrls, trailingSlash } = config;
|
||||||
|
|
||||||
let { builders, warnings, errors } = await detectBuilders(files, pkg, {
|
let {
|
||||||
|
builders,
|
||||||
|
warnings,
|
||||||
|
errors,
|
||||||
|
defaultRoutes,
|
||||||
|
redirectRoutes,
|
||||||
|
} = await detectBuilders(files, pkg, {
|
||||||
tag: getDistTag(cliVersion) === 'canary' ? 'canary' : 'latest',
|
tag: getDistTag(cliVersion) === 'canary' ? 'canary' : 'latest',
|
||||||
functions: config.functions,
|
functions: config.functions,
|
||||||
...(projectSettings ? { projectSettings } : {}),
|
...(projectSettings ? { projectSettings } : {}),
|
||||||
|
featHandleMiss,
|
||||||
|
cleanUrls,
|
||||||
|
trailingSlash,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
@@ -554,32 +576,15 @@ export default class DevServer {
|
|||||||
builders = builders.filter(filterFrontendBuilds);
|
builders = builders.filter(filterFrontendBuilds);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
|
||||||
defaultRoutes,
|
|
||||||
redirectRoutes,
|
|
||||||
error: routesError,
|
|
||||||
} = await detectRoutes(
|
|
||||||
files,
|
|
||||||
builders,
|
|
||||||
featHandleMiss,
|
|
||||||
cleanUrls,
|
|
||||||
trailingSlash
|
|
||||||
);
|
|
||||||
|
|
||||||
config.builds = config.builds || [];
|
config.builds = config.builds || [];
|
||||||
config.builds.push(...builders);
|
config.builds.push(...builders);
|
||||||
|
|
||||||
if (routesError) {
|
const routes: RouteConfig[] = [];
|
||||||
this.output.error(routesError.message);
|
const { routes: nowConfigRoutes } = config;
|
||||||
await this.exit();
|
routes.push(...(redirectRoutes || []));
|
||||||
} else {
|
routes.push(...(nowConfigRoutes || []));
|
||||||
const routes: RouteConfig[] = [];
|
routes.push(...(defaultRoutes || []));
|
||||||
const { routes: nowConfigRoutes } = config;
|
config.routes = routes;
|
||||||
routes.push(...(redirectRoutes || []));
|
|
||||||
routes.push(...(nowConfigRoutes || []));
|
|
||||||
routes.push(...(defaultRoutes || []));
|
|
||||||
config.routes = routes;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,6 +605,8 @@ export default class DevServer {
|
|||||||
await this.validateNowConfig(config);
|
await this.validateNowConfig(config);
|
||||||
|
|
||||||
this.cachedNowConfig = config;
|
this.cachedNowConfig = config;
|
||||||
|
this.apiDir = detectApiDirectory(config.builds || []);
|
||||||
|
this.apiExtensions = detectApiExtensions(config.builds || []);
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -739,26 +746,21 @@ export default class DevServer {
|
|||||||
|
|
||||||
const opts = { output: this.output, isBuilds: true };
|
const opts = { output: this.output, isBuilds: true };
|
||||||
const files = await getFiles(this.cwd, nowConfig, opts);
|
const files = await getFiles(this.cwd, nowConfig, opts);
|
||||||
const results: { [filePath: string]: FileFsRef } = {};
|
this.files = {};
|
||||||
const apiDir = detectApiDirectory(nowConfig.builds || []);
|
|
||||||
const apiExtensions = detectApiExtensions(nowConfig.builds || []);
|
|
||||||
const apiMatch = apiDir + '/';
|
|
||||||
for (const fsPath of files) {
|
for (const fsPath of files) {
|
||||||
let path = relative(this.cwd, fsPath);
|
let path = relative(this.cwd, fsPath);
|
||||||
const { mode } = await fs.stat(fsPath);
|
const { mode } = await fs.stat(fsPath);
|
||||||
const ext = extname(path);
|
this.files[path] = new FileFsRef({ mode, fsPath });
|
||||||
if (apiDir && path.startsWith(apiMatch) && apiExtensions.has(ext)) {
|
const extensionless = this.getExtensionlessFile(path);
|
||||||
// lambda function files are trimmed of their file extension
|
if (extensionless) {
|
||||||
path = path.slice(0, -ext.length);
|
this.files[extensionless] = new FileFsRef({ mode, fsPath });
|
||||||
}
|
}
|
||||||
results[path] = new FileFsRef({ mode, fsPath });
|
|
||||||
}
|
}
|
||||||
this.files = results;
|
|
||||||
|
|
||||||
const builders: Set<string> = new Set(
|
const builders = new Set<string>(
|
||||||
(nowConfig.builds || [])
|
(nowConfig.builds || [])
|
||||||
.filter((b: Builder) => b.use)
|
.filter((b: Builder) => b.use)
|
||||||
.map((b: Builder) => b.use as string)
|
.map((b: Builder) => b.use)
|
||||||
);
|
);
|
||||||
|
|
||||||
await installBuilders(builders, this.yarnPath, this.output);
|
await installBuilders(builders, this.yarnPath, this.output);
|
||||||
@@ -1085,7 +1087,9 @@ export default class DevServer {
|
|||||||
// If the requested asset wasn't found in the match's
|
// If the requested asset wasn't found in the match's
|
||||||
// outputs then trigger a build
|
// outputs then trigger a build
|
||||||
const buildKey =
|
const buildKey =
|
||||||
requestPath === null ? match.src : `${match.src}-${requestPath}`;
|
requestPath === null
|
||||||
|
? match.entrypoint
|
||||||
|
: `${match.entrypoint}-${requestPath}`;
|
||||||
let buildPromise = this.inProgressBuilds.get(buildKey);
|
let buildPromise = this.inProgressBuilds.get(buildKey);
|
||||||
if (buildPromise) {
|
if (buildPromise) {
|
||||||
// A build for `buildKey` is already in progress, so don't trigger
|
// A build for `buildKey` is already in progress, so don't trigger
|
||||||
@@ -1127,6 +1131,19 @@ export default class DevServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getExtensionlessFile = (path: string) => {
|
||||||
|
const ext = extname(path);
|
||||||
|
if (
|
||||||
|
this.apiDir &&
|
||||||
|
path.startsWith(this.apiDir + '/') &&
|
||||||
|
this.apiExtensions.has(ext)
|
||||||
|
) {
|
||||||
|
// lambda function files are trimmed of their file extension
|
||||||
|
return path.slice(0, -ext.length);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DevServer HTTP handler
|
* DevServer HTTP handler
|
||||||
*/
|
*/
|
||||||
@@ -1606,18 +1623,14 @@ export default class DevServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async runDevCommand() {
|
async runDevCommand() {
|
||||||
if (!this.devCommand) return;
|
const { devCommand, cwd } = this;
|
||||||
|
|
||||||
const cwd = this.cwd;
|
if (!devCommand) {
|
||||||
|
return;
|
||||||
const { stdout: yarnBinStdout } = await execAsync('yarn', ['bin'], {
|
}
|
||||||
cwd,
|
|
||||||
});
|
|
||||||
|
|
||||||
const yarnBinPath = yarnBinStdout.trim();
|
|
||||||
|
|
||||||
this.output.log(
|
this.output.log(
|
||||||
`Running Dev Command ${chalk.cyan.bold(`“${this.devCommand}”`)}`
|
`Running Dev Command ${chalk.cyan.bold(`“${devCommand}”`)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const port = await getPort();
|
const port = await getPort();
|
||||||
@@ -1625,24 +1638,43 @@ export default class DevServer {
|
|||||||
const env: EnvConfig = {
|
const env: EnvConfig = {
|
||||||
...process.env,
|
...process.env,
|
||||||
...this.buildEnv,
|
...this.buildEnv,
|
||||||
PATH: `${yarnBinPath}${delimiter}${process.env.PATH}`,
|
|
||||||
NOW_REGION: 'dev1',
|
NOW_REGION: 'dev1',
|
||||||
PORT: `${port}`,
|
PORT: `${port}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is necesary so that the dev command in the Project
|
||||||
|
// will work cross-platform (especially Windows).
|
||||||
|
let command = devCommand
|
||||||
|
.replace(/\$PORT/g, `${port}`)
|
||||||
|
.replace(/%PORT%/g, `${port}`);
|
||||||
|
|
||||||
this.output.debug(
|
this.output.debug(
|
||||||
`Starting dev command with parameters : ${JSON.stringify({
|
`Starting dev command with parameters : ${JSON.stringify({
|
||||||
cwd: this.cwd,
|
cwd,
|
||||||
devCommand: this.devCommand,
|
command,
|
||||||
port,
|
port,
|
||||||
})}`
|
})}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const p = spawnCommand(this.devCommand, {
|
const isNpxAvailable = await which('npx')
|
||||||
stdio: 'inherit',
|
.then(() => true)
|
||||||
cwd,
|
.catch(() => false);
|
||||||
env,
|
|
||||||
});
|
if (isNpxAvailable) {
|
||||||
|
command = `npx --no-install ${command}`;
|
||||||
|
} else {
|
||||||
|
const isYarnAvailable = await which('yarn')
|
||||||
|
.then(() => true)
|
||||||
|
.catch(() => false);
|
||||||
|
|
||||||
|
if (isYarnAvailable) {
|
||||||
|
command = `yarn run --silent ${command}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.output.debug(`Spawning dev command: ${command}`);
|
||||||
|
|
||||||
|
const p = spawnCommand(command, { stdio: 'inherit', cwd, env });
|
||||||
|
|
||||||
p.on('exit', () => {
|
p.on('exit', () => {
|
||||||
this.devProcessPort = undefined;
|
this.devProcessPort = undefined;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export interface EnvConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface BuildMatch extends BuildConfig {
|
export interface BuildMatch extends BuildConfig {
|
||||||
|
entrypoint: string;
|
||||||
builderWithPkg: BuilderWithPackage;
|
builderWithPkg: BuilderWithPackage;
|
||||||
buildOutput: BuilderOutputs;
|
buildOutput: BuilderOutputs;
|
||||||
buildResults: Map<string | null, BuildResult>;
|
buildResults: Map<string | null, BuildResult>;
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import getDomainStatus from './get-domain-status';
|
|||||||
import promptBool from '../input/prompt-bool';
|
import promptBool from '../input/prompt-bool';
|
||||||
import purchaseDomain from './purchase-domain';
|
import purchaseDomain from './purchase-domain';
|
||||||
import stamp from '../output/stamp';
|
import stamp from '../output/stamp';
|
||||||
import wait from '../output/wait';
|
|
||||||
import * as ERRORS from '../errors-ts';
|
import * as ERRORS from '../errors-ts';
|
||||||
|
|
||||||
const isTTY = process.stdout.isTTY;
|
const isTTY = process.stdout.isTTY;
|
||||||
@@ -20,7 +19,7 @@ export default async function purchaseDomainIfAvailable(
|
|||||||
domain: string,
|
domain: string,
|
||||||
contextName: string
|
contextName: string
|
||||||
) {
|
) {
|
||||||
const cancelWait = wait(`Checking status of ${chalk.bold(domain)}`);
|
const cancelWait = output.spinner(`Checking status of ${chalk.bold(domain)}`);
|
||||||
const buyDomainStamp = stamp();
|
const buyDomainStamp = stamp();
|
||||||
const { available } = await getDomainStatus(client, domain);
|
const { available } = await getDomainStatus(client, domain);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import Client from './client';
|
import Client from './client';
|
||||||
import { APIError, InvalidToken } from './errors-ts';
|
import { APIError, InvalidToken } from './errors-ts';
|
||||||
import { Team } from '../types';
|
import { Team } from '../types';
|
||||||
|
// @ts-ignore
|
||||||
|
import NowTeams from './teams.js';
|
||||||
|
|
||||||
let teams: Team[] | undefined;
|
let teams: Team[] | undefined;
|
||||||
|
|
||||||
@@ -8,10 +10,15 @@ export default async function getTeams(client: Client) {
|
|||||||
if (teams) return teams;
|
if (teams) return teams;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await client.fetch<{ teams: Team[] }>('/teams');
|
// we're using NowTeams because `client.fetch` hangs on windows
|
||||||
|
const teamClient = new NowTeams({
|
||||||
|
apiUrl: client._apiUrl,
|
||||||
|
token: client._token,
|
||||||
|
debug: client._debug,
|
||||||
|
});
|
||||||
|
|
||||||
teams = res.teams;
|
const teams = (await teamClient.ls()).teams;
|
||||||
return teams;
|
return teams as Team[];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof APIError && error.status === 403) {
|
if (error instanceof APIError && error.status === 403) {
|
||||||
throw new InvalidToken();
|
throw new InvalidToken();
|
||||||
|
|||||||
@@ -70,7 +70,8 @@ export default class Now extends EventEmitter {
|
|||||||
skipAutoDetectionConfirmation,
|
skipAutoDetectionConfirmation,
|
||||||
},
|
},
|
||||||
org,
|
org,
|
||||||
isSettingUpProject
|
isSettingUpProject,
|
||||||
|
cwd
|
||||||
) {
|
) {
|
||||||
const opts = { output: this._output, hasNowJson };
|
const opts = { output: this._output, hasNowJson };
|
||||||
const { log, warn } = this._output;
|
const { log, warn } = this._output;
|
||||||
@@ -183,6 +184,7 @@ export default class Now extends EventEmitter {
|
|||||||
projectName: name,
|
projectName: name,
|
||||||
isSettingUpProject,
|
isSettingUpProject,
|
||||||
skipAutoDetectionConfirmation,
|
skipAutoDetectionConfirmation,
|
||||||
|
cwd,
|
||||||
});
|
});
|
||||||
|
|
||||||
// We report about files whose sizes are too big
|
// We report about files whose sizes are too big
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ export interface ProjectSettings {
|
|||||||
devCommand: string | null;
|
devCommand: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProjectSettingsWithFramework extends ProjectSettings {
|
||||||
|
framework: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
const fields: { name: string; value: keyof ProjectSettings }[] = [
|
const fields: { name: string; value: keyof ProjectSettings }[] = [
|
||||||
{ name: 'Build Command', value: 'buildCommand' },
|
{ name: 'Build Command', value: 'buildCommand' },
|
||||||
{ name: 'Output Directory', value: 'outputDirectory' },
|
{ name: 'Output Directory', value: 'outputDirectory' },
|
||||||
@@ -22,22 +26,30 @@ export default async function editProjectSettings(
|
|||||||
projectSettings: ProjectSettings | null,
|
projectSettings: ProjectSettings | null,
|
||||||
framework: Framework | null
|
framework: Framework | null
|
||||||
) {
|
) {
|
||||||
// create new settings object filled with "null" values
|
// create new settings object, missing values will be filled with `null`
|
||||||
const settings: Partial<ProjectSettings> = {};
|
const settings: Partial<ProjectSettingsWithFramework> = {
|
||||||
|
...projectSettings,
|
||||||
|
};
|
||||||
|
|
||||||
for (let field of fields) {
|
for (let field of fields) {
|
||||||
settings[field.value] =
|
settings[field.value] =
|
||||||
(projectSettings && projectSettings[field.value]) || null;
|
(projectSettings && projectSettings[field.value]) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skip editing project settings if no framework is detected
|
||||||
if (!framework) {
|
if (!framework) {
|
||||||
|
settings.framework = null;
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
output.print(
|
output.print(
|
||||||
`Auto-detected project settings (${chalk.bold(framework.name)}):\n`
|
!framework.slug
|
||||||
|
? `No framework detected. Default project settings:\n`
|
||||||
|
: `Auto-detected project settings (${chalk.bold(framework.name)}):\n`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
settings.framework = framework.slug;
|
||||||
|
|
||||||
for (let field of fields) {
|
for (let field of fields) {
|
||||||
const defaults = framework.settings[field.value];
|
const defaults = framework.settings[field.value];
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import chalk from 'chalk';
|
|||||||
import { ProjectNotFound } from '../../util/errors-ts';
|
import { ProjectNotFound } from '../../util/errors-ts';
|
||||||
import { Output } from '../output';
|
import { Output } from '../output';
|
||||||
import { Project, Org } from '../../types';
|
import { Project, Org } from '../../types';
|
||||||
import wait from '../output/wait';
|
import slugify from '@sindresorhus/slugify';
|
||||||
|
|
||||||
export default async function inputProject(
|
export default async function inputProject(
|
||||||
output: Output,
|
output: Output,
|
||||||
@@ -19,16 +19,26 @@ export default async function inputProject(
|
|||||||
return detectedProjectName;
|
return detectedProjectName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const slugifiedName = slugify(detectedProjectName);
|
||||||
|
|
||||||
// attempt to auto-detect a project to link
|
// attempt to auto-detect a project to link
|
||||||
let detectedProject = null;
|
let detectedProject = null;
|
||||||
const existingProjectSpinner = wait('Searching for existing projects…', 1000);
|
const existingProjectSpinner = output.spinner(
|
||||||
|
'Searching for existing projects…',
|
||||||
|
1000
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
const project = await getProjectByIdOrName(
|
const [project, slugifiedProject] = await Promise.all([
|
||||||
client,
|
getProjectByIdOrName(client, detectedProjectName, org.id),
|
||||||
detectedProjectName,
|
slugifiedName !== detectedProjectName
|
||||||
org.id
|
? getProjectByIdOrName(client, slugifiedName, org.id)
|
||||||
);
|
: null,
|
||||||
detectedProject = project instanceof ProjectNotFound ? null : project;
|
]);
|
||||||
|
detectedProject = !(project instanceof ProjectNotFound)
|
||||||
|
? project
|
||||||
|
: !(slugifiedProject instanceof ProjectNotFound)
|
||||||
|
? slugifiedProject
|
||||||
|
: null;
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
existingProjectSpinner();
|
existingProjectSpinner();
|
||||||
|
|
||||||
@@ -42,7 +52,7 @@ export default async function inputProject(
|
|||||||
if (
|
if (
|
||||||
await confirm(
|
await confirm(
|
||||||
`Found project ${chalk.cyan(
|
`Found project ${chalk.cyan(
|
||||||
`“${org.slug}/${detectedProjectName}”`
|
`“${org.slug}/${detectedProject.name}”`
|
||||||
)}. Link to it?`,
|
)}. Link to it?`,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
@@ -74,7 +84,7 @@ export default async function inputProject(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const spinner = wait('Verifying project name…', 1000);
|
const spinner = output.spinner('Verifying project name…', 1000);
|
||||||
try {
|
try {
|
||||||
project = await getProjectByIdOrName(client, projectName, org.id);
|
project = await getProjectByIdOrName(client, projectName, org.id);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -97,7 +107,7 @@ export default async function inputProject(
|
|||||||
type: 'input',
|
type: 'input',
|
||||||
name: 'newProjectName',
|
name: 'newProjectName',
|
||||||
message: `What’s your project’s name?`,
|
message: `What’s your project’s name?`,
|
||||||
default: !detectedProject ? detectedProjectName : undefined,
|
default: !detectedProject ? slugifiedName : undefined,
|
||||||
});
|
});
|
||||||
newProjectName = answers.newProjectName as string;
|
newProjectName = answers.newProjectName as string;
|
||||||
|
|
||||||
@@ -106,7 +116,7 @@ export default async function inputProject(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const spinner = wait('Verifying project name…', 1000);
|
const spinner = output.spinner('Verifying project name…', 1000);
|
||||||
let existingProject: Project | ProjectNotFound;
|
let existingProject: Project | ProjectNotFound;
|
||||||
try {
|
try {
|
||||||
existingProject = await getProjectByIdOrName(
|
existingProject = await getProjectByIdOrName(
|
||||||
|
|||||||
54
packages/now-cli/src/util/input/input-root-directory.ts
Normal file
54
packages/now-cli/src/util/input/input-root-directory.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import path from 'path';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import inquirer from 'inquirer';
|
||||||
|
import { Output } from '../output';
|
||||||
|
import { validateRootDirectory } from '../validate-paths';
|
||||||
|
|
||||||
|
export async function inputRootDirectory(
|
||||||
|
cwd: string,
|
||||||
|
output: Output,
|
||||||
|
autoConfirm: boolean
|
||||||
|
) {
|
||||||
|
if (autoConfirm) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const basename = path.basename(cwd);
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
|
while (true) {
|
||||||
|
const { rootDirectory } = await inquirer.prompt({
|
||||||
|
type: 'input',
|
||||||
|
name: 'rootDirectory',
|
||||||
|
message: `In which directory is your code located?`,
|
||||||
|
transformer: (input: string) => {
|
||||||
|
return `${chalk.dim(`${basename}/`)}${input}`;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!rootDirectory) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normal = path.normalize(rootDirectory);
|
||||||
|
|
||||||
|
if (normal === '.' || normal === './') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullPath = path.join(cwd, normal);
|
||||||
|
|
||||||
|
if (
|
||||||
|
(await validateRootDirectory(
|
||||||
|
output,
|
||||||
|
cwd,
|
||||||
|
fullPath,
|
||||||
|
'Please choose a different one.'
|
||||||
|
)) === false
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,11 +3,12 @@ import inquirer from 'inquirer';
|
|||||||
import getUser from '../get-user';
|
import getUser from '../get-user';
|
||||||
import getTeams from '../get-teams';
|
import getTeams from '../get-teams';
|
||||||
import { User, Team, Org } from '../../types';
|
import { User, Team, Org } from '../../types';
|
||||||
import wait from '../output/wait';
|
import { Output } from '../output';
|
||||||
|
|
||||||
type Choice = { name: string; value: Org };
|
type Choice = { name: string; value: Org };
|
||||||
|
|
||||||
export default async function selectProject(
|
export default async function selectOrg(
|
||||||
|
output: Output,
|
||||||
question: string,
|
question: string,
|
||||||
client: Client,
|
client: Client,
|
||||||
currentTeam?: string,
|
currentTeam?: string,
|
||||||
@@ -15,7 +16,7 @@ export default async function selectProject(
|
|||||||
): Promise<Org> {
|
): Promise<Org> {
|
||||||
require('./patch-inquirer');
|
require('./patch-inquirer');
|
||||||
|
|
||||||
const spinner = wait('Loading scopes…', 1000);
|
const spinner = output.spinner('Loading scopes…', 1000);
|
||||||
let user: User;
|
let user: User;
|
||||||
let teams: Team[];
|
let teams: Team[];
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import chalk from 'chalk';
|
|||||||
import boxen from 'boxen';
|
import boxen from 'boxen';
|
||||||
import { format } from 'util';
|
import { format } from 'util';
|
||||||
import { Console } from 'console';
|
import { Console } from 'console';
|
||||||
|
import wait from './wait';
|
||||||
|
|
||||||
export type Output = ReturnType<typeof createOutput>;
|
export type Output = ReturnType<typeof createOutput>;
|
||||||
|
|
||||||
@@ -76,6 +77,20 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function spinner(message: string, delay: number = 300) {
|
||||||
|
if (debugEnabled) {
|
||||||
|
debug(`Spinner invoked (${message}) with a ${delay}ms delay`);
|
||||||
|
let isEnded = false;
|
||||||
|
return () => {
|
||||||
|
if (isEnded) return;
|
||||||
|
isEnded = true;
|
||||||
|
debug(`Spinner ended (${message})`);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return wait(message, delay);
|
||||||
|
}
|
||||||
|
|
||||||
// This is pretty hacky, but since we control the version of Node.js
|
// This is pretty hacky, but since we control the version of Node.js
|
||||||
// being used because of `pkg` it's safe to do in this case.
|
// being used because of `pkg` it's safe to do in this case.
|
||||||
const c = {
|
const c = {
|
||||||
@@ -109,5 +124,6 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
|
|||||||
dim,
|
dim,
|
||||||
time,
|
time,
|
||||||
note,
|
note,
|
||||||
|
spinner,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import ora from 'ora';
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import eraseLines from './erase-lines';
|
import eraseLines from './erase-lines';
|
||||||
|
|
||||||
export default function wait(msg: string, timeout: number = 300, _ora = ora) {
|
export default function wait(msg: string, delay: number = 300, _ora = ora) {
|
||||||
let spinner: ReturnType<typeof _ora>;
|
let spinner: ReturnType<typeof _ora>;
|
||||||
let running = false;
|
let running = false;
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ export default function wait(msg: string, timeout: number = 300, _ora = ora) {
|
|||||||
spinner.color = 'gray';
|
spinner.color = 'gray';
|
||||||
spinner.start();
|
spinner.start();
|
||||||
running = true;
|
running = true;
|
||||||
}, timeout);
|
}, delay);
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
clearTimeout(planned);
|
clearTimeout(planned);
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import { Project } from '../../types';
|
|||||||
import { Org, ProjectLink } from '../../types';
|
import { Org, ProjectLink } from '../../types';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { prependEmoji, emoji } from '../emoji';
|
import { prependEmoji, emoji } from '../emoji';
|
||||||
import wait from '../output/wait';
|
|
||||||
|
|
||||||
const readFile = promisify(fs.readFile);
|
const readFile = promisify(fs.readFile);
|
||||||
const writeFile = promisify(fs.writeFile);
|
const writeFile = promisify(fs.writeFile);
|
||||||
@@ -86,7 +85,7 @@ export async function getLinkedOrg(
|
|||||||
return { status: 'not_linked', org: null };
|
return { status: 'not_linked', org: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
const spinner = wait('Retrieving scope…', 1000);
|
const spinner = output.spinner('Retrieving scope…', 1000);
|
||||||
try {
|
try {
|
||||||
const org = await getOrgById(client, orgId);
|
const org = await getOrgById(client, orgId);
|
||||||
|
|
||||||
@@ -141,7 +140,7 @@ export async function getLinkedProject(
|
|||||||
return { status: 'not_linked', org: null, project: null };
|
return { status: 'not_linked', org: null, project: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
const spinner = wait('Retrieving project…', 1000);
|
const spinner = output.spinner('Retrieving project…', 1000);
|
||||||
let org: Org | null;
|
let org: Org | null;
|
||||||
let project: Project | ProjectNotFound | null;
|
let project: Project | ProjectNotFound | null;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import wait from '../output/wait';
|
|
||||||
import joinWords from '../output/join-words';
|
import joinWords from '../output/join-words';
|
||||||
import * as Errors from '../errors-ts';
|
import * as Errors from '../errors-ts';
|
||||||
import { Output } from '../output';
|
import { Output } from '../output';
|
||||||
@@ -17,7 +16,7 @@ export default async function patchDeploymentScale(
|
|||||||
scaleArgs: ScaleArgs,
|
scaleArgs: ScaleArgs,
|
||||||
url: string
|
url: string
|
||||||
) {
|
) {
|
||||||
const cancelWait = wait(
|
const cancelWait = output.spinner(
|
||||||
`Setting scale rules for ${joinWords(
|
`Setting scale rules for ${joinWords(
|
||||||
Object.keys(scaleArgs).map(dc => `${chalk.bold(dc)}`)
|
Object.keys(scaleArgs).map(dc => `${chalk.bold(dc)}`)
|
||||||
)}`
|
)}`
|
||||||
@@ -28,7 +27,7 @@ export default async function patchDeploymentScale(
|
|||||||
`/v3/now/deployments/${encodeURIComponent(deploymentId)}/instances`,
|
`/v3/now/deployments/${encodeURIComponent(deploymentId)}/instances`,
|
||||||
{
|
{
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
body: scaleArgs
|
body: scaleArgs,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
cancelWait();
|
cancelWait();
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { Output } from '../output';
|
|||||||
import * as ERRORS from '../errors-ts';
|
import * as ERRORS from '../errors-ts';
|
||||||
import Client from '../client';
|
import Client from '../client';
|
||||||
import joinWords from '../output/join-words';
|
import joinWords from '../output/join-words';
|
||||||
import wait from '../output/wait';
|
|
||||||
|
|
||||||
type ScaleArgs = {
|
type ScaleArgs = {
|
||||||
min: number;
|
min: number;
|
||||||
@@ -18,7 +17,7 @@ export default async function setScale(
|
|||||||
scaleArgs: ScaleArgs | DeploymentScale,
|
scaleArgs: ScaleArgs | DeploymentScale,
|
||||||
url: string
|
url: string
|
||||||
) {
|
) {
|
||||||
const cancelWait = wait(
|
const cancelWait = output.spinner(
|
||||||
`Setting scale rules for ${joinWords(
|
`Setting scale rules for ${joinWords(
|
||||||
Object.keys(scaleArgs).map(dc => `${chalk.bold(dc)}`)
|
Object.keys(scaleArgs).map(dc => `${chalk.bold(dc)}`)
|
||||||
)}`
|
)}`
|
||||||
@@ -29,7 +28,7 @@ export default async function setScale(
|
|||||||
`/v3/now/deployments/${encodeURIComponent(deploymentId)}/instances`,
|
`/v3/now/deployments/${encodeURIComponent(deploymentId)}/instances`,
|
||||||
{
|
{
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body: scaleArgs
|
body: scaleArgs,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
cancelWait();
|
cancelWait();
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import Client from '../client';
|
|||||||
import joinWords from '../output/join-words';
|
import joinWords from '../output/join-words';
|
||||||
import stamp from '../output/stamp';
|
import stamp from '../output/stamp';
|
||||||
import verifyDeploymentScale from './verify-deployment-scale';
|
import verifyDeploymentScale from './verify-deployment-scale';
|
||||||
import wait from '../output/wait';
|
|
||||||
|
|
||||||
export default async function waitForScale(
|
export default async function waitForScale(
|
||||||
output: Output,
|
output: Output,
|
||||||
@@ -19,7 +18,7 @@ export default async function waitForScale(
|
|||||||
) {
|
) {
|
||||||
const remainingDCs = new Set(Object.keys(scale));
|
const remainingDCs = new Set(Object.keys(scale));
|
||||||
const scaleStamp = stamp();
|
const scaleStamp = stamp();
|
||||||
let cancelWait = renderWaitDcs(Array.from(remainingDCs.keys()));
|
let cancelWait = renderWaitDcs(output, Array.from(remainingDCs.keys()));
|
||||||
|
|
||||||
for await (const dcReady of verifyDeploymentScale(
|
for await (const dcReady of verifyDeploymentScale(
|
||||||
output,
|
output,
|
||||||
@@ -43,13 +42,13 @@ export default async function waitForScale(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (remainingDCs.size > 0) {
|
if (remainingDCs.size > 0) {
|
||||||
cancelWait = renderWaitDcs(Array.from(remainingDCs.keys()));
|
cancelWait = renderWaitDcs(output, Array.from(remainingDCs.keys()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderWaitDcs(dcs: string[]) {
|
function renderWaitDcs(output: Output, dcs: string[]) {
|
||||||
return wait(
|
return output.spinner(
|
||||||
`Waiting for instances in ${joinWords(
|
`Waiting for instances in ${joinWords(
|
||||||
dcs.map(dc => chalk.bold(dc))
|
dcs.map(dc => chalk.bold(dc))
|
||||||
)} to be ready`
|
)} to be ready`
|
||||||
|
|||||||
@@ -9,6 +9,48 @@ import toHumanPath from './humanize-path';
|
|||||||
|
|
||||||
const stat = promisify(lstatRaw);
|
const stat = promisify(lstatRaw);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper function to validate the `rootDirectory` input.
|
||||||
|
*/
|
||||||
|
export async function validateRootDirectory(
|
||||||
|
output: Output,
|
||||||
|
cwd: string,
|
||||||
|
path: string,
|
||||||
|
errorSuffix: string
|
||||||
|
) {
|
||||||
|
const pathStat = await stat(path).catch(() => null);
|
||||||
|
const suffix = errorSuffix ? ` ${errorSuffix}` : '';
|
||||||
|
|
||||||
|
if (!pathStat) {
|
||||||
|
output.print(
|
||||||
|
`${chalk.red('Error!')} The provided path ${chalk.cyan(
|
||||||
|
`“${toHumanPath(path)}”`
|
||||||
|
)} does not exist.${suffix}\n`
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pathStat.isDirectory()) {
|
||||||
|
output.print(
|
||||||
|
`${chalk.red('Error!')} The provided path ${chalk.cyan(
|
||||||
|
`“${toHumanPath(path)}”`
|
||||||
|
)} is a file, but expected a directory.${suffix}\n`
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!path.startsWith(cwd)) {
|
||||||
|
output.print(
|
||||||
|
`${chalk.red('Error!')} The provided path ${chalk.cyan(
|
||||||
|
`“${toHumanPath(path)}”`
|
||||||
|
)} is outside of the project.${suffix}\n`
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
export default async function validatePaths(
|
export default async function validatePaths(
|
||||||
output: Output,
|
output: Output,
|
||||||
paths: string[]
|
paths: string[]
|
||||||
@@ -25,10 +67,7 @@ export default async function validatePaths(
|
|||||||
const path = paths[0];
|
const path = paths[0];
|
||||||
|
|
||||||
// can only deploy a directory
|
// can only deploy a directory
|
||||||
let pathStat;
|
const pathStat = await stat(path).catch(() => null);
|
||||||
try {
|
|
||||||
pathStat = await stat(path);
|
|
||||||
} catch (error) {}
|
|
||||||
|
|
||||||
if (!pathStat) {
|
if (!pathStat) {
|
||||||
output.print(
|
output.print(
|
||||||
|
|||||||
53
packages/now-cli/test/dev-server.unit.js
vendored
53
packages/now-cli/test/dev-server.unit.js
vendored
@@ -5,7 +5,7 @@ import execa from 'execa';
|
|||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import fetch from 'node-fetch';
|
import fetch from 'node-fetch';
|
||||||
import listen from 'async-listen';
|
import listen from 'async-listen';
|
||||||
import { request, createServer } from 'http';
|
import { createServer } from 'http';
|
||||||
import createOutput from '../src/util/output';
|
import createOutput from '../src/util/output';
|
||||||
import DevServer from '../src/util/dev/server';
|
import DevServer from '../src/util/dev/server';
|
||||||
import { installBuilders, getBuildUtils } from '../src/util/dev/builder-cache';
|
import { installBuilders, getBuildUtils } from '../src/util/dev/builder-cache';
|
||||||
@@ -13,12 +13,28 @@ import parseListen from '../src/util/dev/parse-listen';
|
|||||||
|
|
||||||
async function runNpmInstall(fixturePath) {
|
async function runNpmInstall(fixturePath) {
|
||||||
if (await fs.exists(path.join(fixturePath, 'package.json'))) {
|
if (await fs.exists(path.join(fixturePath, 'package.json'))) {
|
||||||
return execa('yarn', ['install'], { cwd: fixturePath });
|
return execa('yarn', ['install'], { cwd: fixturePath, shell: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const skipOnWindows = new Set([
|
||||||
|
'now-dev-default-builds-and-routes',
|
||||||
|
'now-dev-static-routes',
|
||||||
|
'now-dev-static-build-routing',
|
||||||
|
'now-dev-directory-listing',
|
||||||
|
'now-dev-api-with-public',
|
||||||
|
'now-dev-api-with-static',
|
||||||
|
'now-dev-custom-404',
|
||||||
|
]);
|
||||||
|
|
||||||
function testFixture(name, fn) {
|
function testFixture(name, fn) {
|
||||||
return async t => {
|
return async t => {
|
||||||
|
if (process.platform === 'win32' && skipOnWindows.has(name)) {
|
||||||
|
console.log(`Skipping test "${name}" on Windows.`);
|
||||||
|
t.is(true, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let server;
|
let server;
|
||||||
|
|
||||||
const fixturePath = path.join(__dirname, 'fixtures', 'unit', name);
|
const fixturePath = path.join(__dirname, 'fixtures', 'unit', name);
|
||||||
@@ -68,14 +84,6 @@ function validateResponseHeaders(t, res, podId = null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function get(url) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
request(url, resolve)
|
|
||||||
.on('error', reject)
|
|
||||||
.end();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'[DevServer] Maintains query when invoking lambda',
|
'[DevServer] Maintains query when invoking lambda',
|
||||||
testFixture('now-dev-query-invoke', async (t, server) => {
|
testFixture('now-dev-query-invoke', async (t, server) => {
|
||||||
@@ -138,16 +146,23 @@ test(
|
|||||||
test(
|
test(
|
||||||
'[DevServer] Allow `cache-control` to be overwritten',
|
'[DevServer] Allow `cache-control` to be overwritten',
|
||||||
testFixture('now-dev-headers', async (t, server) => {
|
testFixture('now-dev-headers', async (t, server) => {
|
||||||
const res = await get(
|
const res = await fetch(
|
||||||
`${server.address}/?name=cache-control&value=immutable`
|
`${server.address}/?name=cache-control&value=immutable`
|
||||||
);
|
);
|
||||||
t.is(res.headers['cache-control'], 'immutable');
|
t.is(res.headers.get('cache-control'), 'immutable');
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'[DevServer] Sends `etag` header for static files',
|
'[DevServer] Sends `etag` header for static files',
|
||||||
testFixture('now-dev-headers', async (t, server) => {
|
testFixture('now-dev-headers', async (t, server) => {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
console.log(
|
||||||
|
'Skipping "etag" test on windows since it yields a different result.'
|
||||||
|
);
|
||||||
|
t.is(true, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const res = await fetch(`${server.address}/foo.txt`);
|
const res = await fetch(`${server.address}/foo.txt`);
|
||||||
t.is(res.headers.get('etag'), '"d263af8ab880c0b97eb6c5c125b5d44f9e5addd9"');
|
t.is(res.headers.get('etag'), '"d263af8ab880c0b97eb6c5c125b5d44f9e5addd9"');
|
||||||
t.is(await res.text(), 'hi\n');
|
t.is(await res.text(), 'hi\n');
|
||||||
@@ -400,12 +415,14 @@ test('[DevServer] parseListen()', t => {
|
|||||||
t.deepEqual(parseListen('0.0.0.0'), [3000, '0.0.0.0']);
|
t.deepEqual(parseListen('0.0.0.0'), [3000, '0.0.0.0']);
|
||||||
t.deepEqual(parseListen('127.0.0.1:3005'), [3005, '127.0.0.1']);
|
t.deepEqual(parseListen('127.0.0.1:3005'), [3005, '127.0.0.1']);
|
||||||
t.deepEqual(parseListen('tcp://127.0.0.1:5000'), [5000, '127.0.0.1']);
|
t.deepEqual(parseListen('tcp://127.0.0.1:5000'), [5000, '127.0.0.1']);
|
||||||
t.deepEqual(parseListen('unix:/home/user/server.sock'), [
|
if (process.platform !== 'win32') {
|
||||||
'/home/user/server.sock',
|
t.deepEqual(parseListen('unix:/home/user/server.sock'), [
|
||||||
]);
|
'/home/user/server.sock',
|
||||||
t.deepEqual(parseListen('pipe:\\\\.\\pipe\\PipeName'), [
|
]);
|
||||||
'\\\\.\\pipe\\PipeName',
|
t.deepEqual(parseListen('pipe:\\\\.\\pipe\\PipeName'), [
|
||||||
]);
|
'\\\\.\\pipe\\PipeName',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
let err;
|
let err;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
SKIP_PREFLIGHT_CHECK=true
|
||||||
@@ -22,3 +22,4 @@ npm-debug.log*
|
|||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
!yarn.lock
|
!yarn.lock
|
||||||
|
!.env
|
||||||
|
|||||||
1
packages/now-cli/test/dev/fixtures/custom-runtime/.gitignore
vendored
Normal file
1
packages/now-cli/test/dev/fixtures/custom-runtime/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.now
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"functions": {
|
"functions": {
|
||||||
"api/user": {
|
"api/user.sh": {
|
||||||
"runtime": "now-bash@1.0.3"
|
"runtime": "now-bash@3.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
packages/now-cli/test/dev/fixtures/python-flask/.gitignore
vendored
Normal file
1
packages/now-cli/test/dev/fixtures/python-flask/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.now
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
from flask import Flask, Response
|
||||||
|
from datetime import datetime
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
@app.route('/', defaults={'path': ''})
|
||||||
|
@app.route('/<path:path>')
|
||||||
|
def date(path):
|
||||||
|
d = datetime.now().isoformat()
|
||||||
|
return Response("Current date is %s" % (d), mimetype='text/html')
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
from flask import Flask, Response, request
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
@app.route('/', defaults={'path': ''})
|
||||||
|
@app.route('/<path:path>')
|
||||||
|
def user(path):
|
||||||
|
name = request.args.get('name')
|
||||||
|
return Response("Hello %s" % (name), mimetype='text/html')
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Home Page for Python
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Flask==1.0.3
|
||||||
@@ -109,13 +109,14 @@ function validateResponseHeaders(t, res) {
|
|||||||
async function exec(directory, args = []) {
|
async function exec(directory, args = []) {
|
||||||
return execa(binaryPath, ['dev', directory, ...args], {
|
return execa(binaryPath, ['dev', directory, ...args], {
|
||||||
reject: false,
|
reject: false,
|
||||||
|
shell: true,
|
||||||
env: { __NOW_SKIP_DEV_COMMAND: 1 },
|
env: { __NOW_SKIP_DEV_COMMAND: 1 },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runNpmInstall(fixturePath) {
|
async function runNpmInstall(fixturePath) {
|
||||||
if (await fs.exists(path.join(fixturePath, 'package.json'))) {
|
if (await fs.exists(path.join(fixturePath, 'package.json'))) {
|
||||||
return execa('yarn', ['install'], { cwd: fixturePath });
|
return execa('yarn', ['install'], { cwd: fixturePath, shell: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +124,7 @@ async function getPackedBuilderPath(builderDirName) {
|
|||||||
const packagePath = path.join(__dirname, '..', '..', '..', builderDirName);
|
const packagePath = path.join(__dirname, '..', '..', '..', builderDirName);
|
||||||
const output = await execa('npm', ['pack'], {
|
const output = await execa('npm', ['pack'], {
|
||||||
cwd: packagePath,
|
cwd: packagePath,
|
||||||
|
shell: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (output.exitCode !== 0 || output.stdout.trim() === '') {
|
if (output.exitCode !== 0 || output.stdout.trim() === '') {
|
||||||
@@ -160,6 +162,7 @@ async function testFixture(directory, opts = {}, args = []) {
|
|||||||
{
|
{
|
||||||
reject: false,
|
reject: false,
|
||||||
detached: true,
|
detached: true,
|
||||||
|
shell: true,
|
||||||
stdio: 'pipe',
|
stdio: 'pipe',
|
||||||
...opts,
|
...opts,
|
||||||
env: { ...opts.env, __NOW_SKIP_DEV_COMMAND: 1 },
|
env: { ...opts.env, __NOW_SKIP_DEV_COMMAND: 1 },
|
||||||
@@ -226,6 +229,7 @@ function testFixtureStdio(directory, fn) {
|
|||||||
let printedOutput = false;
|
let printedOutput = false;
|
||||||
|
|
||||||
dev = execa(binaryPath, ['dev', dir, '-l', port], {
|
dev = execa(binaryPath, ['dev', dir, '-l', port], {
|
||||||
|
shell: true,
|
||||||
env: { __NOW_SKIP_DEV_COMMAND: 1 },
|
env: { __NOW_SKIP_DEV_COMMAND: 1 },
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -638,7 +642,7 @@ test(
|
|||||||
await testPath(200, '/about/', 'About Page');
|
await testPath(200, '/about/', 'About Page');
|
||||||
await testPath(200, '/sub/', 'Sub Index Page');
|
await testPath(200, '/sub/', 'Sub Index Page');
|
||||||
await testPath(200, '/sub/another/', 'Sub Another Page');
|
await testPath(200, '/sub/another/', 'Sub Another Page');
|
||||||
await testPath(200, '/style.css/', 'body { color: green }');
|
await testPath(200, '/style.css', 'body { color: green }');
|
||||||
await testPath(308, '/index.html', '', { Location: '/' });
|
await testPath(308, '/index.html', '', { Location: '/' });
|
||||||
await testPath(308, '/about.html', '', { Location: '/about/' });
|
await testPath(308, '/about.html', '', { Location: '/about/' });
|
||||||
await testPath(308, '/sub/index.html', '', { Location: '/sub/' });
|
await testPath(308, '/sub/index.html', '', { Location: '/sub/' });
|
||||||
@@ -653,17 +657,15 @@ test(
|
|||||||
'[now dev] test trailingSlash true serve correct content',
|
'[now dev] test trailingSlash true serve correct content',
|
||||||
testFixtureStdio('test-trailing-slash', async (t, port, testPath) => {
|
testFixtureStdio('test-trailing-slash', async (t, port, testPath) => {
|
||||||
await testPath(200, '/', 'Index Page');
|
await testPath(200, '/', 'Index Page');
|
||||||
await testPath(200, '/index.html/', 'Index Page');
|
await testPath(200, '/index.html', 'Index Page');
|
||||||
await testPath(200, '/about.html/', 'About Page');
|
await testPath(200, '/about.html', 'About Page');
|
||||||
await testPath(200, '/sub/', 'Sub Index Page');
|
await testPath(200, '/sub/', 'Sub Index Page');
|
||||||
await testPath(200, '/sub/index.html/', 'Sub Index Page');
|
await testPath(200, '/sub/index.html', 'Sub Index Page');
|
||||||
await testPath(200, '/sub/another.html/', 'Sub Another Page');
|
await testPath(200, '/sub/another.html', 'Sub Another Page');
|
||||||
await testPath(200, '/style.css/', 'body { color: green }');
|
await testPath(200, '/style.css', 'body { color: green }');
|
||||||
await testPath(308, '/about.html', '', { Location: '/about.html/' });
|
await testPath(308, '/about.html/', '', { Location: '/about.html' });
|
||||||
|
await testPath(308, '/style.css/', '', { Location: '/style.css' });
|
||||||
await testPath(308, '/sub', '', { Location: '/sub/' });
|
await testPath(308, '/sub', '', { Location: '/sub/' });
|
||||||
await testPath(308, '/sub/another.html', '', {
|
|
||||||
Location: '/sub/another.html/',
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -772,17 +774,17 @@ test('[now dev] 03-aurelia', async t => {
|
|||||||
await tester(t);
|
await tester(t);
|
||||||
});
|
});
|
||||||
|
|
||||||
// test(
|
test(
|
||||||
// '[now dev] 04-create-react-app-node',
|
'[now dev] 04-create-react-app',
|
||||||
// testFixtureStdio('create-react-app', async(t, port) => {
|
testFixtureStdio('04-create-react-app', async (t, port) => {
|
||||||
// const response = await fetch(`http://localhost:${port}`);
|
const response = await fetch(`http://localhost:${port}`);
|
||||||
|
|
||||||
// validateResponseHeaders(t, response);
|
validateResponseHeaders(t, response);
|
||||||
|
|
||||||
// const body = await response.text();
|
const body = await response.text();
|
||||||
// t.regex(body, /React App/gm);
|
t.regex(body, /React App/gm);
|
||||||
// })
|
})
|
||||||
// );
|
);
|
||||||
|
|
||||||
test('[now dev] 05-gatsby', async t => {
|
test('[now dev] 05-gatsby', async t => {
|
||||||
if (shouldSkip(t, '05-gatsby', '>^6.14.0 || ^8.10.0 || >=9.10.0')) return;
|
if (shouldSkip(t, '05-gatsby', '>^6.14.0 || ^8.10.0 || >=9.10.0')) return;
|
||||||
@@ -1367,13 +1369,40 @@ test('[now dev] 25-nextjs-src-dir', async t => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'[now dev] Use runtime from the functions property',
|
'[now dev] Use `@now/python` with Flask requirements.txt',
|
||||||
testFixtureStdio('custom-runtime', async (t, port) => {
|
testFixtureStdio('python-flask', async (t, port) => {
|
||||||
const response = await fetchWithRetry(`http://localhost:${port}/api/user`);
|
const name = 'Alice';
|
||||||
|
const year = new Date().getFullYear();
|
||||||
|
const user = await fetchWithRetry(
|
||||||
|
`http://localhost:${port}/api/user?name=${name}`
|
||||||
|
);
|
||||||
|
const date = await fetchWithRetry(`http://localhost:${port}/api/date`);
|
||||||
|
const ext = await fetchWithRetry(`http://localhost:${port}/api/date.py`);
|
||||||
|
|
||||||
validateResponseHeaders(t, response);
|
validateResponseHeaders(t, user);
|
||||||
|
validateResponseHeaders(t, date);
|
||||||
|
validateResponseHeaders(t, ext);
|
||||||
|
|
||||||
const body = await response.text();
|
t.regex(await user.text(), new RegExp(`Hello ${name}`));
|
||||||
t.regex(body, /Hello, from Bash!/gm);
|
t.regex(await date.text(), new RegExp(`Current date is ${year}`));
|
||||||
|
t.regex(await ext.text(), new RegExp(`Current date is ${year}`));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'[now dev] Use runtime from the functions property',
|
||||||
|
testFixtureStdio('custom-runtime', async (t, port) => {
|
||||||
|
const extensionless = await fetchWithRetry(
|
||||||
|
`http://localhost:${port}/api/user`
|
||||||
|
);
|
||||||
|
const extension = await fetchWithRetry(
|
||||||
|
`http://localhost:${port}/api/user.sh`
|
||||||
|
);
|
||||||
|
|
||||||
|
validateResponseHeaders(t, extensionless);
|
||||||
|
validateResponseHeaders(t, extension);
|
||||||
|
|
||||||
|
t.regex(await extensionless.text(), /Hello, from Bash!/gm);
|
||||||
|
t.regex(await extension.text(), /Hello, from Bash!/gm);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -485,6 +485,17 @@ CMD ["node", "index.js"]`,
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
'project-root-directory': {
|
||||||
|
'src/index.html': '<h1>I am a website.</h1>',
|
||||||
|
'src/now.json': JSON.stringify({
|
||||||
|
rewrites: [
|
||||||
|
{
|
||||||
|
source: '/i-do-exist',
|
||||||
|
destination: '/',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const typeName of Object.keys(spec)) {
|
for (const typeName of Object.keys(spec)) {
|
||||||
|
|||||||
152
packages/now-cli/test/integration.js
vendored
152
packages/now-cli/test/integration.js
vendored
@@ -213,6 +213,18 @@ test.before(async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.after.always(async () => {
|
||||||
|
// Make sure the token gets revoked
|
||||||
|
await execa(binaryPath, ['logout', ...defaultArgs]);
|
||||||
|
|
||||||
|
if (!tmpDir) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove config directory entirely
|
||||||
|
tmpDir.removeCallback();
|
||||||
|
});
|
||||||
|
|
||||||
test('login', async t => {
|
test('login', async t => {
|
||||||
// Delete the current token
|
// Delete the current token
|
||||||
const logoutOutput = await execute(['logout']);
|
const logoutOutput = await execute(['logout']);
|
||||||
@@ -2077,6 +2089,11 @@ test('should show prompts to set up project', async t => {
|
|||||||
);
|
);
|
||||||
now.stdin.write(`${projectName}\n`);
|
now.stdin.write(`${projectName}\n`);
|
||||||
|
|
||||||
|
await waitForPrompt(now, chunk =>
|
||||||
|
chunk.includes('In which directory is your code located?')
|
||||||
|
);
|
||||||
|
now.stdin.write('\n');
|
||||||
|
|
||||||
await waitForPrompt(now, chunk =>
|
await waitForPrompt(now, chunk =>
|
||||||
chunk.includes('Want to override the settings?')
|
chunk.includes('Want to override the settings?')
|
||||||
);
|
);
|
||||||
@@ -2132,49 +2149,6 @@ test('should show prompts to set up project', async t => {
|
|||||||
t.is(text.includes('<h1>custom hello</h1>'), true, text);
|
t.is(text.includes('<h1>custom hello</h1>'), true, text);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not prompt "project settings overwrite" for undetected projects', async t => {
|
|
||||||
const directory = fixture('static-deployment');
|
|
||||||
const projectName = `static-deployment-${
|
|
||||||
Math.random()
|
|
||||||
.toString(36)
|
|
||||||
.split('.')[1]
|
|
||||||
}`;
|
|
||||||
|
|
||||||
// remove previously linked project if it exists
|
|
||||||
await remove(path.join(directory, '.now'));
|
|
||||||
|
|
||||||
const now = execa(binaryPath, [directory, ...defaultArgs]);
|
|
||||||
|
|
||||||
await waitForPrompt(now, chunk => /Set up and deploy [^?]+\?/.test(chunk));
|
|
||||||
now.stdin.write('yes\n');
|
|
||||||
|
|
||||||
await waitForPrompt(now, chunk =>
|
|
||||||
chunk.includes('Which scope do you want to deploy to?')
|
|
||||||
);
|
|
||||||
now.stdin.write('\n');
|
|
||||||
|
|
||||||
await waitForPrompt(now, chunk =>
|
|
||||||
chunk.includes('Link to existing project?')
|
|
||||||
);
|
|
||||||
now.stdin.write('no\n');
|
|
||||||
|
|
||||||
await waitForPrompt(now, chunk =>
|
|
||||||
chunk.includes('What’s your project’s name?')
|
|
||||||
);
|
|
||||||
now.stdin.write(`${projectName}\n`);
|
|
||||||
|
|
||||||
await waitForPrompt(now, chunk => {
|
|
||||||
t.false(
|
|
||||||
chunk.includes('Want to override the settings?'),
|
|
||||||
'Should not ask to override'
|
|
||||||
);
|
|
||||||
return chunk.includes('Linked to');
|
|
||||||
});
|
|
||||||
|
|
||||||
const output = await now;
|
|
||||||
t.is(output.exitCode, 0, formatOutput(output));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should prefill "project name" prompt with folder name', async t => {
|
test('should prefill "project name" prompt with folder name', async t => {
|
||||||
const projectName = `static-deployment-${
|
const projectName = `static-deployment-${
|
||||||
Math.random()
|
Math.random()
|
||||||
@@ -2210,6 +2184,16 @@ test('should prefill "project name" prompt with folder name', async t => {
|
|||||||
);
|
);
|
||||||
now.stdin.write(`\n`);
|
now.stdin.write(`\n`);
|
||||||
|
|
||||||
|
await waitForPrompt(now, chunk =>
|
||||||
|
chunk.includes('In which directory is your code located?')
|
||||||
|
);
|
||||||
|
now.stdin.write('\n');
|
||||||
|
|
||||||
|
await waitForPrompt(now, chunk =>
|
||||||
|
chunk.includes('Want to override the settings?')
|
||||||
|
);
|
||||||
|
now.stdin.write('no\n');
|
||||||
|
|
||||||
const output = await now;
|
const output = await now;
|
||||||
t.is(output.exitCode, 0, formatOutput(output));
|
t.is(output.exitCode, 0, formatOutput(output));
|
||||||
});
|
});
|
||||||
@@ -2260,6 +2244,16 @@ test('should prefill "project name" prompt with --name', async t => {
|
|||||||
);
|
);
|
||||||
now.stdin.write(`\n`);
|
now.stdin.write(`\n`);
|
||||||
|
|
||||||
|
await waitForPrompt(now, chunk =>
|
||||||
|
chunk.includes('In which directory is your code located?')
|
||||||
|
);
|
||||||
|
now.stdin.write('\n');
|
||||||
|
|
||||||
|
await waitForPrompt(now, chunk =>
|
||||||
|
chunk.includes('Want to override the settings?')
|
||||||
|
);
|
||||||
|
now.stdin.write('no\n');
|
||||||
|
|
||||||
const output = await now;
|
const output = await now;
|
||||||
t.is(output.exitCode, 0, formatOutput(output));
|
t.is(output.exitCode, 0, formatOutput(output));
|
||||||
});
|
});
|
||||||
@@ -2285,17 +2279,19 @@ test('should prefill "project name" prompt with now.json `name`', async t => {
|
|||||||
|
|
||||||
let isDeprecated = false;
|
let isDeprecated = false;
|
||||||
|
|
||||||
await waitForPrompt(now, chunk => {
|
now.stderr.on('data', data => {
|
||||||
if (chunk.includes('The `name` property in now.json is deprecated')) {
|
if (
|
||||||
|
data.toString().includes('The `name` property in now.json is deprecated')
|
||||||
|
) {
|
||||||
isDeprecated = true;
|
isDeprecated = true;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitForPrompt(now, chunk => {
|
||||||
return /Set up and deploy [^?]+\?/.test(chunk);
|
return /Set up and deploy [^?]+\?/.test(chunk);
|
||||||
});
|
});
|
||||||
now.stdin.write('yes\n');
|
now.stdin.write('yes\n');
|
||||||
|
|
||||||
t.is(isDeprecated, true);
|
|
||||||
|
|
||||||
await waitForPrompt(now, chunk =>
|
await waitForPrompt(now, chunk =>
|
||||||
chunk.includes('Which scope do you want to deploy to?')
|
chunk.includes('Which scope do you want to deploy to?')
|
||||||
);
|
);
|
||||||
@@ -2311,9 +2307,21 @@ test('should prefill "project name" prompt with now.json `name`', async t => {
|
|||||||
);
|
);
|
||||||
now.stdin.write(`\n`);
|
now.stdin.write(`\n`);
|
||||||
|
|
||||||
|
await waitForPrompt(now, chunk =>
|
||||||
|
chunk.includes('In which directory is your code located?')
|
||||||
|
);
|
||||||
|
now.stdin.write('\n');
|
||||||
|
|
||||||
|
await waitForPrompt(now, chunk =>
|
||||||
|
chunk.includes('Want to override the settings?')
|
||||||
|
);
|
||||||
|
now.stdin.write('no\n');
|
||||||
|
|
||||||
const output = await now;
|
const output = await now;
|
||||||
t.is(output.exitCode, 0, formatOutput(output));
|
t.is(output.exitCode, 0, formatOutput(output));
|
||||||
|
|
||||||
|
t.is(isDeprecated, true);
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
await remove(path.join(directory, 'now.json'));
|
await remove(path.join(directory, 'now.json'));
|
||||||
});
|
});
|
||||||
@@ -2420,6 +2428,44 @@ test('deploy shows notice when project in `.now` does not exists', async t => {
|
|||||||
t.is(detectedNotice, true, 'did not detect notice');
|
t.is(detectedNotice, true, 'did not detect notice');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('use `rootDirectory` from project when deploying', async t => {
|
||||||
|
const directory = fixture('project-root-directory');
|
||||||
|
|
||||||
|
const firstResult = await execute([directory, '--confirm', '--public']);
|
||||||
|
t.is(firstResult.exitCode, 0, formatOutput(firstResult));
|
||||||
|
|
||||||
|
const { host: firstHost } = new URL(firstResult.stdout);
|
||||||
|
const response = await apiFetch(`/v12/now/deployments/get?url=${firstHost}`);
|
||||||
|
t.is(response.status, 200);
|
||||||
|
const { projectId } = await response.json();
|
||||||
|
t.is(typeof projectId, 'string', projectId);
|
||||||
|
|
||||||
|
const projectResponse = await apiFetch(`/v2/projects/${projectId}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
body: JSON.stringify({
|
||||||
|
rootDirectory: 'src',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
console.log('response', await projectResponse.text());
|
||||||
|
t.is(projectResponse.status, 200);
|
||||||
|
|
||||||
|
const secondResult = await execute([directory, '--public']);
|
||||||
|
t.is(secondResult.exitCode, 0, formatOutput(secondResult));
|
||||||
|
|
||||||
|
const pageResponse1 = await fetch(secondResult.stdout);
|
||||||
|
t.is(pageResponse1.status, 200);
|
||||||
|
t.regex(await pageResponse1.text(), /I am a website/gm);
|
||||||
|
|
||||||
|
// Ensures that the `now.json` file has been applied
|
||||||
|
const pageResponse2 = await fetch(`${secondResult.stdout}/i-do-exist`);
|
||||||
|
t.is(pageResponse2.status, 200);
|
||||||
|
t.regex(await pageResponse2.text(), /I am a website/gm);
|
||||||
|
|
||||||
|
await apiFetch(`/v2/projects/${projectId}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('whoami with unknown `NOW_ORG_ID` should error', async t => {
|
test('whoami with unknown `NOW_ORG_ID` should error', async t => {
|
||||||
const output = await execute(['whoami'], {
|
const output = await execute(['whoami'], {
|
||||||
env: { NOW_ORG_ID: 'asdf' },
|
env: { NOW_ORG_ID: 'asdf' },
|
||||||
@@ -2465,15 +2511,3 @@ test('whoami with local .now scope', async t => {
|
|||||||
// clean up
|
// clean up
|
||||||
await remove(path.join(directory, '.now'));
|
await remove(path.join(directory, '.now'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test.after.always(async () => {
|
|
||||||
// Make sure the token gets revoked
|
|
||||||
await execa(binaryPath, ['logout', ...defaultArgs]);
|
|
||||||
|
|
||||||
if (!tmpDir) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove config directory entirely
|
|
||||||
tmpDir.removeCallback();
|
|
||||||
});
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user