mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-24 11:49:13 +00:00
Compare commits
77 Commits
@now/cgi@1
...
@now/node@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8043e25d6d | ||
|
|
73b4e7aea4 | ||
|
|
de7e063c9b | ||
|
|
e7c30deee8 | ||
|
|
7464fac792 | ||
|
|
252363cce4 | ||
|
|
d7dceeb2a1 | ||
|
|
6b5b9e8b2f | ||
|
|
addd036149 | ||
|
|
7ca672a470 | ||
|
|
3af0e99689 | ||
|
|
b14068de8a | ||
|
|
c9437e714a | ||
|
|
c0aff5cf4a | ||
|
|
9110b14bb4 | ||
|
|
40e4b69267 | ||
|
|
6c4934dcc4 | ||
|
|
d64e54d61a | ||
|
|
0b75bf07f7 | ||
|
|
a861a8f3f7 | ||
|
|
6407b17b7f | ||
|
|
558463a988 | ||
|
|
1a133995b8 | ||
|
|
a83eecf674 | ||
|
|
a932ed65fa | ||
|
|
e2ae497762 | ||
|
|
89989719c2 | ||
|
|
8166b8e1e7 | ||
|
|
1ceeac498c | ||
|
|
1c47d1360d | ||
|
|
ddcd0918e9 | ||
|
|
573b6b8110 | ||
|
|
40039d7f9b | ||
|
|
dcb37e92f5 | ||
|
|
fe7f875549 | ||
|
|
a516ed6fb8 | ||
|
|
ca2c5f85ef | ||
|
|
adb5a01cc0 | ||
|
|
6b4d39ab4d | ||
|
|
07ce3d2e34 | ||
|
|
93ffcf487b | ||
|
|
3631f0f4cf | ||
|
|
b67b5be8a9 | ||
|
|
bf67b1a29e | ||
|
|
ed86473f74 | ||
|
|
399a3cd114 | ||
|
|
d0fd09810a | ||
|
|
f298f2e894 | ||
|
|
569200ae0e | ||
|
|
c91495338d | ||
|
|
7eed5574e0 | ||
|
|
91e6b85cec | ||
|
|
3ae83172ec | ||
|
|
7c51446e5e | ||
|
|
400a5c73e8 | ||
|
|
ec917ace69 | ||
|
|
f5e0afdd7e | ||
|
|
c1b4c62714 | ||
|
|
5e4bdfbe11 | ||
|
|
bd4a0cbd32 | ||
|
|
7ff9adc90e | ||
|
|
b279f1ffae | ||
|
|
344cc103ee | ||
|
|
83249b3685 | ||
|
|
79e7a9f477 | ||
|
|
b3dce70271 | ||
|
|
cff8d8b8a0 | ||
|
|
da892100d9 | ||
|
|
72e87ee6e4 | ||
|
|
3f9afad167 | ||
|
|
1527447914 | ||
|
|
9ca35df5fb | ||
|
|
05b2e2216c | ||
|
|
167fd5750a | ||
|
|
4a3cd7ec72 | ||
|
|
9aef718917 | ||
|
|
3cdc261802 |
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -8,6 +8,7 @@
|
||||
/packages/now-cli/src/util/dev/ @tootallnate @leo @styfle @AndyBitz
|
||||
/packages/now-cli/src/commands/domains/ @javivelasco @mglagola @anatrajkovska
|
||||
/packages/now-cli/src/commands/certs/ @javivelasco @mglagola @anatrajkovska
|
||||
/packages/now-cli/src/commands/env @styfle @lucleray
|
||||
/packages/now-client @leo @rdev
|
||||
/packages/now-build-utils @styfle @AndyBitz
|
||||
/packages/now-node @styfle @tootallnate @lucleray
|
||||
|
||||
6
.github/workflows/cancel.yml
vendored
6
.github/workflows/cancel.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: Cancel
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
branches:
|
||||
- '*'
|
||||
- '!master'
|
||||
|
||||
@@ -11,8 +11,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 3
|
||||
steps:
|
||||
- uses: styfle/cancel-workflow-action@0.2.0
|
||||
- uses: styfle/cancel-workflow-action@0.3.1
|
||||
with:
|
||||
workflow_id: 435869
|
||||
workflow_id: 849295, 849296, 849297, 849298
|
||||
access_token: ${{ secrets.GITHUB_WORKFLOW_TOKEN }}
|
||||
|
||||
|
||||
117
.github/workflows/continuous-integration.yml
vendored
117
.github/workflows/continuous-integration.yml
vendored
@@ -1,117 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '!*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test-unit:
|
||||
name: Unit Tests
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
node: [10, 12]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch origin master --depth=10
|
||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
||||
- uses: actions/setup-node@v1
|
||||
- run: yarn install
|
||||
- run: yarn run build
|
||||
- run: yarn run test-lint
|
||||
- run: yarn run test-unit --clean false
|
||||
- name: Upload Artifact
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.node == 12 # only run once
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: test-unit-output
|
||||
path: packages/now-cli/.nyc_output
|
||||
|
||||
test-integration:
|
||||
name: Integration Tests
|
||||
timeout-minutes: 120
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch origin master --depth=10
|
||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
||||
- run: yarn install
|
||||
- run: yarn run build
|
||||
- run: yarn test-integration-once --clean false
|
||||
env:
|
||||
ZEIT_TEAM_TOKEN: ${{ secrets.ZEIT_TEAM_TOKEN }}
|
||||
ZEIT_REGISTRATION_URL: ${{ secrets.ZEIT_REGISTRATION_URL }}
|
||||
|
||||
test-now-cli:
|
||||
name: Now CLI Tests
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node: [10, 12]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch origin master --depth=10
|
||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
||||
- name: Install Hugo
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.56.0/hugo_0.56.0_macOS-64bit.tar.gz && tar -xzf hugo_0.56.0_macOS-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
||||
- run: yarn install
|
||||
- run: yarn run build
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: yarn test-integration --clean false
|
||||
env:
|
||||
ZEIT_TEAM_TOKEN: ${{ secrets.ZEIT_TEAM_TOKEN }}
|
||||
ZEIT_REGISTRATION_URL: ${{ secrets.ZEIT_REGISTRATION_URL }}
|
||||
|
||||
test-now-dev:
|
||||
name: "`now dev` Tests"
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
node: [10, 12]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch origin master --depth=10
|
||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
||||
- name: Install Hugo
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.56.0/hugo_0.56.0_macOS-64bit.tar.gz && tar -xzf hugo_0.56.0_macOS-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
||||
- run: yarn install
|
||||
- run: yarn run build
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: yarn test-integration-now-dev --clean false
|
||||
|
||||
coverage:
|
||||
name: Coverage
|
||||
timeout-minutes: 5
|
||||
needs: [test-unit, test-now-cli, test-now-dev, test-integration]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch origin master --depth=10
|
||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
||||
- uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: test-unit-output
|
||||
path: packages/now-cli/.nyc_output
|
||||
- run: yarn install
|
||||
- run: yarn workspace now run coverage
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
36
.github/workflows/test-integration-cli.yml
vendored
Normal file
36
.github/workflows/test-integration-cli.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: CLI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '!*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: CLI
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node: [10, 12]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch origin master --depth=10
|
||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
||||
- name: Install Hugo
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.56.0/hugo_0.56.0_macOS-64bit.tar.gz && tar -xzf hugo_0.56.0_macOS-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
||||
- run: yarn install
|
||||
- run: yarn run build
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: yarn test-integration-cli --clean false
|
||||
env:
|
||||
ZEIT_TEAM_TOKEN: ${{ secrets.ZEIT_TEAM_TOKEN }}
|
||||
ZEIT_REGISTRATION_URL: ${{ secrets.ZEIT_REGISTRATION_URL }}
|
||||
33
.github/workflows/test-integration-dev.yml
vendored
Normal file
33
.github/workflows/test-integration-dev.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Dev
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '!*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Dev
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
node: [10, 12]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch origin master --depth=10
|
||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
||||
- name: Install Hugo
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.56.0/hugo_0.56.0_macOS-64bit.tar.gz && tar -xzf hugo_0.56.0_macOS-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/
|
||||
- run: yarn install
|
||||
- run: yarn run build
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: yarn test-integration-dev --clean false
|
||||
25
.github/workflows/test-integration-once.yml
vendored
Normal file
25
.github/workflows/test-integration-once.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: E2E
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '!*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: E2E
|
||||
timeout-minutes: 120
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch origin master --depth=10
|
||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
||||
- run: yarn install
|
||||
- run: yarn run build
|
||||
- run: yarn test-integration-once --clean false
|
||||
env:
|
||||
ZEIT_TEAM_TOKEN: ${{ secrets.ZEIT_TEAM_TOKEN }}
|
||||
ZEIT_REGISTRATION_URL: ${{ secrets.ZEIT_REGISTRATION_URL }}
|
||||
33
.github/workflows/test-unit.yml
vendored
Normal file
33
.github/workflows/test-unit.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Unit
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '!*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Unit
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
node: [10, 12]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch origin master --depth=10
|
||||
- run: git fetch origin ${{ github.ref }} --depth=10
|
||||
- uses: actions/setup-node@v1
|
||||
- run: yarn install
|
||||
- run: yarn run build
|
||||
- run: yarn run test-lint
|
||||
- run: yarn run test-unit --clean false
|
||||
- run: yarn workspace now run coverage
|
||||
if: matrix.os == 'ubuntu-latest' && matrix.node == 12 # only run once
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
8
codecov.yml
Normal file
8
codecov.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
codecov:
|
||||
require_ci_to_pass: yes
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project: off
|
||||
patch: off
|
||||
|
||||
6
examples/dojo/.dojorc
Normal file
6
examples/dojo/.dojorc
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"build-app": {},
|
||||
"test-intern": {},
|
||||
"create-app": {},
|
||||
"create-widget": {}
|
||||
}
|
||||
4
examples/dojo/.gitignore
vendored
Normal file
4
examples/dojo/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
_build/
|
||||
output/
|
||||
.cert/
|
||||
25
examples/dojo/README.md
Normal file
25
examples/dojo/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Dojo Example
|
||||
|
||||
This directory is a brief example of a [Dojo](https://dojo.io) site that can be deployed with ZEIT Now and zero configuration.
|
||||
|
||||
## Deploy Your Own
|
||||
|
||||
Deploy your own Dojo project with ZEIT Now.
|
||||
|
||||
[](https://zeit.co/import/project?template=https://github.com/zeit/now-examples/tree/master/dojo)
|
||||
|
||||
### How We Created This Example
|
||||
|
||||
To get started with Dojo on Now, you can use the [Dojo CLI](https://github.com/dojo/cli) to initialize the project:
|
||||
|
||||
```shell
|
||||
$ now init dojo
|
||||
```
|
||||
|
||||
### Deploying From Your Terminal
|
||||
|
||||
Once initialized, you can deploy the Dojo example with just a single command:
|
||||
|
||||
```shell
|
||||
$ now
|
||||
```
|
||||
26
examples/dojo/package.json
Normal file
26
examples/dojo/package.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "dojo",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "dojo build --mode dev --watch --serve",
|
||||
"build": "dojo build --mode dist",
|
||||
"build:dev": "dojo build --mode dev",
|
||||
"test": "dojo test",
|
||||
"test:unit": "dojo build --mode unit && dojo test --unit --config local",
|
||||
"test:functional": "dojo build --mode functional && dojo test --functional --config local",
|
||||
"test:all": "dojo build --mode unit && dojo build --mode functional && dojo test --all --config local"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dojo/framework": "^6.0.0",
|
||||
"@dojo/themes": "^6.0.0",
|
||||
"@dojo/widgets": "^6.0.0",
|
||||
"tslib": "~1.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dojo/cli": "^6.0.0",
|
||||
"@dojo/cli-build-app": "^6.0.0",
|
||||
"@dojo/cli-test-intern": "^6.0.0",
|
||||
"@types/node": "~9.6.5",
|
||||
"typescript": "~3.4.5"
|
||||
}
|
||||
}
|
||||
3
examples/dojo/src/App.m.css
Normal file
3
examples/dojo/src/App.m.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.root {
|
||||
|
||||
}
|
||||
1
examples/dojo/src/App.m.css.d.ts
vendored
Normal file
1
examples/dojo/src/App.m.css.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export const root: string;
|
||||
27
examples/dojo/src/App.ts
Normal file
27
examples/dojo/src/App.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { create, v, w } from '@dojo/framework/core/vdom';
|
||||
import theme from '@dojo/framework/core/middleware/theme';
|
||||
import Outlet from '@dojo/framework/routing/Outlet';
|
||||
import dojo from '@dojo/themes/dojo';
|
||||
|
||||
import Menu from './widgets/Menu';
|
||||
import Home from './widgets/Home';
|
||||
import About from './widgets/About';
|
||||
import Profile from './widgets/Profile';
|
||||
|
||||
import * as css from './App.m.css';
|
||||
|
||||
const factory = create({ theme });
|
||||
|
||||
export default factory(function App({ middleware: { theme } }) {
|
||||
if (!theme.get()) {
|
||||
theme.set(dojo);
|
||||
}
|
||||
return v('div', { classes: [css.root] }, [
|
||||
w(Menu, {}),
|
||||
v('div', [
|
||||
w(Outlet, { key: 'home', id: 'home', renderer: () => w(Home, {}) }),
|
||||
w(Outlet, { key: 'about', id: 'about', renderer: () => w(About, {}) }),
|
||||
w(Outlet, { key: 'profile', id: 'profile', renderer: () => w(Profile, { username: 'Dojo User' }) })
|
||||
])
|
||||
]);
|
||||
});
|
||||
11
examples/dojo/src/index.html
Normal file
11
examples/dojo/src/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-us" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>dojo</title>
|
||||
<meta name="theme-color" content="#222127">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
6
examples/dojo/src/main.css
Normal file
6
examples/dojo/src/main.css
Normal file
@@ -0,0 +1,6 @@
|
||||
/* Put your styles and imports here */
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
13
examples/dojo/src/main.ts
Normal file
13
examples/dojo/src/main.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import renderer, { w } from '@dojo/framework/core/vdom';
|
||||
import Registry from '@dojo/framework/core/Registry';
|
||||
import { registerRouterInjector } from '@dojo/framework/routing/RouterInjector';
|
||||
import '@dojo/themes/dojo/index.css';
|
||||
|
||||
import routes from './routes';
|
||||
import App from './App';
|
||||
|
||||
const registry = new Registry();
|
||||
registerRouterInjector(routes, registry);
|
||||
|
||||
const r = renderer(() => w(App, {}));
|
||||
r.mount({ registry });
|
||||
15
examples/dojo/src/routes.ts
Normal file
15
examples/dojo/src/routes.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export default [
|
||||
{
|
||||
path: 'home',
|
||||
outlet: 'home',
|
||||
defaultRoute: true
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
outlet: 'about'
|
||||
},
|
||||
{
|
||||
path: 'profile',
|
||||
outlet: 'profile'
|
||||
}
|
||||
];
|
||||
9
examples/dojo/src/widgets/About.ts
Normal file
9
examples/dojo/src/widgets/About.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { v, create } from '@dojo/framework/core/vdom';
|
||||
|
||||
import * as css from './styles/About.m.css';
|
||||
|
||||
const factory = create();
|
||||
|
||||
export default factory(function Profile() {
|
||||
return v('h1', { classes: [css.root] }, ['About Page']);
|
||||
});
|
||||
9
examples/dojo/src/widgets/Home.ts
Normal file
9
examples/dojo/src/widgets/Home.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { v, create } from '@dojo/framework/core/vdom';
|
||||
|
||||
import * as css from './styles/Home.m.css';
|
||||
|
||||
const factory = create();
|
||||
|
||||
export default factory(function Profile() {
|
||||
return v('h1', { classes: [css.root] }, ['Home Page']);
|
||||
});
|
||||
39
examples/dojo/src/widgets/Menu.ts
Normal file
39
examples/dojo/src/widgets/Menu.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { create, w } from '@dojo/framework/core/vdom';
|
||||
import Link from '@dojo/framework/routing/ActiveLink';
|
||||
import Toolbar from '@dojo/widgets/toolbar';
|
||||
|
||||
import * as css from './styles/Menu.m.css';
|
||||
|
||||
const factory = create();
|
||||
|
||||
export default factory(function Menu() {
|
||||
return w(Toolbar, { heading: 'My Dojo App!', collapseWidth: 600 }, [
|
||||
w(
|
||||
Link,
|
||||
{
|
||||
to: 'home',
|
||||
classes: [css.link],
|
||||
activeClasses: [css.selected]
|
||||
},
|
||||
['Home']
|
||||
),
|
||||
w(
|
||||
Link,
|
||||
{
|
||||
to: 'about',
|
||||
classes: [css.link],
|
||||
activeClasses: [css.selected]
|
||||
},
|
||||
['About']
|
||||
),
|
||||
w(
|
||||
Link,
|
||||
{
|
||||
to: 'profile',
|
||||
classes: [css.link],
|
||||
activeClasses: [css.selected]
|
||||
},
|
||||
['Profile']
|
||||
)
|
||||
]);
|
||||
});
|
||||
14
examples/dojo/src/widgets/Profile.ts
Normal file
14
examples/dojo/src/widgets/Profile.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { v, create } from '@dojo/framework/core/vdom';
|
||||
|
||||
import * as css from './styles/Profile.m.css';
|
||||
|
||||
export interface ProfileProperties {
|
||||
username: string;
|
||||
}
|
||||
|
||||
const factory = create().properties<ProfileProperties>();
|
||||
|
||||
export default factory(function Profile({ properties }) {
|
||||
const { username } = properties();
|
||||
return v('h1', { classes: [css.root] }, [`Welcome ${username}!`]);
|
||||
});
|
||||
3
examples/dojo/src/widgets/styles/About.m.css
Normal file
3
examples/dojo/src/widgets/styles/About.m.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.root {
|
||||
|
||||
}
|
||||
1
examples/dojo/src/widgets/styles/About.m.css.d.ts
vendored
Normal file
1
examples/dojo/src/widgets/styles/About.m.css.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export const root: string;
|
||||
3
examples/dojo/src/widgets/styles/Home.m.css
Normal file
3
examples/dojo/src/widgets/styles/Home.m.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.root {
|
||||
|
||||
}
|
||||
1
examples/dojo/src/widgets/styles/Home.m.css.d.ts
vendored
Normal file
1
examples/dojo/src/widgets/styles/Home.m.css.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export const root: string;
|
||||
23
examples/dojo/src/widgets/styles/Menu.m.css
Normal file
23
examples/dojo/src/widgets/styles/Menu.m.css
Normal file
@@ -0,0 +1,23 @@
|
||||
.root {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
color: #4db3ff;
|
||||
background-color:#ccddee;
|
||||
}
|
||||
|
||||
.link {
|
||||
min-width: 140px;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
padding: 18px;
|
||||
color: black;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.selected {
|
||||
color: darkorange;
|
||||
}
|
||||
3
examples/dojo/src/widgets/styles/Menu.m.css.d.ts
vendored
Normal file
3
examples/dojo/src/widgets/styles/Menu.m.css.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export const root: string;
|
||||
export const link: string;
|
||||
export const selected: string;
|
||||
3
examples/dojo/src/widgets/styles/Profile.m.css
Normal file
3
examples/dojo/src/widgets/styles/Profile.m.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.root {
|
||||
|
||||
}
|
||||
1
examples/dojo/src/widgets/styles/Profile.m.css.d.ts
vendored
Normal file
1
examples/dojo/src/widgets/styles/Profile.m.css.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export const root: string;
|
||||
1
examples/dojo/tests/functional/all.ts
Normal file
1
examples/dojo/tests/functional/all.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './main';
|
||||
1
examples/dojo/tests/functional/main.ts
Normal file
1
examples/dojo/tests/functional/main.ts
Normal file
@@ -0,0 +1 @@
|
||||
/* Write your app tests here */
|
||||
46
examples/dojo/tests/unit/App.ts
Normal file
46
examples/dojo/tests/unit/App.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
const { describe, it } = intern.getInterface('bdd');
|
||||
import harness from '@dojo/framework/testing/harness';
|
||||
import { v, w } from '@dojo/framework/core/vdom';
|
||||
import Outlet from '@dojo/framework/routing/Outlet';
|
||||
|
||||
import Menu from '../../src/widgets/Menu';
|
||||
import Home from '../../src/widgets/Home';
|
||||
import About from '../../src/widgets/About';
|
||||
import Profile from '../../src/widgets/Profile';
|
||||
|
||||
import App from '../../src/App';
|
||||
import * as css from '../../src/App.m.css';
|
||||
|
||||
describe('App', () => {
|
||||
it('default renders correctly', () => {
|
||||
const h = harness(() => w(App, {}));
|
||||
h.expect(() =>
|
||||
v('div', { classes: [css.root] }, [
|
||||
w(Menu, {}),
|
||||
v('div', [
|
||||
w(Outlet, { key: 'home', id: 'home', renderer: () => w(Home, {}) }),
|
||||
w(Outlet, { key: 'about', id: 'about', renderer: () => w(About, {}) }),
|
||||
w(Outlet, { key: 'profile', id: 'profile', renderer: () => w(Profile, { username: 'Dojo User' }) })
|
||||
])
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
it('home outlet renderer', () => {
|
||||
const h = harness(() => w(App, {}));
|
||||
const renderer = h.trigger('@home', 'renderer');
|
||||
h.expect(() => w(Home, {}), () => renderer);
|
||||
});
|
||||
|
||||
it('about outlet renderer', () => {
|
||||
const h = harness(() => w(App, {}));
|
||||
const renderer = h.trigger('@about', 'renderer');
|
||||
h.expect(() => w(About, {}), () => renderer);
|
||||
});
|
||||
|
||||
it('profile outlet renderer', () => {
|
||||
const h = harness(() => w(App, {}));
|
||||
const renderer = h.trigger('@profile', 'renderer');
|
||||
h.expect(() => w(Profile, { username: 'Dojo User' }), () => renderer);
|
||||
});
|
||||
});
|
||||
2
examples/dojo/tests/unit/all.ts
Normal file
2
examples/dojo/tests/unit/all.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import './App';
|
||||
import './widgets/all';
|
||||
1
examples/dojo/tests/unit/main.ts
Normal file
1
examples/dojo/tests/unit/main.ts
Normal file
@@ -0,0 +1 @@
|
||||
/* Write your app tests here */
|
||||
13
examples/dojo/tests/unit/widgets/About.ts
Normal file
13
examples/dojo/tests/unit/widgets/About.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
const { describe, it } = intern.getInterface('bdd');
|
||||
import harness from '@dojo/framework/testing/harness';
|
||||
import { w, v } from '@dojo/framework/core/vdom';
|
||||
|
||||
import About from '../../../src/widgets/About';
|
||||
import * as css from '../../../src/widgets/styles/About.m.css';
|
||||
|
||||
describe('About', () => {
|
||||
it('default renders correctly', () => {
|
||||
const h = harness(() => w(About, {}));
|
||||
h.expect(() => v('h1', { classes: [css.root] }, ['About Page']));
|
||||
});
|
||||
});
|
||||
13
examples/dojo/tests/unit/widgets/Home.ts
Normal file
13
examples/dojo/tests/unit/widgets/Home.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
const { describe, it } = intern.getInterface('bdd');
|
||||
import harness from '@dojo/framework/testing/harness';
|
||||
import { w, v } from '@dojo/framework/core/vdom';
|
||||
|
||||
import Home from '../../../src/widgets/Home';
|
||||
import * as css from '../../../src/widgets/styles/Home.m.css';
|
||||
|
||||
describe('Home', () => {
|
||||
it('default renders correctly', () => {
|
||||
const h = harness(() => w(Home, {}));
|
||||
h.expect(() => v('h1', { classes: [css.root] }, ['Home Page']));
|
||||
});
|
||||
});
|
||||
45
examples/dojo/tests/unit/widgets/Menu.ts
Normal file
45
examples/dojo/tests/unit/widgets/Menu.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
const { describe, it } = intern.getInterface('bdd');
|
||||
import harness from '@dojo/framework/testing/harness';
|
||||
import { w } from '@dojo/framework/core/vdom';
|
||||
import Link from '@dojo/framework/routing/ActiveLink';
|
||||
import Toolbar from '@dojo/widgets/toolbar';
|
||||
|
||||
import Menu from '../../../src/widgets/Menu';
|
||||
import * as css from '../../../src/widgets/styles/Menu.m.css';
|
||||
|
||||
describe('Menu', () => {
|
||||
it('default renders correctly', () => {
|
||||
const h = harness(() => w(Menu, {}));
|
||||
h.expect(() =>
|
||||
w(Toolbar, { heading: 'My Dojo App!', collapseWidth: 600 }, [
|
||||
w(
|
||||
Link,
|
||||
{
|
||||
to: 'home',
|
||||
classes: [css.link],
|
||||
activeClasses: [css.selected]
|
||||
},
|
||||
['Home']
|
||||
),
|
||||
w(
|
||||
Link,
|
||||
{
|
||||
to: 'about',
|
||||
classes: [css.link],
|
||||
activeClasses: [css.selected]
|
||||
},
|
||||
['About']
|
||||
),
|
||||
w(
|
||||
Link,
|
||||
{
|
||||
to: 'profile',
|
||||
classes: [css.link],
|
||||
activeClasses: [css.selected]
|
||||
},
|
||||
['Profile']
|
||||
)
|
||||
])
|
||||
);
|
||||
});
|
||||
});
|
||||
13
examples/dojo/tests/unit/widgets/Profile.ts
Normal file
13
examples/dojo/tests/unit/widgets/Profile.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
const { describe, it } = intern.getInterface('bdd');
|
||||
import harness from '@dojo/framework/testing/harness';
|
||||
import { w, v } from '@dojo/framework/core/vdom';
|
||||
|
||||
import Profile from '../../../src/widgets/Profile';
|
||||
import * as css from '../../../src/widgets/styles/Profile.m.css';
|
||||
|
||||
describe('Profile', () => {
|
||||
it('default renders correctly', () => {
|
||||
const h = harness(() => w(Profile, { username: 'Dojo User' }));
|
||||
h.expect(() => v('h1', { classes: [css.root] }, ['Welcome Dojo User!']));
|
||||
});
|
||||
});
|
||||
4
examples/dojo/tests/unit/widgets/all.ts
Normal file
4
examples/dojo/tests/unit/widgets/all.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import './About';
|
||||
import './Home';
|
||||
import './Profile';
|
||||
import './Menu';
|
||||
31
examples/dojo/tsconfig.json
Normal file
31
examples/dojo/tsconfig.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"declaration": false,
|
||||
"experimentalDecorators": true,
|
||||
"jsx": "react",
|
||||
"jsxFactory": "tsx",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es5",
|
||||
"es2015.promise",
|
||||
"es2015.iterable",
|
||||
"es2015.symbol",
|
||||
"es2015.symbol.wellknown"
|
||||
],
|
||||
"module": "umd",
|
||||
"moduleResolution": "node",
|
||||
"noUnusedLocals": true,
|
||||
"outDir": "_build/",
|
||||
"removeComments": false,
|
||||
"importHelpers": true,
|
||||
"downlevelIteration": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"target": "es5",
|
||||
"types": [ "intern" ]
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*.ts",
|
||||
"./tests/**/*.ts"
|
||||
]
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
"react-router": "^5.1.2",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"react-scripts": "3.3.0",
|
||||
"typescript": "3.7.4"
|
||||
"typescript": "3.8.3"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "9.2.2",
|
||||
"react": "16.13.0",
|
||||
"react-dom": "16.13.0"
|
||||
"next": "^9.3.3",
|
||||
"react": "^16.13.0",
|
||||
"react-dom": "^16.13.0"
|
||||
}
|
||||
}
|
||||
|
||||
4
now.json
4
now.json
@@ -9,10 +9,6 @@
|
||||
"destination": "/api/frameworks"
|
||||
}
|
||||
],
|
||||
"env": {
|
||||
"GITHUB_ACCESS_TOKEN": "@now-api-examples-github-token",
|
||||
"SENTRY_DSN": "@sentry-product-dsn"
|
||||
},
|
||||
"github": {
|
||||
"silent": true,
|
||||
"autoJobCancelation": true
|
||||
|
||||
@@ -38,9 +38,9 @@
|
||||
"build": "node utils/run.js build all",
|
||||
"test-lint": "node utils/run.js test-lint",
|
||||
"test-unit": "node utils/run.js test-unit",
|
||||
"test-integration": "node utils/run.js test-integration",
|
||||
"test-integration-cli": "node utils/run.js test-integration-cli",
|
||||
"test-integration-once": "node utils/run.js test-integration-once",
|
||||
"test-integration-now-dev": "node utils/run.js test-integration-now-dev",
|
||||
"test-integration-dev": "node utils/run.js test-integration-dev",
|
||||
"lint": "eslint . --ext .ts,.js"
|
||||
},
|
||||
"lint-staged": {
|
||||
@@ -59,9 +59,6 @@
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"resolutions": {
|
||||
"signal-exit": "TooTallNate/signal-exit#update/sighub-to-sigint-on-windows"
|
||||
},
|
||||
"prettier": {
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true
|
||||
|
||||
@@ -112,7 +112,35 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Docusaurus",
|
||||
"name": "Docusaurus 2",
|
||||
"slug": "docusaurus-2",
|
||||
"demo": "https://docusaurus.now-examples.now.sh",
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/docusaurus.svg",
|
||||
"tagline": "Docusaurus makes it easy to maintain Open Source documentation websites.",
|
||||
"description": "A static Docusaurus site that makes it easy to maintain OSS documentation.",
|
||||
"website": "https://v2.docusaurus.io",
|
||||
"detectors": {
|
||||
"some": [
|
||||
{
|
||||
"path": "package.json",
|
||||
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@docusaurus\\/core\":\\s*\".+?\"[^}]*}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `docusaurus build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "docusaurus start --port $PORT"
|
||||
},
|
||||
"outputDirectory": {
|
||||
"value": "build"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Docusaurus 1",
|
||||
"slug": "docusaurus",
|
||||
"demo": "https://docusaurus.now-examples.now.sh",
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/docusaurus.svg",
|
||||
@@ -124,10 +152,6 @@
|
||||
{
|
||||
"path": "package.json",
|
||||
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"docusaurus\":\\s*\".+?\"[^}]*}"
|
||||
},
|
||||
{
|
||||
"path": "package.json",
|
||||
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@docusaurus\\/core\":\\s*\".+?\"[^}]*}"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -171,6 +195,37 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Dojo",
|
||||
"slug": "dojo",
|
||||
"demo": "https://dojo.now-examples.now.sh",
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/dojo.svg",
|
||||
"tagline": "Dojo is a modern progressive, TypeScript first framework.",
|
||||
"description": "A Dojo app, created with the Dojo CLI's cli-create-app command.",
|
||||
"website": "https://dojo.io",
|
||||
"detectors": {
|
||||
"some": [
|
||||
{
|
||||
"path": "package.json",
|
||||
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@dojo\\/framework\":\\s*\".+?\"[^}]*}"
|
||||
},
|
||||
{
|
||||
"path": ".dojorc"
|
||||
}
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `dojo build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "dojo build -m dev -w -s -p $PORT"
|
||||
},
|
||||
"outputDirectory": {
|
||||
"value": "output/dist"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Ember",
|
||||
"slug": "ember",
|
||||
@@ -254,6 +309,34 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Ionic Angular",
|
||||
"slug": "ionic-angular",
|
||||
"demo": "https://ionic-angular.now-examples.now.sh",
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/ionic.svg",
|
||||
"tagline": "Ionic Angular allows you to build mobile PWAs with Angular and the Ionic Framework.",
|
||||
"description": "An Ionic Angular site, created with the Ionic CLI.",
|
||||
"website": "https://ionicframework.com",
|
||||
"detectors": {
|
||||
"every": [
|
||||
{
|
||||
"path": "package.json",
|
||||
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@ionic\\/angular\":\\s*\".+?\"[^}]*}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`npm run build` or `ng build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "ng start"
|
||||
},
|
||||
"outputDirectory": {
|
||||
"value": "www"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Angular",
|
||||
"slug": "angular",
|
||||
@@ -342,7 +425,7 @@
|
||||
"name": "Ionic React",
|
||||
"slug": "ionic-react",
|
||||
"demo": "https://ionic-react.now-examples.now.sh",
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/ionic-react.svg",
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/ionic.svg",
|
||||
"tagline": "Ionic React allows you to build mobile PWAs with React and the Ionic Framework.",
|
||||
"description": "An Ionic React site, created with the Ionic CLI.",
|
||||
"website": "https://ionicframework.com",
|
||||
@@ -356,13 +439,13 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "npm run build"
|
||||
"placeholder": "`npm run build` or `react-scripts build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "stencil build --dev --watch --serve --port $PORT"
|
||||
"value": "react-scripts start"
|
||||
},
|
||||
"outputDirectory": {
|
||||
"value": "public"
|
||||
"value": "build"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
BIN
packages/frameworks/logos/dojo.png
Normal file
BIN
packages/frameworks/logos/dojo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 1003 B After Width: | Height: | Size: 1003 B |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/frameworks",
|
||||
"version": "0.0.11",
|
||||
"version": "0.0.13",
|
||||
"main": "frameworks.json",
|
||||
"license": "UNLICENSED"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/build-utils",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.1",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -190,7 +190,12 @@ export async function detectBuilders(
|
||||
hasNextApiFiles = true;
|
||||
}
|
||||
|
||||
if (!fallbackEntrypoint && buildCommand && !fileName.includes('/')) {
|
||||
if (
|
||||
!fallbackEntrypoint &&
|
||||
buildCommand &&
|
||||
!fileName.includes('/') &&
|
||||
fileName !== 'now.json'
|
||||
) {
|
||||
fallbackEntrypoint = fileName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,18 @@ import spawn from 'cross-spawn';
|
||||
import { SpawnOptions } from 'child_process';
|
||||
import { deprecate } from 'util';
|
||||
import { cpus } from 'os';
|
||||
import { NowBuildError } from '../errors';
|
||||
import { Meta, PackageJson, NodeVersion, Config } from '../types';
|
||||
import { getSupportedNodeVersion, getLatestNodeVersion } from './node-version';
|
||||
|
||||
interface SpawnOptionsExtended extends SpawnOptions {
|
||||
prettyCommand?: string;
|
||||
}
|
||||
|
||||
export function spawnAsync(
|
||||
command: string,
|
||||
args: string[],
|
||||
opts: SpawnOptions = {}
|
||||
opts: SpawnOptionsExtended = {}
|
||||
) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const stderrLogs: Buffer[] = [];
|
||||
@@ -29,12 +34,18 @@ export function spawnAsync(
|
||||
return resolve();
|
||||
}
|
||||
|
||||
const errorLogs = stderrLogs.map(line => line.toString()).join('');
|
||||
if (opts.stdio !== 'inherit') {
|
||||
reject(new Error(`Exited with ${code || signal}\n${errorLogs}`));
|
||||
} else {
|
||||
reject(new Error(`Exited with ${code || signal}`));
|
||||
}
|
||||
const cmd = opts.prettyCommand
|
||||
? `Command "${opts.prettyCommand}"`
|
||||
: 'Command';
|
||||
reject(
|
||||
new NowBuildError({
|
||||
code: `NOW_BUILD_UTILS_SPAWN_${code || signal}`,
|
||||
message:
|
||||
opts.stdio === 'inherit'
|
||||
? `${cmd} exited with ${code || signal}`
|
||||
: stderrLogs.map(line => line.toString()).join(''),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -42,7 +53,7 @@ export function spawnAsync(
|
||||
export function execAsync(
|
||||
command: string,
|
||||
args: string[],
|
||||
opts: SpawnOptions = {}
|
||||
opts: SpawnOptionsExtended = {}
|
||||
) {
|
||||
return new Promise<{ stdout: string; stderr: string; code: number }>(
|
||||
(resolve, reject) => {
|
||||
@@ -64,10 +75,15 @@ export function execAsync(
|
||||
child.on('error', reject);
|
||||
child.on('close', (code, signal) => {
|
||||
if (code !== 0) {
|
||||
const cmd = opts.prettyCommand
|
||||
? `Command "${opts.prettyCommand}"`
|
||||
: 'Command';
|
||||
|
||||
return reject(
|
||||
new Error(
|
||||
`Program "${command}" exited with non-zero exit code ${code} ${signal}.`
|
||||
)
|
||||
new NowBuildError({
|
||||
code: `NOW_BUILD_UTILS_EXEC_${code || signal}`,
|
||||
message: `${cmd} exited with ${code || signal}`,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -82,18 +98,20 @@ export function execAsync(
|
||||
}
|
||||
|
||||
export function spawnCommand(command: string, options: SpawnOptions = {}) {
|
||||
const opts = { ...options, prettyCommand: command };
|
||||
if (process.platform === 'win32') {
|
||||
return spawn('cmd.exe', ['/C', command], options);
|
||||
return spawn('cmd.exe', ['/C', command], opts);
|
||||
}
|
||||
|
||||
return spawn('sh', ['-c', command], options);
|
||||
return spawn('sh', ['-c', command], opts);
|
||||
}
|
||||
|
||||
export async function execCommand(command: string, options: SpawnOptions = {}) {
|
||||
const opts = { ...options, prettyCommand: command };
|
||||
if (process.platform === 'win32') {
|
||||
await spawnAsync('cmd.exe', ['/C', command], options);
|
||||
await spawnAsync('cmd.exe', ['/C', command], opts);
|
||||
} else {
|
||||
await spawnAsync('sh', ['-c', command], options);
|
||||
await spawnAsync('sh', ['-c', command], opts);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -120,9 +138,11 @@ export async function runShellScript(
|
||||
assert(path.isAbsolute(fsPath));
|
||||
const destPath = path.dirname(fsPath);
|
||||
await chmodPlusX(fsPath);
|
||||
await spawnAsync(`./${path.basename(fsPath)}`, args, {
|
||||
cwd: destPath,
|
||||
const command = `./${path.basename(fsPath)}`;
|
||||
await spawnAsync(command, args, {
|
||||
...spawnOpts,
|
||||
cwd: destPath,
|
||||
prettyCommand: command,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@@ -249,7 +269,7 @@ export async function runNpmInstall(
|
||||
debug(`Installing to ${destPath}`);
|
||||
|
||||
const { hasPackageLockJson } = await scanParentDirs(destPath);
|
||||
const opts: SpawnOptions = { cwd: destPath, ...spawnOpts };
|
||||
const opts: SpawnOptionsExtended = { cwd: destPath, ...spawnOpts };
|
||||
const env = opts.env ? { ...opts.env } : { ...process.env };
|
||||
delete env.NODE_ENV;
|
||||
opts.env = env;
|
||||
@@ -258,11 +278,13 @@ export async function runNpmInstall(
|
||||
let commandArgs: string[];
|
||||
|
||||
if (hasPackageLockJson) {
|
||||
opts.prettyCommand = 'npm install';
|
||||
command = 'npm';
|
||||
commandArgs = args
|
||||
.filter(a => a !== '--prefer-offline')
|
||||
.concat(['install', '--no-audit', '--unsafe-perm']);
|
||||
} else {
|
||||
opts.prettyCommand = 'yarn install';
|
||||
command = 'yarn';
|
||||
commandArgs = args.concat(['install', '--ignore-engines']);
|
||||
}
|
||||
@@ -285,7 +307,7 @@ export async function runBundleInstall(
|
||||
}
|
||||
|
||||
assert(path.isAbsolute(destPath));
|
||||
const opts = { cwd: destPath, ...spawnOpts };
|
||||
const opts = { ...spawnOpts, cwd: destPath, prettyCommand: 'bundle install' };
|
||||
|
||||
await spawnAsync(
|
||||
'bundle',
|
||||
@@ -313,7 +335,7 @@ export async function runPipInstall(
|
||||
}
|
||||
|
||||
assert(path.isAbsolute(destPath));
|
||||
const opts = { cwd: destPath, ...spawnOpts };
|
||||
const opts = { ...spawnOpts, cwd: destPath, prettyCommand: 'pip3 install' };
|
||||
|
||||
await spawnAsync(
|
||||
'pip3',
|
||||
@@ -340,18 +362,22 @@ export async function runPackageJsonScript(
|
||||
);
|
||||
if (!hasScript) return false;
|
||||
|
||||
const opts = { cwd: destPath, ...spawnOpts };
|
||||
|
||||
if (hasPackageLockJson) {
|
||||
console.log(`Running "npm run ${scriptName}"`);
|
||||
await spawnAsync('npm', ['run', scriptName], opts);
|
||||
const prettyCommand = `npm run ${scriptName}`;
|
||||
console.log(`Running "${prettyCommand}"`);
|
||||
await spawnAsync('npm', ['run', scriptName], {
|
||||
...spawnOpts,
|
||||
cwd: destPath,
|
||||
prettyCommand,
|
||||
});
|
||||
} else {
|
||||
console.log(`Running "yarn run ${scriptName}"`);
|
||||
await spawnAsync(
|
||||
'yarn',
|
||||
['--ignore-engines', '--cwd', destPath, 'run', scriptName],
|
||||
opts
|
||||
);
|
||||
const prettyCommand = `yarn run ${scriptName}`;
|
||||
console.log(`Running "${prettyCommand}"`);
|
||||
await spawnAsync('yarn', ['--ignore-engines', 'run', scriptName], {
|
||||
...spawnOpts,
|
||||
cwd: destPath,
|
||||
prettyCommand,
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -7,6 +7,19 @@ import {
|
||||
} from '../';
|
||||
|
||||
describe('Test `detectBuilders`', () => {
|
||||
it('should never select now.json src', async () => {
|
||||
const files = ['docs/index.md', 'mkdocs.yml', 'now.json'];
|
||||
const { builders, errors } = await detectBuilders(files, null, {
|
||||
projectSettings: {
|
||||
buildCommand: 'mkdocs build',
|
||||
outputDirectory: 'site',
|
||||
},
|
||||
});
|
||||
expect(errors).toBe(null);
|
||||
expect(builders).toBeDefined();
|
||||
expect(builders![0].src).not.toBe('now.json');
|
||||
});
|
||||
|
||||
it('package.json + no build', async () => {
|
||||
const pkg = { dependencies: { next: '9.0.0' } };
|
||||
const files = ['package.json', 'pages/index.js', 'public/index.html'];
|
||||
@@ -817,6 +830,21 @@ describe('Test `detectBuilders`', () => {
|
||||
|
||||
describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
|
||||
const featHandleMiss = true;
|
||||
|
||||
it('should never select now.json src', async () => {
|
||||
const files = ['docs/index.md', 'mkdocs.yml', 'now.json'];
|
||||
const { builders, errors } = await detectBuilders(files, null, {
|
||||
featHandleMiss,
|
||||
projectSettings: {
|
||||
buildCommand: 'mkdocs build',
|
||||
outputDirectory: 'site',
|
||||
},
|
||||
});
|
||||
expect(errors).toBe(null);
|
||||
expect(builders).toBeDefined();
|
||||
expect(builders![0].src).not.toBe('now.json');
|
||||
});
|
||||
|
||||
it('package.json + no build', async () => {
|
||||
const pkg = { dependencies: { next: '9.0.0' } };
|
||||
const files = ['package.json', 'pages/index.js', 'public/index.html'];
|
||||
|
||||
@@ -21,7 +21,6 @@ describe('Test `walkParentDirs`', () => {
|
||||
await walkParentDirs({ base, start, filename });
|
||||
fail('Expected error');
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
deepEqual(
|
||||
(error as Error).message,
|
||||
'Expected "base" to be absolute path'
|
||||
@@ -36,7 +35,6 @@ describe('Test `walkParentDirs`', () => {
|
||||
await walkParentDirs({ base, start, filename });
|
||||
fail('Expected error');
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
deepEqual(
|
||||
(error as Error).message,
|
||||
'Expected "start" to be absolute path'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "now",
|
||||
"version": "17.1.0",
|
||||
"version": "18.0.0",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Now",
|
||||
@@ -13,9 +13,9 @@
|
||||
"scripts": {
|
||||
"preinstall": "node ./scripts/preinstall.js",
|
||||
"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-cli": "ava test/integration.js --serial --fail-fast --verbose",
|
||||
"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-dev": "ava test/dev/integration.js --serial --fail-fast --verbose",
|
||||
"prepublishOnly": "yarn build",
|
||||
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
||||
"build": "ts-node ./scripts/build.ts",
|
||||
@@ -63,7 +63,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sentry/node": "5.5.0",
|
||||
"@sindresorhus/slugify": "0.10.0",
|
||||
"@sindresorhus/slugify": "0.11.0",
|
||||
"@types/ansi-escapes": "3.0.0",
|
||||
"@types/ansi-regex": "4.0.0",
|
||||
"@types/async-retry": "1.2.1",
|
||||
@@ -141,8 +141,9 @@
|
||||
"micro": "9.1.2",
|
||||
"mime-types": "2.1.24",
|
||||
"minimatch": "3.0.4",
|
||||
"mri": "1.1.0",
|
||||
"mri": "1.1.5",
|
||||
"ms": "2.1.2",
|
||||
"nanoid": "3.0.2",
|
||||
"node-fetch": "2.6.0",
|
||||
"npm-package-arg": "6.1.0",
|
||||
"nyc": "13.2.0",
|
||||
|
||||
@@ -40,7 +40,7 @@ const help = () => {
|
||||
)} Login token
|
||||
-S, --scope Set a custom scope
|
||||
-n, --no-verify Don't wait until instance count meets the previous alias constraints
|
||||
|
||||
-N, --next Show next page of results
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Add a new alias to ${chalk.underline('my-api.now.sh')}
|
||||
@@ -81,6 +81,12 @@ const help = () => {
|
||||
${chalk.cyan(
|
||||
`$ now alias ls aliasId --json > ${chalk.underline('rules.json')}`
|
||||
)}
|
||||
|
||||
${chalk.gray('–')} Paginate results, where ${chalk.dim(
|
||||
'`1584722256178`'
|
||||
)} is the time in milliseconds since the UNIX epoch.
|
||||
|
||||
${chalk.cyan(`$ now alias ls --next 1584722256178`)}
|
||||
`);
|
||||
};
|
||||
|
||||
@@ -100,9 +106,11 @@ export default async function main(ctx) {
|
||||
'--no-verify': Boolean,
|
||||
'--rules': String,
|
||||
'--yes': Boolean,
|
||||
'--next': Number,
|
||||
'-n': '--no-verify',
|
||||
'-r': '--rules',
|
||||
'-y': '--yes',
|
||||
'-N': '--next',
|
||||
});
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
|
||||
@@ -8,6 +8,8 @@ import getAliases from '../../util/alias/get-aliases';
|
||||
import getScope from '../../util/get-scope.ts';
|
||||
import stamp from '../../util/output/stamp.ts';
|
||||
import strlen from '../../util/strlen.ts';
|
||||
import getCommandFlags from '../../util/get-command-flags';
|
||||
import cmd from '../../util/output/cmd.ts';
|
||||
|
||||
export default async function ls(ctx, opts, args, output) {
|
||||
const {
|
||||
@@ -16,7 +18,7 @@ export default async function ls(ctx, opts, args, output) {
|
||||
} = ctx;
|
||||
const { currentTeam } = config;
|
||||
const { apiUrl } = ctx;
|
||||
const { '--debug': debugEnabled } = opts;
|
||||
const { '--debug': debugEnabled, '--next': nextTimestamp } = opts;
|
||||
const client = new Client({
|
||||
apiUrl,
|
||||
token,
|
||||
@@ -36,8 +38,17 @@ export default async function ls(ctx, opts, args, output) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// $FlowFixMe
|
||||
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
|
||||
if (typeof nextTimestamp !== undefined && Number.isNaN(nextTimestamp)) {
|
||||
output.error('Please provide a number for flag --next');
|
||||
return 1;
|
||||
}
|
||||
|
||||
const now = new Now({
|
||||
apiUrl,
|
||||
token,
|
||||
debug: debugEnabled,
|
||||
currentTeam,
|
||||
});
|
||||
const lsStamp = stamp();
|
||||
let cancelWait;
|
||||
|
||||
@@ -58,7 +69,11 @@ export default async function ls(ctx, opts, args, output) {
|
||||
: `Fetching aliases under ${chalk.bold(contextName)}`
|
||||
);
|
||||
|
||||
const aliases = await getAliases(now);
|
||||
const { aliases, pagination } = await getAliases(
|
||||
now,
|
||||
undefined,
|
||||
nextTimestamp
|
||||
);
|
||||
if (cancelWait) cancelWait();
|
||||
|
||||
if (args[0]) {
|
||||
@@ -84,15 +99,19 @@ export default async function ls(ctx, opts, args, output) {
|
||||
output.print(`${printPathAliasTable(rules)}\n`);
|
||||
}
|
||||
} else {
|
||||
aliases.sort((a, b) => new Date(b.created) - new Date(a.created));
|
||||
output.log(
|
||||
`${plural('alias', aliases.length, true)} found under ${chalk.bold(
|
||||
contextName
|
||||
)} ${lsStamp()}`
|
||||
);
|
||||
output.log(`aliases found under ${chalk.bold(contextName)} ${lsStamp()}`);
|
||||
console.log(printAliasTable(aliases));
|
||||
}
|
||||
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(opts, ['_', '--next']);
|
||||
output.log(
|
||||
`To display the next page run ${cmd(
|
||||
`now alias ls${flags} --next ${pagination.next}`
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
now.close();
|
||||
return 0;
|
||||
}
|
||||
@@ -111,7 +130,7 @@ function printAliasTable(aliases) {
|
||||
? a.deployment.url
|
||||
: chalk.gray('–'),
|
||||
a.alias,
|
||||
ms(Date.now() - new Date(a.created)),
|
||||
ms(Date.now() - new Date(a.createdAt)),
|
||||
]),
|
||||
],
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@ export const latestHelp = () => `
|
||||
'(default)'
|
||||
)}
|
||||
dev Start a local development server
|
||||
env Manages the Environment Variables for your current Project
|
||||
init [example] Initialize an example project
|
||||
ls | list [app] Lists deployments
|
||||
inspect [id] Displays information related to a deployment
|
||||
@@ -28,7 +29,7 @@ export const latestHelp = () => `
|
||||
domains [name] Manages your domain names
|
||||
dns [name] Manages your DNS records
|
||||
certs [cmd] Manages your SSL certificates
|
||||
secrets [name] Manages your secret environment variables
|
||||
secrets [name] Manages your global Secrets, for use in Environment Variables
|
||||
logs [url] Displays the logs for a deployment
|
||||
teams Manages your teams
|
||||
whoami Shows the username of the currently logged in user
|
||||
@@ -46,6 +47,7 @@ export const latestHelp = () => `
|
||||
)} Path to the global ${'`.now`'} directory
|
||||
-d, --debug Debug mode [off]
|
||||
-f, --force Force a new deployment even if nothing has changed
|
||||
--with-cache Retain build cache when using "--force"
|
||||
-t ${chalk.underline('TOKEN')}, --token=${chalk.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
@@ -83,7 +85,7 @@ export const latestHelp = () => `
|
||||
|
||||
${chalk.cyan('$ now /usr/src/project')}
|
||||
|
||||
${chalk.gray('–')} Deploy with environment variables
|
||||
${chalk.gray('–')} Deploy with Environment Variables
|
||||
|
||||
${chalk.cyan('$ now -e NODE_ENV=production -e SECRET=@mysql-secret')}
|
||||
|
||||
@@ -97,6 +99,7 @@ export const latestHelp = () => `
|
||||
|
||||
export const latestArgs = {
|
||||
'--force': Boolean,
|
||||
'--with-cache': Boolean,
|
||||
'--public': Boolean,
|
||||
'--no-clipboard': Boolean,
|
||||
'--env': [String],
|
||||
|
||||
@@ -506,6 +506,7 @@ export default async function main(
|
||||
env: deploymentEnv,
|
||||
build: { env: deploymentBuildEnv },
|
||||
forceNew: argv['--force'],
|
||||
withCache: argv['--with-cache'],
|
||||
quiet,
|
||||
wantsPublic: argv['--public'] || localConfig.public,
|
||||
isFile,
|
||||
@@ -576,7 +577,7 @@ export default async function main(
|
||||
}
|
||||
|
||||
if (deployment.readyState === 'CANCELED') {
|
||||
output.log('The deployment has been canceled.');
|
||||
output.print('The deployment has been canceled.\n');
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -665,7 +666,7 @@ export default async function main(
|
||||
if (err instanceof BuildError) {
|
||||
output.error('Build failed');
|
||||
output.error(
|
||||
`Check your logs at ${now.url}/_logs or run ${code(
|
||||
`Check your logs at https://${now.url}/_logs or run ${code(
|
||||
`now logs ${now.url}`,
|
||||
{
|
||||
// Backticks are interpreted as part of the URL, causing CMD+Click
|
||||
|
||||
@@ -93,6 +93,7 @@ let paths: string[];
|
||||
|
||||
// Options
|
||||
let forceNew: boolean;
|
||||
let withCache: boolean;
|
||||
let deploymentName: string;
|
||||
let sessionAffinity: string;
|
||||
let log: any;
|
||||
@@ -238,6 +239,7 @@ export default async function main(
|
||||
|
||||
// Options
|
||||
forceNew = argv.force;
|
||||
withCache = argv['with-cache'];
|
||||
deploymentName = argv.name;
|
||||
sessionAffinity = argv['session-affinity'];
|
||||
debugEnabled = argv.debug;
|
||||
@@ -741,6 +743,7 @@ async function sync({
|
||||
meta: metadata,
|
||||
followSymlinks,
|
||||
forceNew,
|
||||
withCache,
|
||||
forwardNpm,
|
||||
quiet,
|
||||
scale,
|
||||
|
||||
@@ -45,6 +45,7 @@ const help = () => {
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
-S, --scope Set a custom scope
|
||||
-N, --next Show next page of results
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
@@ -61,6 +62,12 @@ const help = () => {
|
||||
if it's configured with these nameservers (no need to ${chalk.dim(
|
||||
'`domain add`'
|
||||
)}).
|
||||
|
||||
${chalk.gray('–')} Paginate results, where ${chalk.dim(
|
||||
'`1584722256178`'
|
||||
)} is the time in milliseconds since the UNIX epoch.
|
||||
|
||||
${chalk.cyan(`$ now domains ls --next 1584722256178`)}
|
||||
`);
|
||||
};
|
||||
|
||||
@@ -72,7 +79,7 @@ const COMMAND_CONFIG = {
|
||||
move: ['move'],
|
||||
rm: ['rm', 'remove'],
|
||||
transferIn: ['transfer-in'],
|
||||
verify: ['verify']
|
||||
verify: ['verify'],
|
||||
};
|
||||
|
||||
export default async function main(ctx: NowContext) {
|
||||
@@ -83,7 +90,9 @@ export default async function main(ctx: NowContext) {
|
||||
'--cdn': Boolean,
|
||||
'--code': String,
|
||||
'--no-cdn': Boolean,
|
||||
'--yes': Boolean
|
||||
'--yes': Boolean,
|
||||
'--next': Number,
|
||||
'-N': '--next',
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import ms from 'ms';
|
||||
import chalk from 'chalk';
|
||||
import plural from 'pluralize';
|
||||
import table from 'text-table';
|
||||
|
||||
import Client from '../../util/client';
|
||||
@@ -10,9 +9,12 @@ import stamp from '../../util/output/stamp';
|
||||
import strlen from '../../util/strlen';
|
||||
import { Output } from '../../util/output';
|
||||
import { Domain, NowContext } from '../../types';
|
||||
import getCommandFlags from '../../util/get-command-flags';
|
||||
import cmd from '../../util/output/cmd';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
'--next': number;
|
||||
};
|
||||
|
||||
export default async function ls(
|
||||
@@ -21,13 +23,21 @@ export default async function ls(
|
||||
args: string[],
|
||||
output: Output
|
||||
) {
|
||||
const { authConfig: { token }, config } = ctx;
|
||||
const {
|
||||
authConfig: { token },
|
||||
config,
|
||||
} = ctx;
|
||||
const { currentTeam } = config;
|
||||
const { apiUrl } = ctx;
|
||||
const debug = opts['--debug'];
|
||||
const { '--debug': debug, '--next': nextTimestamp } = opts;
|
||||
const client = new Client({ apiUrl, token, currentTeam, debug });
|
||||
let contextName = null;
|
||||
|
||||
if (typeof nextTimestamp !== undefined && Number.isNaN(nextTimestamp)) {
|
||||
output.error('Please provide a number for flag --next');
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
({ contextName } = await getScope(client));
|
||||
} catch (err) {
|
||||
@@ -48,16 +58,27 @@ export default async function ls(
|
||||
return 1;
|
||||
}
|
||||
|
||||
const domains = await getDomains(client, contextName);
|
||||
const { domains, pagination } = await getDomains(
|
||||
client,
|
||||
contextName,
|
||||
nextTimestamp
|
||||
);
|
||||
output.log(
|
||||
`${plural('domain', domains.length, true)} found under ${chalk.bold(
|
||||
contextName
|
||||
)} ${chalk.gray(lsStamp())}\n`
|
||||
`Domains found under ${chalk.bold(contextName)} ${chalk.gray(lsStamp())}\n`
|
||||
);
|
||||
if (domains.length > 0) {
|
||||
console.log(`${formatDomainsTable(domains)}\n`);
|
||||
}
|
||||
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(opts, ['_', '--next']);
|
||||
output.log(
|
||||
`To display the next page run ${cmd(
|
||||
`now domains ls${flags} --next ${pagination.next}`
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -71,18 +92,18 @@ function formatDomainsTable(domains: Domain[]) {
|
||||
chalk.gray('serviceType'),
|
||||
chalk.gray('verified'),
|
||||
chalk.gray('cdn'),
|
||||
chalk.gray('age')
|
||||
chalk.gray('age'),
|
||||
].map(s => chalk.dim(s)),
|
||||
...domains.map(domain => {
|
||||
const url = chalk.bold(domain.name);
|
||||
const time = chalk.gray(ms(current.getTime() - domain.createdAt));
|
||||
return ['', url, domain.serviceType, domain.verified, true, time];
|
||||
})
|
||||
}),
|
||||
],
|
||||
{
|
||||
align: ['l', 'l', 'l', 'l', 'l'],
|
||||
hsep: ' '.repeat(4),
|
||||
stringLength: strlen
|
||||
stringLength: strlen,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -111,12 +111,26 @@ async function removeDomain(
|
||||
|
||||
for (const id of aliasIds) {
|
||||
output.debug(`Removing alias ${id}`);
|
||||
await removeAliasById(client, id);
|
||||
try {
|
||||
await removeAliasById(client, id);
|
||||
} catch (error) {
|
||||
// Ignore if the alias does not exist anymore
|
||||
if (error.status !== 404) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const id of certIds) {
|
||||
output.debug(`Removing cert ${id}`);
|
||||
await deleteCertById(output, client, id);
|
||||
try {
|
||||
await deleteCertById(output, client, id);
|
||||
} catch (error) {
|
||||
// Ignore if the cert does not exist anymore
|
||||
if (error.status !== 404) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (suffix) {
|
||||
|
||||
172
packages/now-cli/src/commands/env/add.ts
vendored
Normal file
172
packages/now-cli/src/commands/env/add.ts
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
import chalk from 'chalk';
|
||||
import inquirer from 'inquirer';
|
||||
import { NowContext, ProjectEnvTarget } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import Client from '../../util/client';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import { getLinkedProject } from '../../util/projects/link';
|
||||
import addEnvRecord from '../../util/env/add-env-record';
|
||||
import getEnvVariables from '../../util/env/get-env-records';
|
||||
import {
|
||||
isValidEnvTarget,
|
||||
getEnvTargetPlaceholder,
|
||||
getEnvTargetChoices,
|
||||
} from '../../util/env/env-target';
|
||||
import readStandardInput from '../../util/input/read-standard-input';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import param from '../../util/output/param';
|
||||
import withSpinner from '../../util/with-spinner';
|
||||
import { emoji, prependEmoji } from '../../util/emoji';
|
||||
import { isKnownError } from '../../util/env/known-error';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
};
|
||||
|
||||
export default async function add(
|
||||
ctx: NowContext,
|
||||
opts: Options,
|
||||
args: string[],
|
||||
output: Output
|
||||
) {
|
||||
const {
|
||||
authConfig: { token },
|
||||
config,
|
||||
} = ctx;
|
||||
const { currentTeam } = config;
|
||||
const { apiUrl } = ctx;
|
||||
const debug = opts['--debug'];
|
||||
const client = new Client({ apiUrl, token, currentTeam, debug });
|
||||
const link = await getLinkedProject(output, client);
|
||||
|
||||
if (link.status === 'error') {
|
||||
return link.exitCode;
|
||||
} else if (link.status === 'not_linked') {
|
||||
output.print(
|
||||
`${chalk.red(
|
||||
'Error!'
|
||||
)} Your codebase isn’t linked to a project on ZEIT Now. Run ${cmd(
|
||||
'now'
|
||||
)} to link it.\n`
|
||||
);
|
||||
return 1;
|
||||
} else {
|
||||
const { project } = link;
|
||||
const stdInput = await readStandardInput();
|
||||
let [envName, envTarget] = args;
|
||||
|
||||
if (args.length > 2) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${cmd(
|
||||
`now env add <name> ${getEnvTargetPlaceholder()}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (stdInput && (!envName || !envTarget)) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${cmd(
|
||||
`now env add <name> <target> < <file>`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
let envTargets: ProjectEnvTarget[] = [];
|
||||
if (envTarget) {
|
||||
if (!isValidEnvTarget(envTarget)) {
|
||||
output.error(
|
||||
`The Environment ${param(
|
||||
envTarget
|
||||
)} is invalid. It must be one of: ${getEnvTargetPlaceholder()}.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
envTargets.push(envTarget);
|
||||
}
|
||||
|
||||
while (!envName) {
|
||||
const { inputName } = await inquirer.prompt({
|
||||
type: 'input',
|
||||
name: 'inputName',
|
||||
message: `What’s the name of the variable?`,
|
||||
});
|
||||
|
||||
envName = inputName;
|
||||
|
||||
if (!inputName) {
|
||||
output.error('Name cannot be empty');
|
||||
}
|
||||
}
|
||||
|
||||
const envs = await getEnvVariables(output, client, project.id);
|
||||
const existing = new Set(
|
||||
envs.filter(r => r.key === envName).map(r => r.target)
|
||||
);
|
||||
const choices = getEnvTargetChoices().filter(c => !existing.has(c.value));
|
||||
|
||||
if (choices.length === 0) {
|
||||
output.error(
|
||||
`The variable ${param(
|
||||
envName
|
||||
)} has already been added to all Environments. To remove, run ${cmd(
|
||||
`now env rm ${envName}`
|
||||
)}.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
let envValue: string;
|
||||
|
||||
if (stdInput) {
|
||||
envValue = stdInput;
|
||||
} else {
|
||||
const { inputValue } = await inquirer.prompt({
|
||||
type: 'password',
|
||||
name: 'inputValue',
|
||||
message: `What’s the value of ${envName}?`,
|
||||
});
|
||||
envValue = inputValue || '';
|
||||
}
|
||||
|
||||
while (envTargets.length === 0) {
|
||||
const { inputTargets } = await inquirer.prompt({
|
||||
name: 'inputTargets',
|
||||
type: 'checkbox',
|
||||
message: `Add ${envName} to which Environments (select multiple)?`,
|
||||
choices,
|
||||
});
|
||||
|
||||
envTargets = inputTargets;
|
||||
|
||||
if (inputTargets.length === 0) {
|
||||
output.error('Please select at least one Environment');
|
||||
}
|
||||
}
|
||||
|
||||
const addStamp = stamp();
|
||||
try {
|
||||
await withSpinner('Saving', () =>
|
||||
addEnvRecord(output, client, project.id, envName, envValue, envTargets)
|
||||
);
|
||||
} catch (error) {
|
||||
if (isKnownError(error) && error.serverMessage) {
|
||||
output.error(error.serverMessage);
|
||||
return 1;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
output.print(
|
||||
`${prependEmoji(
|
||||
`Added Environment Variable ${chalk.bold(
|
||||
envName
|
||||
)} to Project ${chalk.bold(project.name)} ${chalk.gray(addStamp())}`,
|
||||
emoji('success')
|
||||
)}\n`
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
114
packages/now-cli/src/commands/env/index.ts
vendored
Normal file
114
packages/now-cli/src/commands/env/index.ts
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { NowContext } from '../../types';
|
||||
import createOutput from '../../util/output';
|
||||
import getArgs from '../../util/get-args';
|
||||
import getSubcommand from '../../util/get-subcommand';
|
||||
import getInvalidSubcommand from '../../util/get-invalid-subcommand';
|
||||
import { getEnvTargetPlaceholder } from '../../util/env/env-target';
|
||||
import handleError from '../../util/handle-error';
|
||||
import logo from '../../util/output/logo';
|
||||
|
||||
import add from './add';
|
||||
import pull from './pull';
|
||||
import ls from './ls';
|
||||
import rm from './rm';
|
||||
|
||||
const help = () => {
|
||||
const placeholder = getEnvTargetPlaceholder();
|
||||
console.log(`
|
||||
${chalk.bold(`${logo} now env`)} [options] <command>
|
||||
|
||||
${chalk.dim('Commands:')}
|
||||
|
||||
ls [environment] List all variables for the specified Environment
|
||||
add [name] [environment] Add an Environment Variable (see examples below)
|
||||
rm [name] [environment] Remove an Environment Variable (see examples below)
|
||||
pull [filename] Pull all Development Environment Variables from the cloud and write to a file [.env]
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`now.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.now`'} directory
|
||||
-d, --debug Debug mode [off]
|
||||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Add a new variable to multiple Environments
|
||||
|
||||
${chalk.cyan('$ now env add <name>')}
|
||||
${chalk.cyan('$ now env add API_TOKEN')}
|
||||
|
||||
${chalk.gray('–')} Add a new variable for a specific Environment
|
||||
|
||||
${chalk.cyan(`$ now env add <name> ${placeholder}`)}
|
||||
${chalk.cyan('$ now env add DB_CONNECTION production')}
|
||||
|
||||
${chalk.gray('–')} Add a new Environment Variable from stdin
|
||||
|
||||
${chalk.cyan(`$ cat <file> | now env add <name> ${placeholder}`)}
|
||||
${chalk.cyan('$ cat ~/.npmrc | now env add NPM_RC preview')}
|
||||
${chalk.cyan('$ now env add DB_PASS production < secret.txt')}
|
||||
|
||||
${chalk.gray('–')} Remove an variable from multiple Environments
|
||||
|
||||
${chalk.cyan('$ now env rm <name>')}
|
||||
${chalk.cyan('$ now env rm API_TOKEN')}
|
||||
|
||||
${chalk.gray('–')} Remove a variable from a specific Environment
|
||||
|
||||
${chalk.cyan(`$ now env rm <name> ${placeholder}`)}
|
||||
${chalk.cyan('$ now env rm NPM_RC preview')}
|
||||
`);
|
||||
};
|
||||
|
||||
const COMMAND_CONFIG = {
|
||||
ls: ['ls', 'list'],
|
||||
add: ['add'],
|
||||
rm: ['rm', 'remove'],
|
||||
pull: ['pull'],
|
||||
};
|
||||
|
||||
export default async function main(ctx: NowContext) {
|
||||
let argv;
|
||||
|
||||
try {
|
||||
argv = getArgs(ctx.argv.slice(2), {
|
||||
'--yes': Boolean,
|
||||
'-y': '--yes',
|
||||
});
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
return 2;
|
||||
}
|
||||
|
||||
const output = createOutput({ debug: argv['--debug'] });
|
||||
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
|
||||
switch (subcommand) {
|
||||
case 'ls':
|
||||
return ls(ctx, argv, args, output);
|
||||
case 'add':
|
||||
return add(ctx, argv, args, output);
|
||||
case 'rm':
|
||||
return rm(ctx, argv, args, output);
|
||||
case 'pull':
|
||||
return pull(ctx, argv, args, output);
|
||||
default:
|
||||
output.error(getInvalidSubcommand(COMMAND_CONFIG));
|
||||
help();
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
117
packages/now-cli/src/commands/env/ls.ts
vendored
Normal file
117
packages/now-cli/src/commands/env/ls.ts
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import plural from 'pluralize';
|
||||
import { Output } from '../../util/output';
|
||||
import { ProjectEnvVariable, ProjectEnvTarget, NowContext } from '../../types';
|
||||
import Client from '../../util/client';
|
||||
import formatTable from '../../util/format-table';
|
||||
import getEnvVariables from '../../util/env/get-env-records';
|
||||
import {
|
||||
isValidEnvTarget,
|
||||
getEnvTargetPlaceholder,
|
||||
} from '../../util/env/env-target';
|
||||
import { getLinkedProject } from '../../util/projects/link';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import param from '../../util/output/param';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
};
|
||||
|
||||
export default async function ls(
|
||||
ctx: NowContext,
|
||||
opts: Options,
|
||||
args: string[],
|
||||
output: Output
|
||||
) {
|
||||
const {
|
||||
authConfig: { token },
|
||||
config,
|
||||
} = ctx;
|
||||
const { currentTeam } = config;
|
||||
const { apiUrl } = ctx;
|
||||
const debug = opts['--debug'];
|
||||
const client = new Client({ apiUrl, token, currentTeam, debug });
|
||||
const link = await getLinkedProject(output, client);
|
||||
|
||||
if (link.status === 'error') {
|
||||
return link.exitCode;
|
||||
} else if (link.status === 'not_linked') {
|
||||
output.print(
|
||||
`${chalk.red(
|
||||
'Error!'
|
||||
)} Your codebase isn’t linked to a project on ZEIT Now. Run ${cmd(
|
||||
'now'
|
||||
)} to link it.\n`
|
||||
);
|
||||
return 1;
|
||||
} else {
|
||||
if (args.length > 1) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${cmd(
|
||||
`now env ls ${getEnvTargetPlaceholder()}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const { project } = link;
|
||||
const envTarget = args[0] as ProjectEnvTarget | undefined;
|
||||
|
||||
if (!isValidEnvTarget(envTarget)) {
|
||||
output.error(
|
||||
`The Environment ${param(
|
||||
envTarget
|
||||
)} is invalid. It must be one of: ${getEnvTargetPlaceholder()}.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const lsStamp = stamp();
|
||||
|
||||
const records = await getEnvVariables(
|
||||
output,
|
||||
client,
|
||||
project.id,
|
||||
envTarget
|
||||
);
|
||||
output.log(
|
||||
`${plural(
|
||||
'Environment Variable',
|
||||
records.length,
|
||||
true
|
||||
)} found in Project ${chalk.bold(project.name)} ${chalk.gray(lsStamp())}`
|
||||
);
|
||||
console.log(getTable(records));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function getTable(records: ProjectEnvVariable[]) {
|
||||
return formatTable(
|
||||
['name', 'value', 'environment', 'created'],
|
||||
['l', 'l', 'l', 'l', 'l'],
|
||||
[
|
||||
{
|
||||
name: '',
|
||||
rows: records.map(getRow),
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
function getRow({
|
||||
key,
|
||||
system = false,
|
||||
target,
|
||||
createdAt = 0,
|
||||
}: ProjectEnvVariable) {
|
||||
const now = Date.now();
|
||||
return [
|
||||
chalk.bold(key),
|
||||
chalk.gray(chalk.italic(system ? 'Populated by System' : 'Encrypted')),
|
||||
target || '',
|
||||
`${ms(now - createdAt)} ago`,
|
||||
];
|
||||
}
|
||||
120
packages/now-cli/src/commands/env/pull.ts
vendored
Normal file
120
packages/now-cli/src/commands/env/pull.ts
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
import chalk from 'chalk';
|
||||
import { NowContext, ProjectEnvTarget } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import promptBool from '../../util/prompt-bool';
|
||||
import Client from '../../util/client';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import getEnvVariables from '../../util/env/get-env-records';
|
||||
import getDecryptedSecret from '../../util/env/get-decrypted-secret';
|
||||
import { getLinkedProject } from '../../util/projects/link';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import param from '../../util/output/param';
|
||||
import withSpinner from '../../util/with-spinner';
|
||||
import { join } from 'path';
|
||||
import { promises, existsSync } from 'fs';
|
||||
import { emoji, prependEmoji } from '../../util/emoji';
|
||||
const { writeFile } = promises;
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
'--yes': boolean;
|
||||
};
|
||||
|
||||
export default async function pull(
|
||||
ctx: NowContext,
|
||||
opts: Options,
|
||||
args: string[],
|
||||
output: Output
|
||||
) {
|
||||
const {
|
||||
authConfig: { token },
|
||||
config,
|
||||
} = ctx;
|
||||
const { currentTeam } = config;
|
||||
const { apiUrl } = ctx;
|
||||
const debug = opts['--debug'];
|
||||
const client = new Client({ apiUrl, token, currentTeam, debug });
|
||||
const link = await getLinkedProject(output, client);
|
||||
|
||||
if (link.status === 'error') {
|
||||
return link.exitCode;
|
||||
} else if (link.status === 'not_linked') {
|
||||
output.print(
|
||||
`${chalk.red(
|
||||
'Error!'
|
||||
)} Your codebase isn’t linked to a project on ZEIT Now. Run ${cmd(
|
||||
'now'
|
||||
)} to link it.\n`
|
||||
);
|
||||
return 1;
|
||||
} else {
|
||||
if (args.length > 1) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${cmd('now env pull <file>')}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const { project } = link;
|
||||
const [filename = '.env'] = args;
|
||||
const fullPath = join(process.cwd(), filename);
|
||||
const exists = existsSync(fullPath);
|
||||
const skipConfirmation = opts['--yes'];
|
||||
|
||||
if (
|
||||
exists &&
|
||||
!skipConfirmation &&
|
||||
!(await promptBool(
|
||||
output,
|
||||
`Found existing file ${param(filename)}. Do you want to overwrite?`
|
||||
))
|
||||
) {
|
||||
output.log('Aborted');
|
||||
return 0;
|
||||
}
|
||||
|
||||
output.print(
|
||||
`Downloading Development Environment Variables for Project ${chalk.bold(
|
||||
project.name
|
||||
)}\n`
|
||||
);
|
||||
const pullStamp = stamp();
|
||||
|
||||
const records = await withSpinner('Downloading', async () => {
|
||||
const dev = ProjectEnvTarget.Development;
|
||||
const envs = await getEnvVariables(output, client, project.id, dev);
|
||||
const values = await Promise.all(
|
||||
envs.map(env => getDecryptedSecret(output, client, env.value))
|
||||
);
|
||||
const results: { key: string; value: string }[] = [];
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
results.push({ key: envs[i].key, value: values[i] });
|
||||
}
|
||||
return results;
|
||||
});
|
||||
|
||||
const contents =
|
||||
records
|
||||
.map(({ key, value }) => `${key}="${escapeValue(value)}"`)
|
||||
.join('\n') + '\n';
|
||||
|
||||
await writeFile(fullPath, contents, 'utf8');
|
||||
|
||||
output.print(
|
||||
`${prependEmoji(
|
||||
`${exists ? 'Updated' : 'Created'} ${chalk.bold(
|
||||
filename
|
||||
)} file ${chalk.gray(pullStamp())}`,
|
||||
emoji('success')
|
||||
)}\n`
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function escapeValue(value: string) {
|
||||
return value
|
||||
.replace(new RegExp('\\"', 'g'), '\\"') // escape quotes
|
||||
.replace(new RegExp('\n', 'g'), '\\n') // combine newlines (unix) into one line
|
||||
.replace(new RegExp('\r', 'g'), '\\r'); // combine newlines (windows) into one line
|
||||
}
|
||||
168
packages/now-cli/src/commands/env/rm.ts
vendored
Normal file
168
packages/now-cli/src/commands/env/rm.ts
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
import chalk from 'chalk';
|
||||
import inquirer from 'inquirer';
|
||||
import { NowContext, ProjectEnvTarget } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import promptBool from '../../util/prompt-bool';
|
||||
import { getLinkedProject } from '../../util/projects/link';
|
||||
import removeEnvRecord from '../../util/env/remove-env-record';
|
||||
import getEnvVariables from '../../util/env/get-env-records';
|
||||
import {
|
||||
isValidEnvTarget,
|
||||
getEnvTargetPlaceholder,
|
||||
getEnvTargetChoices,
|
||||
} from '../../util/env/env-target';
|
||||
import Client from '../../util/client';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import param from '../../util/output/param';
|
||||
import withSpinner from '../../util/with-spinner';
|
||||
import { emoji, prependEmoji } from '../../util/emoji';
|
||||
import { isKnownError } from '../../util/env/known-error';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
'--yes': boolean;
|
||||
};
|
||||
|
||||
export default async function rm(
|
||||
ctx: NowContext,
|
||||
opts: Options,
|
||||
args: string[],
|
||||
output: Output
|
||||
) {
|
||||
const {
|
||||
authConfig: { token },
|
||||
config,
|
||||
} = ctx;
|
||||
const { currentTeam } = config;
|
||||
const { apiUrl } = ctx;
|
||||
const debug = opts['--debug'];
|
||||
const client = new Client({ apiUrl, token, currentTeam, debug });
|
||||
const link = await getLinkedProject(output, client);
|
||||
|
||||
if (link.status === 'error') {
|
||||
return link.exitCode;
|
||||
} else if (link.status === 'not_linked') {
|
||||
output.print(
|
||||
`${chalk.red(
|
||||
'Error!'
|
||||
)} Your codebase isn’t linked to a project on ZEIT Now. Run ${cmd(
|
||||
'now'
|
||||
)} to link it.\n`
|
||||
);
|
||||
return 1;
|
||||
} else {
|
||||
if (args.length > 2) {
|
||||
output.error(
|
||||
`Invalid number of arguments. Usage: ${cmd(
|
||||
`now env rm <name> ${getEnvTargetPlaceholder()}`
|
||||
)}`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const { project } = link;
|
||||
let [envName, envTarget] = args;
|
||||
let envTargets: ProjectEnvTarget[] = [];
|
||||
|
||||
if (envTarget) {
|
||||
if (!isValidEnvTarget(envTarget)) {
|
||||
output.error(
|
||||
`The Environment ${param(
|
||||
envTarget
|
||||
)} is invalid. It must be one of: ${getEnvTargetPlaceholder()}.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
envTargets.push(envTarget);
|
||||
}
|
||||
|
||||
while (!envName) {
|
||||
const { inputName } = await inquirer.prompt({
|
||||
type: 'input',
|
||||
name: 'inputName',
|
||||
message: `What’s the name of the variable?`,
|
||||
});
|
||||
|
||||
if (!inputName) {
|
||||
output.error(`Name cannot be empty`);
|
||||
continue;
|
||||
}
|
||||
|
||||
envName = inputName;
|
||||
}
|
||||
|
||||
const envs = await getEnvVariables(output, client, project.id);
|
||||
const existing = new Set(
|
||||
envs.filter(r => r.key === envName).map(r => r.target)
|
||||
);
|
||||
|
||||
if (existing.size === 0) {
|
||||
output.error(
|
||||
`The Environment Variable ${param(envName)} was not found.\n`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (envTargets.length === 0) {
|
||||
const choices = getEnvTargetChoices().filter(c => existing.has(c.value));
|
||||
if (choices.length === 0) {
|
||||
output.error(
|
||||
`The Environment Variable ${param(
|
||||
envName
|
||||
)} was found but it is not assigned to any Environments.\n`
|
||||
);
|
||||
return 1;
|
||||
} else if (choices.length === 1) {
|
||||
envTargets = [choices[0].value];
|
||||
} else {
|
||||
const { inputTargets } = await inquirer.prompt({
|
||||
name: 'inputTargets',
|
||||
type: 'checkbox',
|
||||
message: `Remove ${envName} from which Environments (select multiple)?`,
|
||||
choices,
|
||||
});
|
||||
envTargets = inputTargets;
|
||||
}
|
||||
}
|
||||
|
||||
const skipConfirmation = opts['--yes'];
|
||||
if (
|
||||
!skipConfirmation &&
|
||||
!(await promptBool(
|
||||
output,
|
||||
`Removing Environment Variable ${param(
|
||||
envName
|
||||
)} from Project ${chalk.bold(project.name)}. Are you sure?`
|
||||
))
|
||||
) {
|
||||
output.log('Aborted');
|
||||
return 0;
|
||||
}
|
||||
|
||||
const rmStamp = stamp();
|
||||
|
||||
try {
|
||||
await withSpinner('Removing', async () => {
|
||||
for (const target of envTargets) {
|
||||
await removeEnvRecord(output, client, project.id, envName, target);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
if (isKnownError(error) && error.serverMessage) {
|
||||
output.error(error.serverMessage);
|
||||
return 1;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
output.print(
|
||||
`${prependEmoji(
|
||||
`Removed Environment Variable ${chalk.gray(rmStamp())}`,
|
||||
emoji('success')
|
||||
)}\n`
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ export default new Map([
|
||||
['domain', 'domains'],
|
||||
['domains', 'domains'],
|
||||
['downgrade', 'upgrade'],
|
||||
['env', 'env'],
|
||||
['help', 'help'],
|
||||
['init', 'init'],
|
||||
['inspect', 'inspect'],
|
||||
|
||||
@@ -185,7 +185,7 @@ function prepareFolder(cwd: string, folder: string, force?: boolean) {
|
||||
folder
|
||||
)}" already exists and is not an empty directory. You may use ${cmd(
|
||||
'--force'
|
||||
)} or ${cmd('--f')} to override it.`
|
||||
)} or ${cmd('-f')} to override it.`
|
||||
);
|
||||
}
|
||||
} else if (dest !== cwd) {
|
||||
|
||||
@@ -17,6 +17,7 @@ import getScope from '../util/get-scope.ts';
|
||||
import toHost from '../util/to-host';
|
||||
import parseMeta from '../util/parse-meta';
|
||||
import { isValidName } from '../util/is-valid-name';
|
||||
import getCommandFlags from '../util/get-command-flags';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
@@ -222,7 +223,7 @@ export default async function main(ctx) {
|
||||
debug(
|
||||
'No deployments: attempting to find aliases that matches supplied app name'
|
||||
);
|
||||
const aliases = await getAliases(now);
|
||||
const { aliases } = await getAliases(now);
|
||||
const item = aliases.find(e => e.uid === app || e.alias === app);
|
||||
|
||||
if (item) {
|
||||
@@ -332,11 +333,16 @@ export default async function main(ctx) {
|
||||
hsep: ' '.repeat(4),
|
||||
stringLength: strlen,
|
||||
}
|
||||
).replace(/^/gm, ' ')}\n\n`
|
||||
).replace(/^/gm, ' ')}\n`
|
||||
);
|
||||
|
||||
if (pagination && deployments.length === 20) {
|
||||
log(`To display the next page use the flag --next ${pagination.next}`);
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(argv, ['_', '--next']);
|
||||
log(
|
||||
`To display the next page run ${cmd(
|
||||
`now ls${app ? ' ' + app : ''}${flags} --next ${pagination.next}`
|
||||
)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,10 @@ export default async function main(ctx) {
|
||||
}
|
||||
|
||||
aliases = await Promise.all(
|
||||
deployments.map(depl => getAliases(client, depl.uid))
|
||||
deployments.map(async depl => {
|
||||
const { aliases } = await getAliases(client, depl.uid);
|
||||
return aliases;
|
||||
})
|
||||
);
|
||||
} finally {
|
||||
cancelWait();
|
||||
|
||||
@@ -110,7 +110,7 @@ const main = async ctx => {
|
||||
}
|
||||
|
||||
try {
|
||||
await run({ output, token, contextName, currentTeam });
|
||||
await run({ output, token, contextName, currentTeam, ctx });
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
exit(1);
|
||||
@@ -126,7 +126,7 @@ export default async ctx => {
|
||||
}
|
||||
};
|
||||
|
||||
async function run({ output, token, contextName, currentTeam }) {
|
||||
async function run({ output, token, contextName, currentTeam, ctx }) {
|
||||
const secrets = new NowSecrets({ apiUrl, token, debug, currentTeam });
|
||||
const args = argv._.slice(1);
|
||||
const start = Date.now();
|
||||
@@ -260,7 +260,28 @@ async function run({ output, token, contextName, currentTeam }) {
|
||||
return exit(1);
|
||||
}
|
||||
|
||||
const [name, value] = args;
|
||||
const [name, parsedValue] = args;
|
||||
const [originalName, originalValue] = ctx.argv.slice(-2);
|
||||
|
||||
let value = parsedValue;
|
||||
if (
|
||||
name === originalName &&
|
||||
typeof parsedValue === 'boolean' &&
|
||||
parsedValue !== originalValue
|
||||
) {
|
||||
// Corner case where `mri` transforms the secret value into a boolean because
|
||||
// it starts with a `-` so it thinks its a flag, so we use the original value instead.
|
||||
value = originalValue;
|
||||
}
|
||||
|
||||
if (typeof value === 'boolean') {
|
||||
const example = chalk.cyan(`$ now secret add -- "${name}"`);
|
||||
console.log(
|
||||
`If your secret starts with '-', make sure to terminate command options with double dash and wrap it in quotes. Example: \n ${example} `
|
||||
);
|
||||
return exit(1);
|
||||
}
|
||||
|
||||
await secrets.add(name, value);
|
||||
const elapsed = ms(new Date() - start);
|
||||
|
||||
|
||||
@@ -216,6 +216,33 @@ export type DNSRecordData =
|
||||
| SRVRecordData
|
||||
| MXRecordData;
|
||||
|
||||
export interface Secret {
|
||||
uid: string;
|
||||
name: string;
|
||||
value: string;
|
||||
teamId?: string;
|
||||
userId?: string;
|
||||
projectId?: string;
|
||||
created: string;
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
export enum ProjectEnvTarget {
|
||||
Production = 'production',
|
||||
Preview = 'preview',
|
||||
Development = 'development',
|
||||
}
|
||||
|
||||
export interface ProjectEnvVariable {
|
||||
key: string;
|
||||
value: string;
|
||||
configurationId?: string | null;
|
||||
createdAt?: number;
|
||||
updatedAt?: number;
|
||||
target?: ProjectEnvTarget;
|
||||
system?: boolean;
|
||||
}
|
||||
|
||||
export interface Project {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -238,3 +265,9 @@ export interface ProjectLink {
|
||||
projectId: string;
|
||||
orgId: string;
|
||||
}
|
||||
|
||||
export interface PaginationOptions {
|
||||
prev: number;
|
||||
count: number;
|
||||
next?: number;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ export default async function deploymentIsAliased(
|
||||
client: Client,
|
||||
deployment: Deployment
|
||||
) {
|
||||
const aliases = await getAliases(client);
|
||||
const { aliases } = await getAliases(client);
|
||||
return aliases.some(alias => alias.deploymentId === deployment.uid);
|
||||
}
|
||||
|
||||
@@ -7,11 +7,16 @@ type Response = {
|
||||
|
||||
export default async function getAliases(
|
||||
client: Client,
|
||||
deploymentId?: string
|
||||
deploymentId?: string,
|
||||
next?: number
|
||||
) {
|
||||
let aliasUrl = `/v3/now/aliases?limit=20`;
|
||||
if (next) {
|
||||
aliasUrl += `&until=${next}`;
|
||||
}
|
||||
const to = deploymentId
|
||||
? `/now/deployments/${deploymentId}/aliases`
|
||||
: '/now/aliases';
|
||||
: aliasUrl;
|
||||
const payload = await client.fetch<Response>(to);
|
||||
return payload.aliases || [];
|
||||
return payload;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@ import getAliases from './get-aliases';
|
||||
import { Alias } from '../../types';
|
||||
|
||||
export default async function getDomainAliases(client: Client, domain: string) {
|
||||
const aliases = await getAliases(client);
|
||||
const { aliases } = await getAliases(client);
|
||||
return aliases.filter((alias: Alias) => alias.alias.endsWith(domain));
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ export default class Client extends EventEmitter {
|
||||
_apiUrl: string;
|
||||
_debug: boolean;
|
||||
_forceNew: boolean;
|
||||
_withCache: boolean;
|
||||
_output: Output;
|
||||
_token: string;
|
||||
currentTeam?: string;
|
||||
@@ -30,18 +31,21 @@ export default class Client extends EventEmitter {
|
||||
token,
|
||||
currentTeam,
|
||||
forceNew = false,
|
||||
withCache = false,
|
||||
debug = false,
|
||||
}: {
|
||||
apiUrl: string;
|
||||
token: string;
|
||||
currentTeam?: string;
|
||||
forceNew?: boolean;
|
||||
withCache?: boolean;
|
||||
debug?: boolean;
|
||||
}) {
|
||||
super();
|
||||
this._token = token;
|
||||
this._debug = debug;
|
||||
this._forceNew = forceNew;
|
||||
this._withCache = withCache;
|
||||
this._output = createOutput({ debug });
|
||||
this._apiUrl = apiUrl;
|
||||
this._onRetry = this._onRetry.bind(this);
|
||||
|
||||
@@ -66,6 +66,7 @@ export default async function processDeployment({
|
||||
quiet: boolean;
|
||||
nowConfig?: NowConfig;
|
||||
force?: boolean;
|
||||
withCache?: boolean;
|
||||
org: Org;
|
||||
projectName: string;
|
||||
isSettingUpProject: boolean;
|
||||
@@ -82,6 +83,7 @@ export default async function processDeployment({
|
||||
requestBody,
|
||||
deployStamp,
|
||||
force,
|
||||
withCache,
|
||||
nowConfig,
|
||||
quiet,
|
||||
} = args;
|
||||
@@ -99,6 +101,7 @@ export default async function processDeployment({
|
||||
userAgent: ua,
|
||||
path: paths[0],
|
||||
force,
|
||||
withCache,
|
||||
skipAutoDetectionConfirmation,
|
||||
};
|
||||
|
||||
@@ -205,6 +208,9 @@ export default async function processDeployment({
|
||||
}
|
||||
|
||||
if (event.type === 'canceled') {
|
||||
if (queuedSpinner) {
|
||||
queuedSpinner();
|
||||
}
|
||||
if (buildSpinner) {
|
||||
buildSpinner();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ import {
|
||||
BuilderOutput,
|
||||
BuildResultV3,
|
||||
BuilderOutputs,
|
||||
BuilderParams,
|
||||
EnvConfigs,
|
||||
} from './types';
|
||||
import { normalizeRoutes } from '@now/routing-utils';
|
||||
import getUpdateCommand from '../get-update-command';
|
||||
@@ -45,7 +47,7 @@ const treeKill = promisify(_treeKill);
|
||||
|
||||
async function createBuildProcess(
|
||||
match: BuildMatch,
|
||||
buildEnv: EnvConfig,
|
||||
envConfigs: EnvConfigs,
|
||||
workPath: string,
|
||||
output: Output,
|
||||
yarnPath?: string
|
||||
@@ -64,7 +66,7 @@ async function createBuildProcess(
|
||||
const env: EnvConfig = {
|
||||
...process.env,
|
||||
PATH,
|
||||
...buildEnv,
|
||||
...envConfigs.allEnv,
|
||||
NOW_REGION: 'dev1',
|
||||
};
|
||||
|
||||
@@ -109,7 +111,7 @@ export async function executeBuild(
|
||||
builderWithPkg: { runInProcess, builder, package: pkg },
|
||||
} = match;
|
||||
const { entrypoint } = match;
|
||||
const { env, debug, buildEnv, yarnPath, cwd: workPath } = devServer;
|
||||
const { debug, envConfigs, yarnPath, cwd: workPath } = devServer;
|
||||
|
||||
const startTime = Date.now();
|
||||
const showBuildTimestamp =
|
||||
@@ -131,14 +133,14 @@ export async function executeBuild(
|
||||
devServer.output.debug(`Creating build process for ${entrypoint}`);
|
||||
buildProcess = await createBuildProcess(
|
||||
match,
|
||||
buildEnv,
|
||||
envConfigs,
|
||||
workPath,
|
||||
devServer.output,
|
||||
yarnPath
|
||||
);
|
||||
}
|
||||
|
||||
const buildParams = {
|
||||
const buildParams: BuilderParams = {
|
||||
files,
|
||||
entrypoint,
|
||||
workPath,
|
||||
@@ -148,8 +150,10 @@ export async function executeBuild(
|
||||
requestPath,
|
||||
filesChanged,
|
||||
filesRemoved,
|
||||
env,
|
||||
buildEnv,
|
||||
// This env distiniction is only necessary to maintain
|
||||
// backwards compatibility with the `@now/next` builder.
|
||||
env: envConfigs.runEnv,
|
||||
buildEnv: envConfigs.buildEnv,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -361,7 +365,7 @@ export async function executeBuild(
|
||||
Variables: {
|
||||
...nowConfig.env,
|
||||
...asset.environment,
|
||||
...env,
|
||||
...envConfigs.runEnv,
|
||||
NOW_REGION: 'dev1',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -54,16 +54,18 @@ export async function devRouter(
|
||||
missRoutes?: RouteConfig[],
|
||||
phase?: HandleValue | null
|
||||
): Promise<RouteResult> {
|
||||
let found: RouteResult | undefined;
|
||||
let result: RouteResult | undefined;
|
||||
let { query, pathname: reqPathname = '/' } = url.parse(reqUrl, true);
|
||||
const combinedHeaders: HttpHeadersConfig = { ...previousHeaders };
|
||||
let status: number | undefined;
|
||||
let isContinue = false;
|
||||
|
||||
// Try route match
|
||||
if (routes) {
|
||||
let idx = -1;
|
||||
for (const routeConfig of routes) {
|
||||
idx++;
|
||||
isContinue = false;
|
||||
|
||||
if (isHandler(routeConfig)) {
|
||||
// We don't expect any Handle, only Source routes
|
||||
@@ -110,6 +112,7 @@ export async function devRouter(
|
||||
status = routeConfig.status;
|
||||
}
|
||||
reqPathname = destPath;
|
||||
isContinue = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -149,9 +152,10 @@ export async function devRouter(
|
||||
|
||||
const isDestUrl = isURL(destPath);
|
||||
if (isDestUrl) {
|
||||
found = {
|
||||
result = {
|
||||
found: true,
|
||||
dest: destPath,
|
||||
continue: isContinue,
|
||||
userDest: false,
|
||||
isDestUrl,
|
||||
status: routeConfig.status || status,
|
||||
@@ -167,9 +171,10 @@ export async function devRouter(
|
||||
destPath = `/${destPath}`;
|
||||
}
|
||||
const { pathname, query } = url.parse(destPath, true);
|
||||
found = {
|
||||
result = {
|
||||
found: true,
|
||||
dest: pathname || '/',
|
||||
continue: isContinue,
|
||||
userDest: Boolean(routeConfig.dest),
|
||||
isDestUrl,
|
||||
status: routeConfig.status || status,
|
||||
@@ -185,10 +190,11 @@ export async function devRouter(
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
found = {
|
||||
if (!result) {
|
||||
result = {
|
||||
found: false,
|
||||
dest: reqPathname,
|
||||
continue: isContinue,
|
||||
status,
|
||||
isDestUrl: false,
|
||||
uri_args: query,
|
||||
@@ -197,5 +203,5 @@ export async function devRouter(
|
||||
};
|
||||
}
|
||||
|
||||
return found;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -85,6 +85,8 @@ import {
|
||||
ListenSpec,
|
||||
RouteConfig,
|
||||
RouteResult,
|
||||
HttpHeadersConfig,
|
||||
EnvConfigs,
|
||||
} from './types';
|
||||
|
||||
interface FSEvent {
|
||||
@@ -108,8 +110,7 @@ export default class DevServer {
|
||||
public cwd: string;
|
||||
public debug: boolean;
|
||||
public output: Output;
|
||||
public env: EnvConfig;
|
||||
public buildEnv: EnvConfig;
|
||||
public envConfigs: EnvConfigs;
|
||||
public frameworkSlug: string | null;
|
||||
public files: BuilderInputs;
|
||||
public yarnPath: string;
|
||||
@@ -141,8 +142,7 @@ export default class DevServer {
|
||||
this.cwd = cwd;
|
||||
this.debug = options.debug;
|
||||
this.output = options.output;
|
||||
this.env = {};
|
||||
this.buildEnv = {};
|
||||
this.envConfigs = { buildEnv: {}, runEnv: {}, allEnv: {} };
|
||||
this.files = {};
|
||||
this.address = '';
|
||||
this.devCommand = options.devCommand;
|
||||
@@ -739,13 +739,13 @@ export default class DevServer {
|
||||
// Retrieve the path of the native module
|
||||
const nowConfig = await this.getNowConfig(false);
|
||||
const nowConfigBuild = nowConfig.build || {};
|
||||
const [env, buildEnv] = await Promise.all([
|
||||
const [runEnv, buildEnv] = await Promise.all([
|
||||
this.getLocalEnv('.env', nowConfig.env),
|
||||
this.getLocalEnv('.env.build', nowConfigBuild.env),
|
||||
]);
|
||||
Object.assign(process.env, buildEnv);
|
||||
this.env = env;
|
||||
this.buildEnv = buildEnv;
|
||||
const allEnv = { ...buildEnv, ...runEnv };
|
||||
Object.assign(process.env, allEnv);
|
||||
this.envConfigs = { buildEnv, runEnv, allEnv };
|
||||
|
||||
const opts = { output: this.output, isBuilds: true };
|
||||
const files = await getFiles(this.cwd, nowConfig, opts);
|
||||
@@ -1264,19 +1264,25 @@ export default class DevServer {
|
||||
let routeResult: RouteResult | null = null;
|
||||
let match: BuildMatch | null = null;
|
||||
let statusCode: number | undefined;
|
||||
let prevUrl = req.url;
|
||||
let prevHeaders: HttpHeadersConfig = {};
|
||||
|
||||
for (const phase of phases) {
|
||||
statusCode = undefined;
|
||||
const phaseRoutes = handleMap.get(phase) || [];
|
||||
routeResult = await devRouter(
|
||||
req.url,
|
||||
prevUrl,
|
||||
req.method,
|
||||
phaseRoutes,
|
||||
this,
|
||||
undefined,
|
||||
prevHeaders,
|
||||
missRoutes,
|
||||
phase
|
||||
);
|
||||
prevUrl =
|
||||
routeResult.continue && routeResult.dest ? routeResult.dest : req.url;
|
||||
prevHeaders =
|
||||
routeResult.continue && routeResult.headers ? routeResult.headers : {};
|
||||
|
||||
if (routeResult.isDestUrl) {
|
||||
// Mix the `routes` result dest query params into the req path
|
||||
@@ -1682,8 +1688,7 @@ export default class DevServer {
|
||||
FORCE_COLOR: process.stdout.isTTY ? '1' : '0',
|
||||
...(this.frameworkSlug === 'create-react-app' ? { BROWSER: 'none' } : {}),
|
||||
...process.env,
|
||||
...this.buildEnv,
|
||||
...(this.frameworkSlug === 'nextjs' ? this.env : {}),
|
||||
...this.envConfigs.allEnv,
|
||||
NOW_REGION: 'dev1',
|
||||
PORT: `${port}`,
|
||||
};
|
||||
|
||||
@@ -26,6 +26,23 @@ export interface EnvConfig {
|
||||
[name: string]: string | undefined;
|
||||
}
|
||||
|
||||
export interface EnvConfigs {
|
||||
/**
|
||||
* environment variables from `.env.build` file (deprecated)
|
||||
*/
|
||||
buildEnv: EnvConfig;
|
||||
|
||||
/**
|
||||
* environment variables from `.env` file
|
||||
*/
|
||||
runEnv: EnvConfig;
|
||||
|
||||
/**
|
||||
* environment variables from `.env` and `.env.build`
|
||||
*/
|
||||
allEnv: EnvConfig;
|
||||
}
|
||||
|
||||
export interface BuildMatch extends BuildConfig {
|
||||
entrypoint: string;
|
||||
builderWithPkg: BuilderWithPackage;
|
||||
@@ -147,6 +164,8 @@ export interface RouteResult {
|
||||
found: boolean;
|
||||
// "dest": <string of the dest, either file for lambda or full url for remote>
|
||||
dest: string;
|
||||
// `true` if last route in current phase matched but set `continue: true`
|
||||
continue: boolean;
|
||||
// "status": <integer in case exit code is intended to be changed>
|
||||
status?: number;
|
||||
// "headers": <object of the added response header values>
|
||||
|
||||
@@ -42,6 +42,6 @@ function getAddDomainName(domainNames: string[]) {
|
||||
}
|
||||
|
||||
async function getDomainNames(client: Client, contextName: string) {
|
||||
const domains = await getDomains(client, contextName);
|
||||
const { domains } = await getDomains(client, contextName);
|
||||
return domains.map(domain => domain.name).sort((a, b) => a.localeCompare(b));
|
||||
}
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
import chalk from 'chalk';
|
||||
import { Domain } from '../../types';
|
||||
import { Domain, PaginationOptions } from '../../types';
|
||||
import Client from '../client';
|
||||
import wait from '../output/wait';
|
||||
|
||||
type Response = {
|
||||
domains: Domain[];
|
||||
pagination: PaginationOptions;
|
||||
};
|
||||
|
||||
export default async function getDomains(client: Client, contextName: string) {
|
||||
export default async function getDomains(
|
||||
client: Client,
|
||||
contextName: string,
|
||||
next?: number
|
||||
) {
|
||||
let domainUrl = `/v5/domains?limit=20`;
|
||||
if (next) {
|
||||
domainUrl += `&until=${next}`;
|
||||
}
|
||||
const cancelWait = wait(`Fetching domains under ${chalk.bold(contextName)}`);
|
||||
const { domains } = await client.fetch<Response>('/v4/domains');
|
||||
const domains = await client.fetch<Response>(domainUrl);
|
||||
cancelWait();
|
||||
return domains.sort(
|
||||
(domainA, domainB) => domainB.createdAt - domainA.createdAt
|
||||
);
|
||||
return domains;
|
||||
}
|
||||
|
||||
61
packages/now-cli/src/util/env/add-env-record.ts
vendored
Normal file
61
packages/now-cli/src/util/env/add-env-record.ts
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Output } from '../output';
|
||||
import Client from '../client';
|
||||
import { Secret, ProjectEnvTarget, ProjectEnvVariable } from '../../types';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import slugify from '@sindresorhus/slugify';
|
||||
|
||||
export default async function addEnvRecord(
|
||||
output: Output,
|
||||
client: Client,
|
||||
projectId: string,
|
||||
envName: string,
|
||||
envValue: string | undefined,
|
||||
targets: ProjectEnvTarget[]
|
||||
): Promise<void> {
|
||||
output.debug(
|
||||
`Adding Environment Variable ${envName} to ${targets.length} targets`
|
||||
);
|
||||
|
||||
let values: string[] | undefined;
|
||||
|
||||
if (envValue) {
|
||||
const urlSecret = `/v2/now/secrets/${encodeURIComponent(envName)}`;
|
||||
const secrets = await Promise.all(
|
||||
targets.map(target =>
|
||||
client.fetch<Secret>(urlSecret, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
name: generateSecretName(envName, target),
|
||||
value: envValue,
|
||||
projectId: projectId,
|
||||
decryptable: target === ProjectEnvTarget.Development,
|
||||
}),
|
||||
})
|
||||
)
|
||||
);
|
||||
values = secrets.map(secret => secret.uid);
|
||||
}
|
||||
|
||||
const body = targets.map((target, i) => ({
|
||||
key: envName,
|
||||
value: values ? values[i] : '',
|
||||
target,
|
||||
}));
|
||||
|
||||
const urlProject = `/v4/projects/${projectId}/env`;
|
||||
await client.fetch<ProjectEnvVariable>(urlProject, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
}
|
||||
|
||||
const randomSecretSuffix = customAlphabet(
|
||||
'123456789abcdefghijklmnopqrstuvwxyz',
|
||||
4
|
||||
);
|
||||
|
||||
function generateSecretName(envName: string, target: ProjectEnvTarget) {
|
||||
return `${
|
||||
slugify(envName).substring(0, 80) // we truncate because the max secret length is 100
|
||||
}-${target}-${randomSecretSuffix()}`;
|
||||
}
|
||||
22
packages/now-cli/src/util/env/env-target.ts
vendored
Normal file
22
packages/now-cli/src/util/env/env-target.ts
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
import { ProjectEnvTarget } from '../../types';
|
||||
|
||||
function envTargets(): string[] {
|
||||
return Object.values(ProjectEnvTarget);
|
||||
}
|
||||
|
||||
export function getEnvTargetChoices() {
|
||||
return Object.entries(ProjectEnvTarget).map(([key, value]) => ({
|
||||
name: key,
|
||||
value: value,
|
||||
}));
|
||||
}
|
||||
|
||||
export function isValidEnvTarget(
|
||||
target?: string
|
||||
): target is ProjectEnvTarget | undefined {
|
||||
return typeof target === 'undefined' || envTargets().includes(target);
|
||||
}
|
||||
|
||||
export function getEnvTargetPlaceholder() {
|
||||
return `<${envTargets().join(' | ')}>`;
|
||||
}
|
||||
17
packages/now-cli/src/util/env/get-decrypted-secret.ts
vendored
Normal file
17
packages/now-cli/src/util/env/get-decrypted-secret.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Output } from '../output';
|
||||
import Client from '../client';
|
||||
import { Secret } from '../../types';
|
||||
|
||||
export default async function getDecryptedSecret(
|
||||
output: Output,
|
||||
client: Client,
|
||||
secretId: string
|
||||
): Promise<string> {
|
||||
if (!secretId) {
|
||||
return '';
|
||||
}
|
||||
output.debug(`Fetching decrypted secret ${secretId}`);
|
||||
const url = `/v2/now/secrets/${secretId}?decrypt=true`;
|
||||
const secret = await client.fetch<Secret>(url);
|
||||
return secret.value;
|
||||
}
|
||||
18
packages/now-cli/src/util/env/get-env-records.ts
vendored
Normal file
18
packages/now-cli/src/util/env/get-env-records.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Output } from '../output';
|
||||
import Client from '../client';
|
||||
import { ProjectEnvVariable, ProjectEnvTarget } from '../../types';
|
||||
|
||||
export default async function getEnvVariables(
|
||||
output: Output,
|
||||
client: Client,
|
||||
projectId: string,
|
||||
target?: ProjectEnvTarget
|
||||
): Promise<ProjectEnvVariable[]> {
|
||||
output.debug(
|
||||
`Fetching Environment Variables of project ${projectId} and target ${target}`
|
||||
);
|
||||
const qs = target ? `?target=${encodeURIComponent(target)}` : '';
|
||||
const url = `/v4/projects/${projectId}/env${qs}`;
|
||||
const records = await client.fetch<ProjectEnvVariable[]>(url);
|
||||
return records;
|
||||
}
|
||||
13
packages/now-cli/src/util/env/known-error.ts
vendored
Normal file
13
packages/now-cli/src/util/env/known-error.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
const knownErrorsCodes = new Set([
|
||||
'PAYMENT_REQUIRED',
|
||||
'BAD_REQUEST',
|
||||
'SYSTEM_ENV_WITH_VALUE',
|
||||
'RESERVED_ENV_VARIABLE',
|
||||
'ENV_ALREADY_EXISTS',
|
||||
'ENV_SHOULD_BE_A_SECRET',
|
||||
]);
|
||||
|
||||
export function isKnownError(error: { code?: string }) {
|
||||
const code = error && typeof error.code === 'string' ? error.code : '';
|
||||
return knownErrorsCodes.has(code.toUpperCase());
|
||||
}
|
||||
38
packages/now-cli/src/util/env/remove-env-record.ts
vendored
Normal file
38
packages/now-cli/src/util/env/remove-env-record.ts
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Output } from '../output';
|
||||
import Client from '../client';
|
||||
import { ProjectEnvTarget, Secret, ProjectEnvVariable } from '../../types';
|
||||
|
||||
export default async function removeEnvRecord(
|
||||
output: Output,
|
||||
client: Client,
|
||||
projectId: string,
|
||||
envName: string,
|
||||
target?: ProjectEnvTarget
|
||||
): Promise<void> {
|
||||
output.debug(
|
||||
`Removing Environment Variable ${envName} from target ${target}`
|
||||
);
|
||||
|
||||
const qs = target ? `?target=${encodeURIComponent(target)}` : '';
|
||||
const urlProject = `/v4/projects/${projectId}/env/${encodeURIComponent(
|
||||
envName
|
||||
)}${qs}`;
|
||||
|
||||
const env = await client.fetch<ProjectEnvVariable>(urlProject, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (env && env.value) {
|
||||
const idOrName = env.value.startsWith('@') ? env.value.slice(1) : env.value;
|
||||
const urlSecret = `/v2/now/secrets/${idOrName}`;
|
||||
const secret = await client.fetch<Secret>(urlSecret);
|
||||
|
||||
// Since integrations add global secrets, we must only delete if the secret was
|
||||
// specifically added to this project
|
||||
if (secret && secret.projectId === projectId) {
|
||||
await client.fetch<Secret>(urlSecret, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
20
packages/now-cli/src/util/get-command-flags.ts
Normal file
20
packages/now-cli/src/util/get-command-flags.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
This function returns the provided arguments from a command in a string format.
|
||||
|
||||
Example: if `argv` is { '--debug': true, '--all': true, '--scope': 'zeit' },
|
||||
the output will be '--debug --all --scope zeit'.
|
||||
|
||||
Flags can be excluded using the `excludeFlags` param.
|
||||
*/
|
||||
export default function getCommandFlags(
|
||||
argv: { [key: string]: any },
|
||||
excludeFlags: string[] = []
|
||||
) {
|
||||
const flags = Object.keys(argv)
|
||||
.filter(key => !excludeFlags.includes(key))
|
||||
.map(
|
||||
key => `${key}${typeof argv[key] !== 'boolean' ? ' ' + argv[key] : ''}`
|
||||
);
|
||||
|
||||
return flags.length > 0 ? ` ${flags.join(' ')}` : '';
|
||||
}
|
||||
9
packages/now-cli/src/util/get-invalid-subcommand.ts
Normal file
9
packages/now-cli/src/util/get-invalid-subcommand.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
type CommandConfig = {
|
||||
[command: string]: string[];
|
||||
};
|
||||
|
||||
export default function getInvalidSubcommand(config: CommandConfig) {
|
||||
return `Please specify a valid subcommand: ${Object.keys(config).join(
|
||||
' | '
|
||||
)}`;
|
||||
}
|
||||
@@ -29,12 +29,20 @@ const IS_WIN = process.platform.startsWith('win');
|
||||
const SEP = IS_WIN ? '\\' : '/';
|
||||
|
||||
export default class Now extends EventEmitter {
|
||||
constructor({ apiUrl, token, currentTeam, forceNew = false, debug = false }) {
|
||||
constructor({
|
||||
apiUrl,
|
||||
token,
|
||||
currentTeam,
|
||||
forceNew = false,
|
||||
withCache = false,
|
||||
debug = false,
|
||||
}) {
|
||||
super();
|
||||
|
||||
this._token = token;
|
||||
this._debug = debug;
|
||||
this._forceNew = forceNew;
|
||||
this._withCache = withCache;
|
||||
this._output = createOutput({ debug });
|
||||
this._apiUrl = apiUrl;
|
||||
this._onRetry = this._onRetry.bind(this);
|
||||
@@ -64,6 +72,7 @@ export default class Now extends EventEmitter {
|
||||
env,
|
||||
build,
|
||||
forceNew = false,
|
||||
withCache = false,
|
||||
target = null,
|
||||
deployStamp,
|
||||
projectSettings,
|
||||
@@ -155,6 +164,7 @@ export default class Now extends EventEmitter {
|
||||
meta,
|
||||
public: wantsPublic || nowConfig.public,
|
||||
forceNew,
|
||||
withCache,
|
||||
name,
|
||||
project,
|
||||
description,
|
||||
@@ -180,6 +190,7 @@ export default class Now extends EventEmitter {
|
||||
quiet,
|
||||
nowConfig,
|
||||
force: forceNew,
|
||||
withCache,
|
||||
org,
|
||||
projectName: name,
|
||||
isSettingUpProject,
|
||||
@@ -377,7 +388,12 @@ export default class Now extends EventEmitter {
|
||||
if (!app && !Object.keys(meta).length) {
|
||||
// Get the 20 latest projects and their latest deployment
|
||||
const query = new URLSearchParams({ limit: (20).toString() });
|
||||
const projects = await fetchRetry(`/v2/projects/?${query}`);
|
||||
if (nextTimestamp) {
|
||||
query.set('until', String(nextTimestamp));
|
||||
}
|
||||
const { projects, pagination } = await fetchRetry(
|
||||
`/v4/projects/?${query}`
|
||||
);
|
||||
|
||||
const deployments = await Promise.all(
|
||||
projects.map(async ({ id: projectId }) => {
|
||||
@@ -389,7 +405,7 @@ export default class Now extends EventEmitter {
|
||||
})
|
||||
);
|
||||
|
||||
return { deployments: deployments.filter(x => x) };
|
||||
return { deployments: deployments.filter(x => x), pagination };
|
||||
}
|
||||
|
||||
const query = new URLSearchParams();
|
||||
|
||||
13
packages/now-cli/src/util/input/read-standard-input.ts
Normal file
13
packages/now-cli/src/util/input/read-standard-input.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export default async function readStandardInput(): Promise<string> {
|
||||
return new Promise<string>(resolve => {
|
||||
setTimeout(() => resolve(''), 500);
|
||||
|
||||
if (process.stdin.isTTY) {
|
||||
// found tty so we know there is nothing piped to stdin
|
||||
resolve('');
|
||||
} else {
|
||||
process.stdin.setEncoding('utf8');
|
||||
process.stdin.once('data', resolve);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import chalk from 'chalk';
|
||||
import { Output } from './output';
|
||||
|
||||
async function promptBool(output: Output, message: string) {
|
||||
return new Promise(resolve => {
|
||||
async function promptBool(output: Output, message: string): Promise<boolean> {
|
||||
return new Promise<boolean>(resolve => {
|
||||
output.print(`${chalk.gray('>')} ${message} ${chalk.gray('[y/N] ')}`);
|
||||
process.stdin
|
||||
.on('data', d => {
|
||||
|
||||
14
packages/now-cli/test/dev-router.unit.js
vendored
14
packages/now-cli/test/dev-router.unit.js
vendored
@@ -10,6 +10,7 @@ test('[dev-router] 301 redirection', async t => {
|
||||
t.deepEqual(result, {
|
||||
found: true,
|
||||
dest: '/redirect',
|
||||
continue: false,
|
||||
status: 301,
|
||||
headers: { location: 'https://zeit.co' },
|
||||
uri_args: {},
|
||||
@@ -28,6 +29,7 @@ test('[dev-router] captured groups', async t => {
|
||||
t.deepEqual(result, {
|
||||
found: true,
|
||||
dest: '/endpoints/user.js',
|
||||
continue: false,
|
||||
status: undefined,
|
||||
headers: {},
|
||||
uri_args: {},
|
||||
@@ -46,6 +48,7 @@ test('[dev-router] named groups', async t => {
|
||||
t.deepEqual(result, {
|
||||
found: true,
|
||||
dest: '/user.js',
|
||||
continue: false,
|
||||
status: undefined,
|
||||
headers: {},
|
||||
uri_args: { id: '123' },
|
||||
@@ -69,6 +72,7 @@ test('[dev-router] optional named groups', async t => {
|
||||
t.deepEqual(result, {
|
||||
found: true,
|
||||
dest: '/api/functions/hello/index.js',
|
||||
continue: false,
|
||||
status: undefined,
|
||||
headers: {},
|
||||
uri_args: { name: '' },
|
||||
@@ -88,6 +92,7 @@ test('[dev-router] proxy_pass', async t => {
|
||||
t.deepEqual(result, {
|
||||
found: true,
|
||||
dest: 'https://zeit.co',
|
||||
continue: false,
|
||||
status: undefined,
|
||||
headers: {},
|
||||
uri_args: {},
|
||||
@@ -109,6 +114,7 @@ test('[dev-router] methods', async t => {
|
||||
t.deepEqual(result, {
|
||||
found: true,
|
||||
dest: '/get',
|
||||
continue: false,
|
||||
status: undefined,
|
||||
headers: {},
|
||||
uri_args: {},
|
||||
@@ -123,6 +129,7 @@ test('[dev-router] methods', async t => {
|
||||
t.deepEqual(result, {
|
||||
found: true,
|
||||
dest: '/post',
|
||||
continue: false,
|
||||
status: undefined,
|
||||
headers: {},
|
||||
uri_args: {},
|
||||
@@ -141,6 +148,7 @@ test('[dev-router] match without prefix slash', async t => {
|
||||
t.deepEqual(result, {
|
||||
found: true,
|
||||
dest: '/endpoints/user.js',
|
||||
continue: false,
|
||||
status: undefined,
|
||||
headers: {},
|
||||
uri_args: {},
|
||||
@@ -164,6 +172,7 @@ test('[dev-router] match with needed prefixed slash', async t => {
|
||||
t.deepEqual(result, {
|
||||
found: true,
|
||||
dest: '/some/dest',
|
||||
continue: false,
|
||||
userDest: true,
|
||||
isDestUrl: false,
|
||||
phase: undefined,
|
||||
@@ -197,6 +206,7 @@ test('[dev-router] `continue: true` with fallthrough', async t => {
|
||||
t.deepEqual(result, {
|
||||
found: false,
|
||||
dest: '/_next/static/chunks/0.js',
|
||||
continue: true,
|
||||
isDestUrl: false,
|
||||
phase: undefined,
|
||||
status: undefined,
|
||||
@@ -230,6 +240,7 @@ test('[dev-router] `continue: true` with match', async t => {
|
||||
t.deepEqual(result, {
|
||||
found: true,
|
||||
dest: '/hi',
|
||||
continue: false,
|
||||
status: undefined,
|
||||
userDest: true,
|
||||
isDestUrl: false,
|
||||
@@ -253,6 +264,7 @@ test('[dev-router] match with catch-all with prefix slash', async t => {
|
||||
t.deepEqual(result, {
|
||||
found: true,
|
||||
dest: '/www/',
|
||||
continue: false,
|
||||
userDest: true,
|
||||
isDestUrl: false,
|
||||
phase: undefined,
|
||||
@@ -271,6 +283,7 @@ test('[dev-router] match with catch-all with no prefix slash', async t => {
|
||||
t.deepEqual(result, {
|
||||
found: true,
|
||||
dest: '/www/',
|
||||
continue: false,
|
||||
userDest: true,
|
||||
isDestUrl: false,
|
||||
phase: undefined,
|
||||
@@ -295,6 +308,7 @@ test('[dev-router] `continue: true` with `dest`', async t => {
|
||||
t.deepEqual(result, {
|
||||
found: true,
|
||||
dest: 'http://localhost:5000/a/foo',
|
||||
continue: false,
|
||||
status: undefined,
|
||||
headers: {},
|
||||
uri_args: {},
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
SKIP_PREFLIGHT_CHECK=true
|
||||
BROWSER=none
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"url": "https://github.com/marko-js-samples/marko-starter-demo"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production marko-starter build && mv dist public",
|
||||
"build": "NODE_ENV=production marko-starter build && rm -rf public && mv dist public",
|
||||
"format": "prettier src/**/*.{js,css,less} --write && marko-prettyprint src",
|
||||
"lint": "eslint src",
|
||||
"serve": "NODE_ENV=production marko-starter serve-static",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user