esa-cli 1.0.10 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +206 -22
  2. package/bin/enter.cjs +45 -12
  3. package/dist/bin/enter.cjs +45 -12
  4. package/dist/commands/commit/index.js +5 -4
  5. package/dist/commands/deployments/delete.js +1 -1
  6. package/dist/commands/deployments/list.js +1 -1
  7. package/dist/commands/dev/build.js +4 -1
  8. package/dist/commands/dev/doProcess.js +94 -36
  9. package/dist/commands/domain/add.js +1 -1
  10. package/dist/commands/domain/delete.js +2 -3
  11. package/dist/commands/domain/list.js +1 -1
  12. package/dist/commands/init/helper.js +12 -8
  13. package/dist/commands/init/index.js +3 -4
  14. package/dist/commands/login/index.js +9 -5
  15. package/dist/commands/logout.js +3 -3
  16. package/dist/commands/route/add.js +1 -1
  17. package/dist/commands/route/delete.js +1 -1
  18. package/dist/commands/route/list.js +1 -1
  19. package/dist/commands/routine/delete.js +1 -2
  20. package/dist/commands/routine/list.js +1 -1
  21. package/dist/commands/site/list.js +1 -1
  22. package/dist/commands/utils.js +9 -3
  23. package/dist/components/mutiSelectTable.js +16 -83
  24. package/dist/components/routeBuilder.js +30 -54
  25. package/dist/components/selectInput.js +27 -13
  26. package/dist/docs/Commands_en.md +14 -2
  27. package/dist/docs/Commands_zh_CN.md +11 -1
  28. package/dist/i18n/locales.json +1 -1
  29. package/dist/index.js +8 -3
  30. package/dist/libs/api.js +0 -8
  31. package/dist/libs/logger.js +7 -2
  32. package/dist/utils/fileMd5.js +13 -3
  33. package/dist/utils/fileUtils/base.js +14 -18
  34. package/dist/utils/fileUtils/index.js +20 -0
  35. package/package.json +18 -20
  36. package/zh_CN.md +2 -2
  37. package/dist/components/descriptionInput.js +0 -38
  38. package/dist/components/filterSelector.js +0 -132
  39. package/dist/components/selectItem.js +0 -6
  40. package/dist/components/yesNoPrompt.js +0 -9
package/README.md CHANGED
@@ -1,6 +1,13 @@
1
- # Install/Update ESA CLI
1
+ # Alibaba Cloud ESA CLI
2
2
 
3
- ESA CLI is a command-line tool for building with Alibaba Cloud ESA Functions and Pages.
3
+ [![npm version](https://img.shields.io/npm/v/esa-cli?style=flat-square)](https://www.npmjs.com/package/esa-cli)
4
+ [![CI](https://img.shields.io/github/actions/workflow/status/aliyun/alibabacloud-esa-cli/main.yml?style=flat-square&label=CI)](https://github.com/aliyun/alibabacloud-esa-cli/actions/workflows/main.yml)
5
+ ![Node.js](https://img.shields.io/badge/Node.js-18%20%7C%2020%20%7C%2022-3c873a?style=flat-square)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-blue?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org)
7
+
8
+ ESA CLI is a command-line tool for building, developing, and deploying [Alibaba Cloud ESA](https://www.alibabacloud.com/help/en/edge-security-acceleration/esa/user-guide/what-is-er/) Functions & Pages projects.
9
+
10
+ It helps you create projects from templates, run Functions & Pages locally, package static assets and edge functions, publish code versions, deploy to staging or production, and manage domains, routes, and deployments from your terminal.
4
11
 
5
12
  <p>
6
13
  <a href="https://discord.gg/BxcRVEeh">
@@ -9,54 +16,231 @@ ESA CLI is a command-line tool for building with Alibaba Cloud ESA Functions and
9
16
  <a href="https://discord.gg/SHYe5926" style="margin-left:8px;">
10
17
  <img alt="Discord EN" src="https://img.shields.io/badge/Discord-English-5865F2?logo=discord&logoColor=white" />
11
18
  </a>
12
- </p>
19
+ </p>
20
+
21
+ [Get started](#getting-started) • [Configuration](#configuration) • [Commands](#commands) • [Development](#development) • [Resources](#resources)
13
22
 
14
- ## Install ESA CLI
23
+ ## Features
15
24
 
16
- ### Prerequisites
25
+ - **Project scaffolding**: initialize React, Vue, Next.js static export, Astro, and React Router projects with ESA-ready configuration.
26
+ - **Local development**: run a local ESA-compatible server with automatic rebuilds, local upstream proxying, mock KV, and cache support.
27
+ - **Static + dynamic deployments**: deploy static Pages, edge functions, or hybrid Functions & Pages projects.
28
+ - **Versioned releases**: create code versions with `commit`, deploy existing versions, or split traffic between two versions by percentage.
29
+ - **Account operations**: login with AK/SK or STS credentials, list projects and sites, and manage custom domains and routes.
30
+ - **Bilingual CLI**: switch the CLI language between English and Simplified Chinese.
17
31
 
18
- - Node.js: 20.x or higher (supports 20.x, 22.x)
19
- - OS: macOS (x86, Apple Silicon), Linux
20
- - Recommended to use a Node version manager like Volta or nvm to avoid permission issues and easily switch Node.js versions.
32
+ ## Prerequisites
33
+
34
+ - Node.js 20 or newer is recommended. CI currently covers Node.js 18.x, 20.x, and 22.x.
35
+ - npm, pnpm, yarn, or Bun for installing the package.
36
+ - An Alibaba Cloud account with ESA Functions & Pages access.
37
+
38
+ > [!TIP]
39
+ > For team projects, install `esa-cli` as a dev dependency so everyone runs the same CLI version.
21
40
 
22
41
  ## Installation
23
42
 
24
- To ensure consistent collaboration across your team, we recommend installing `esa-cli` as a dev dependency in your project. This helps everyone use the same version.
43
+ Install locally in your project:
25
44
 
26
45
  ```bash
27
46
  npm i -D esa-cli@latest
28
47
  ```
29
48
 
30
- Alternatively, install globally to use the `esa-cli` command system-wide:
49
+ Or install globally:
31
50
 
32
51
  ```bash
33
52
  npm i -g esa-cli@latest
34
53
  ```
35
54
 
36
- > [!TIP]
37
- > When `esa-cli` is not previously installed, `npx` will fetch and run the latest version from the registry.
38
-
39
- ## Check your ESA CLI version
55
+ The package exposes both `esa-cli` and the shorter `esa` command after installation.
40
56
 
41
- **To check your ESA CLI version, run:**
57
+ Check the installed version:
42
58
 
43
59
  ```bash
44
60
  npx esa-cli --version
45
- # or
46
61
  npx esa-cli -v
47
62
  ```
48
63
 
49
- ## Update ESA CLI
64
+ ## Getting Started
65
+
66
+ ### 1. Authenticate
50
67
 
51
- **To update ESA CLI to the latest version, run:**
68
+ Run the interactive login flow:
52
69
 
53
70
  ```bash
54
- npm i -D esa-cli@latest
71
+ npx esa-cli login
72
+ ```
73
+
74
+ You can also pass credentials directly:
75
+
76
+ ```bash
77
+ npx esa-cli login --access-key-id <ACCESS_KEY_ID> --access-key-secret <ACCESS_KEY_SECRET>
78
+ ```
79
+
80
+ For temporary credentials, pass an STS token:
81
+
82
+ ```bash
83
+ npx esa-cli login --sts-token "AccessKeyId,AccessKeySecret,SecurityToken"
84
+ ```
85
+
86
+ Environment variables are supported:
87
+
88
+ ```bash
89
+ export ALIBABA_CLOUD_ACCESS_KEY_ID=<ACCESS_KEY_ID>
90
+ export ALIBABA_CLOUD_ACCESS_KEY_SECRET=<ACCESS_KEY_SECRET>
91
+ export ALIBABA_CLOUD_SECURITY_TOKEN=<SECURITY_TOKEN>
92
+ npx esa-cli login
93
+ ```
94
+
95
+ The legacy `ESA_ACCESS_KEY_ID`, `ESA_ACCESS_KEY_SECRET`, and `ESA_SECURITY_TOKEN` variables remain supported as fallbacks. When both forms are set, the standard `ALIBABA_CLOUD_*` variables take precedence.
96
+
97
+ ### 2. Create a Project
98
+
99
+ Create a new ESA-ready application from a template:
100
+
101
+ ```bash
102
+ npx esa-cli init my-esa-app
103
+ cd my-esa-app
104
+ ```
105
+
106
+ You can also make the template choice explicit:
107
+
108
+ ```bash
109
+ npx esa-cli init my-esa-app --framework react --language typescript
55
110
  ```
56
111
 
57
- ## Related Documentation
112
+ ### 3. Develop Locally
113
+
114
+ Start the local development server:
115
+
116
+ ```bash
117
+ npx esa-cli dev
118
+ ```
119
+
120
+ Use a custom port or local upstream when needed:
121
+
122
+ ```bash
123
+ npx esa-cli dev --port 18080 --local-upstream https://example.com
124
+ ```
125
+
126
+ ### 4. Commit and Deploy
127
+
128
+ Create a code version without deploying it:
129
+
130
+ ```bash
131
+ npx esa-cli commit --description "Prepare release"
132
+ ```
133
+
134
+ Deploy the current project:
135
+
136
+ ```bash
137
+ npx esa-cli deploy --description "Initial release"
138
+ ```
139
+
140
+ Deploy an existing version:
141
+
142
+ ```bash
143
+ npx esa-cli deploy --version <VERSION_ID> --environment production
144
+ ```
145
+
146
+ Split traffic between two versions:
147
+
148
+ ```bash
149
+ npx esa-cli deploy --versions v1:80,v2:20 --environment production
150
+ ```
151
+
152
+ ## Configuration
153
+
154
+ ESA CLI looks for `esa.jsonc` or `esa.toml` from the current directory upward. `esa.jsonc` is recommended for new projects.
155
+
156
+ ```jsonc
157
+ {
158
+ "name": "my-esa-app",
159
+ "description": "My ESA Functions & Pages project",
160
+ "entry": "./src/index.ts",
161
+ "assets": {
162
+ "directory": "./dist",
163
+ "notFoundStrategy": "singlePageApplication"
164
+ },
165
+ "dev": {
166
+ "port": 18080,
167
+ "localUpstream": "https://example.com"
168
+ }
169
+ }
170
+ ```
171
+
172
+ You can configure only `entry` for an edge function, only `assets.directory` for static Pages, or both for a hybrid project.
173
+
174
+ > [!NOTE]
175
+ > If both a function entry and `assets.notFoundStrategy` are configured, navigation requests are handled by the static fallback strategy instead of the function entry.
176
+
177
+ See the [ESA Configuration Guide](https://github.com/aliyun/alibabacloud-esa-cli/blob/master/docs/Config_en.md) for all fields and examples.
178
+
179
+ ## Commands
180
+
181
+ | Command | Description |
182
+ | --- | --- |
183
+ | `esa-cli init [name]` | Create a new project from framework templates. |
184
+ | `esa-cli login` | Authenticate with AK/SK, STS token, or supported environment variables. |
185
+ | `esa-cli dev [entry]` | Start a local server for Functions & Pages development. |
186
+ | `esa-cli commit [entry]` | Package code and assets, then save them as a new version. |
187
+ | `esa-cli deploy [entry]` | Generate or select a version and deploy it to staging, production, or both. |
188
+ | `esa-cli deployments list` | List code versions for the current Functions & Pages project. |
189
+ | `esa-cli deployments delete` | Delete one or more code versions. |
190
+ | `esa-cli project list` | List Functions & Pages projects in the current account. |
191
+ | `esa-cli project delete` | Delete a Functions & Pages project. |
192
+ | `esa-cli site list` | List activated ESA sites. |
193
+ | `esa-cli domain add/list/delete` | Manage custom domain bindings. |
194
+ | `esa-cli route add/list/delete` | Manage route bindings for activated sites. |
195
+ | `esa-cli config --local/--global` | Edit local project or global CLI configuration. |
196
+ | `esa-cli lang` | Switch CLI language. |
197
+
198
+ For the full option reference, see [ESA CLI Commands](https://github.com/aliyun/alibabacloud-esa-cli/blob/master/docs/Commands_en.md).
199
+
200
+ ## Development
201
+
202
+ Clone the repository and install dependencies:
203
+
204
+ ```bash
205
+ git clone git@github.com:aliyun/alibabacloud-esa-cli.git
206
+ cd alibabacloud-esa-cli
207
+ npm install
208
+ ```
209
+
210
+ Build the CLI:
211
+
212
+ ```bash
213
+ npm run build
214
+ ```
215
+
216
+ Run tests:
217
+
218
+ ```bash
219
+ npm test
220
+ ```
221
+
222
+ Useful scripts:
223
+
224
+ | Script | Description |
225
+ | --- | --- |
226
+ | `npm run dev` | Run TypeScript in watch mode. |
227
+ | `npm run build` | Generate locale files, compile TypeScript, and copy runtime assets into `dist`. |
228
+ | `npm test` | Run Vitest with coverage. |
229
+ | `npm run lint` | Run ESLint on `src`. |
230
+ | `npm run lint:fix` | Run ESLint with automatic fixes. |
231
+
232
+ ## Troubleshooting
233
+
234
+ - Run commands with `--debug` to print more detailed logs where supported.
235
+ - Use `--skip-update-check` or `ESA_NO_UPDATE_CHECK=1` to skip the CLI update check.
236
+ - Set `ALIBABA_CLOUD_ESA_CLI_COMPAT_MODE` to override the CLI name shown in help, usage, and error messages when invoking ESA CLI through a compatibility wrapper.
237
+ - If packaging fails, confirm that `entry` points to an existing file and `assets.directory` points to an existing directory.
238
+ - If route or domain binding fails, confirm the target site is already activated under the current account.
239
+
240
+ ## Resources
58
241
 
59
- - [esa-cli command](./docs/Commands_en.md)
60
- - [ESA Configuration Guide](./docs/Config_en.md)
242
+ - [ESA CLI Commands](https://github.com/aliyun/alibabacloud-esa-cli/blob/master/docs/Commands_en.md)
243
+ - [ESA Configuration Guide](https://github.com/aliyun/alibabacloud-esa-cli/blob/master/docs/Config_en.md)
61
244
  - [Alibaba Cloud ESA Documentation](https://www.alibabacloud.com/help/en/edge-security-acceleration/esa/user-guide/what-is-er/)
62
245
  - [Functions and Pages API Reference](https://www.alibabacloud.com/help/en/edge-security-acceleration/esa/user-guide/api-documentation/)
246
+ - [中文文档](./zh_CN.md)
package/bin/enter.cjs CHANGED
@@ -3,6 +3,38 @@ const fs = require('fs');
3
3
  const spawn = require('cross-spawn');
4
4
  const path = require('path');
5
5
 
6
+ const getRuntimeNodePath = () => {
7
+ const nodePaths = new Set();
8
+ const launcherDirs = [
9
+ __dirname,
10
+ process.argv[1] ? path.dirname(process.argv[1]) : undefined
11
+ ].filter(Boolean);
12
+
13
+ for (const launcherDir of launcherDirs) {
14
+ const pnpmNodeModules = path.resolve(
15
+ launcherDir,
16
+ '..',
17
+ '..',
18
+ '.pnpm',
19
+ 'node_modules'
20
+ );
21
+
22
+ if (fs.existsSync(pnpmNodeModules)) {
23
+ nodePaths.add(pnpmNodeModules);
24
+ }
25
+ }
26
+
27
+ if (process.env.NODE_PATH) {
28
+ for (const nodePath of process.env.NODE_PATH.split(path.delimiter)) {
29
+ if (nodePath) {
30
+ nodePaths.add(nodePath);
31
+ }
32
+ }
33
+ }
34
+
35
+ return nodePaths.size > 0 ? Array.from(nodePaths).join(path.delimiter) : '';
36
+ };
37
+
6
38
  const main = () => {
7
39
  let entryPath;
8
40
  if (fs.existsSync(path.join(__dirname, '../dist/index.js'))) {
@@ -13,29 +45,30 @@ const main = () => {
13
45
  throw new Error('Neither dist/index.js nor index.js could be found.');
14
46
  }
15
47
 
48
+ const nodePath = getRuntimeNodePath();
49
+
16
50
  return spawn(
17
51
  process.execPath,
18
52
  ['--no-warnings', ...process.execArgv, entryPath, ...process.argv.slice(2)],
19
53
  {
20
- stdio: [0, 1, 2, 'ipc'],
54
+ stdio: 'inherit',
21
55
  env: {
22
- ...process.env
56
+ ...process.env,
57
+ ...(nodePath ? { NODE_PATH: nodePath } : {})
23
58
  }
24
59
  }
25
60
  )
26
61
  .on('error', (err) => {
27
62
  console.log('Get Error', err);
28
63
  })
29
- .on('message', (msg) => {
30
- // console.log('Get Message', msg);
31
- process.send && process.send(msg);
32
- })
33
- .on('disconnect', () => {
34
- // console.log('Get disconnect');
35
- process.disconnect && process.disconnect();
36
- })
37
- .on('exit', (code) => {
38
- process.exit && process.exit(code);
64
+ .on('exit', (code, signal) => {
65
+ if (code !== null) {
66
+ process.exit(code);
67
+ } else if (signal) {
68
+ process.kill(process.pid, signal);
69
+ } else {
70
+ process.exit(1);
71
+ }
39
72
  });
40
73
  };
41
74
 
@@ -3,6 +3,38 @@ const fs = require('fs');
3
3
  const spawn = require('cross-spawn');
4
4
  const path = require('path');
5
5
 
6
+ const getRuntimeNodePath = () => {
7
+ const nodePaths = new Set();
8
+ const launcherDirs = [
9
+ __dirname,
10
+ process.argv[1] ? path.dirname(process.argv[1]) : undefined
11
+ ].filter(Boolean);
12
+
13
+ for (const launcherDir of launcherDirs) {
14
+ const pnpmNodeModules = path.resolve(
15
+ launcherDir,
16
+ '..',
17
+ '..',
18
+ '.pnpm',
19
+ 'node_modules'
20
+ );
21
+
22
+ if (fs.existsSync(pnpmNodeModules)) {
23
+ nodePaths.add(pnpmNodeModules);
24
+ }
25
+ }
26
+
27
+ if (process.env.NODE_PATH) {
28
+ for (const nodePath of process.env.NODE_PATH.split(path.delimiter)) {
29
+ if (nodePath) {
30
+ nodePaths.add(nodePath);
31
+ }
32
+ }
33
+ }
34
+
35
+ return nodePaths.size > 0 ? Array.from(nodePaths).join(path.delimiter) : '';
36
+ };
37
+
6
38
  const main = () => {
7
39
  let entryPath;
8
40
  if (fs.existsSync(path.join(__dirname, '../dist/index.js'))) {
@@ -13,29 +45,30 @@ const main = () => {
13
45
  throw new Error('Neither dist/index.js nor index.js could be found.');
14
46
  }
15
47
 
48
+ const nodePath = getRuntimeNodePath();
49
+
16
50
  return spawn(
17
51
  process.execPath,
18
52
  ['--no-warnings', ...process.execArgv, entryPath, ...process.argv.slice(2)],
19
53
  {
20
- stdio: [0, 1, 2, 'ipc'],
54
+ stdio: 'inherit',
21
55
  env: {
22
- ...process.env
56
+ ...process.env,
57
+ ...(nodePath ? { NODE_PATH: nodePath } : {})
23
58
  }
24
59
  }
25
60
  )
26
61
  .on('error', (err) => {
27
62
  console.log('Get Error', err);
28
63
  })
29
- .on('message', (msg) => {
30
- // console.log('Get Message', msg);
31
- process.send && process.send(msg);
32
- })
33
- .on('disconnect', () => {
34
- // console.log('Get disconnect');
35
- process.disconnect && process.disconnect();
36
- })
37
- .on('exit', (code) => {
38
- process.exit && process.exit(code);
64
+ .on('exit', (code, signal) => {
65
+ if (code !== null) {
66
+ process.exit(code);
67
+ } else if (signal) {
68
+ process.kill(process.pid, signal);
69
+ } else {
70
+ process.exit(1);
71
+ }
39
72
  });
40
73
  };
41
74
 
@@ -47,8 +47,8 @@ const commit = {
47
47
  });
48
48
  },
49
49
  handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
50
- yield handleCommit(argv);
51
- exit();
50
+ const success = yield handleCommit(argv);
51
+ exit(success ? 0 : 1);
52
52
  })
53
53
  };
54
54
  export default commit;
@@ -58,7 +58,7 @@ export function handleCommit(argv) {
58
58
  intro(`Commit an application with ESA`);
59
59
  const projectInfo = yield validateAndInitializeProject(argv === null || argv === void 0 ? void 0 : argv.name);
60
60
  if (!projectInfo)
61
- return;
61
+ return false;
62
62
  const { projectName } = projectInfo;
63
63
  let description;
64
64
  if (argv.description) {
@@ -77,7 +77,7 @@ export function handleCommit(argv) {
77
77
  const { isSuccess } = res || {};
78
78
  if (!isSuccess) {
79
79
  logger.endSubStep('Generate version failed');
80
- exit(1);
80
+ return false;
81
81
  }
82
82
  const codeVersion = (_b = (_a = res === null || res === void 0 ? void 0 : res.res) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.CodeVersion;
83
83
  if (!codeVersion) {
@@ -86,5 +86,6 @@ export function handleCommit(argv) {
86
86
  }
87
87
  logger.endSubStep(`Version generated: ${codeVersion}`);
88
88
  outro(`Code version ${chalk.bold(codeVersion)} generated successfully`);
89
+ return true;
89
90
  });
90
91
  }
@@ -31,7 +31,7 @@ const deploymentsDelete = {
31
31
  });
32
32
  },
33
33
  handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
34
- handleDeleteDeployments(argv);
34
+ yield handleDeleteDeployments(argv);
35
35
  })
36
36
  };
37
37
  export default deploymentsDelete;
@@ -19,7 +19,7 @@ const deploymentsList = {
19
19
  command: 'list',
20
20
  describe: `🔍 ${t('deployments_list_describe').d('List all deployments')}`,
21
21
  handler: () => __awaiter(void 0, void 0, void 0, function* () {
22
- handleListDeployments();
22
+ yield handleListDeployments();
23
23
  })
24
24
  };
25
25
  export default deploymentsList;
@@ -7,7 +7,10 @@ import esbuild from 'esbuild';
7
7
  import { lessLoader } from 'esbuild-plugin-less';
8
8
  import { getRoot } from '../../utils/fileUtils/base.js';
9
9
  import { NODE_EXTERNALS } from '../common/constant.js';
10
- const replacements = { cache: 'mockCache', EdgeKV: 'mockKV' };
10
+ const replacements = {
11
+ cache: 'mockCache',
12
+ EdgeKV: 'mockKV'
13
+ };
11
14
  const traverse = _traverse.default;
12
15
  const generator = _generator.default;
13
16
  const renameMock = {
@@ -8,26 +8,102 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import chalk from 'chalk';
11
- import { Box, Text, useApp, useInput, render } from 'ink';
12
- import React, { useEffect } from 'react';
13
11
  import t from '../../i18n/index.js';
14
12
  import logger from '../../libs/logger.js';
15
13
  import { checkOS, Platforms } from '../../utils/checkOS.js';
16
14
  import { getDevOpenBrowserUrl } from '../../utils/fileUtils/index.js';
17
15
  import openInBrowser from '../../utils/openInBrowser.js';
18
- const InteractionBox = ({ worker }) => {
19
- const { exit } = useApp();
16
+ const getShortcutSegments = (useEw2) => [
17
+ ['[b]', ' open a browser, '],
18
+ ...(!useEw2
19
+ ? [['[d]', ' open Devtools, ']]
20
+ : []),
21
+ ['[c]', ' clear console, '],
22
+ ['[x]', ' to exit']
23
+ ];
24
+ export const formatShortcutPanel = (useEw2, columns = process.stdout.columns) => {
25
+ const segments = getShortcutSegments(useEw2);
26
+ const fullPlain = segments
27
+ .map(([key, description]) => key + description)
28
+ .join('');
29
+ const shouldStack = Boolean(columns && fullPlain.length + 4 > columns);
30
+ const rows = shouldStack
31
+ ? segments.map(([key, description]) => ({
32
+ plain: key + description,
33
+ styled: chalk.bold(key) + description
34
+ }))
35
+ : [
36
+ {
37
+ plain: fullPlain,
38
+ styled: segments
39
+ .map(([key, description]) => chalk.bold(key) + description)
40
+ .join('')
41
+ }
42
+ ];
43
+ const innerWidth = Math.max(...rows.map(({ plain }) => plain.length));
44
+ const border = `+${'-'.repeat(innerWidth + 2)}+`;
45
+ return [
46
+ border,
47
+ ...rows.map(({ plain, styled }) => `| ${styled}${' '.repeat(innerWidth - plain.length)} |`),
48
+ border
49
+ ];
50
+ };
51
+ const printShortcutPanel = (useEw2) => {
52
+ formatShortcutPanel(useEw2).forEach((line) => logger.log(line));
53
+ };
54
+ /**
55
+ * Keyboard shortcut panel for `dev` mode, implemented with raw stdin
56
+ * (previously an ink component). Returns the same shape as before:
57
+ * `{ devElement: { waitUntilExit }, exit }`.
58
+ */
59
+ const doProcess = (worker) => {
20
60
  const inspectLink = chalk.underline.blue('chrome://inspect/#devices');
21
61
  const remoteTarget = chalk.blue('Remote Target');
22
62
  const inspect = chalk.blue('inspect');
23
63
  const OS = checkOS();
24
- const useEw2 = OS === Platforms.AppleArm || Platforms.AppleIntel || Platforms.LinuxX86;
25
- /* eslint-disable no-unused-vars */
26
- useInput((input) => __awaiter(void 0, void 0, void 0, function* () {
64
+ const useEw2 = [
65
+ Platforms.AppleArm,
66
+ Platforms.AppleIntel,
67
+ Platforms.LinuxX86
68
+ ].includes(OS);
69
+ printShortcutPanel(useEw2);
70
+ let resolveExit;
71
+ const exitPromise = new Promise((resolve) => {
72
+ resolveExit = resolve;
73
+ });
74
+ let exited = false;
75
+ const cleanup = () => {
76
+ process.stdin.off('data', onData);
77
+ if (process.stdin.isTTY) {
78
+ process.stdin.setRawMode(false);
79
+ }
80
+ process.stdin.pause();
81
+ if (worker) {
82
+ worker.stop();
83
+ }
84
+ };
85
+ const exit = () => {
86
+ if (exited)
87
+ return;
88
+ exited = true;
89
+ cleanup();
90
+ resolveExit();
91
+ setTimeout(() => {
92
+ process.exit(0);
93
+ }, 500);
94
+ };
95
+ const onData = (data) => __awaiter(void 0, void 0, void 0, function* () {
96
+ const input = data.toString();
97
+ // Ctrl+C
98
+ if (input === '\u0003') {
99
+ exit();
100
+ return;
101
+ }
27
102
  switch (input.toLowerCase()) {
28
103
  case 'c':
29
104
  console.clear();
30
105
  logger.block();
106
+ printShortcutPanel(useEw2);
31
107
  break;
32
108
  case 'b': {
33
109
  yield openInBrowser(getDevOpenBrowserUrl());
@@ -46,36 +122,18 @@ const InteractionBox = ({ worker }) => {
46
122
  default:
47
123
  break;
48
124
  }
49
- }));
50
- useEffect(() => {
51
- return () => {
52
- if (worker) {
53
- worker.stop();
54
- }
55
- };
56
- }, []);
57
- return (React.createElement(React.Fragment, null,
58
- React.createElement(Box, { borderStyle: "classic", paddingLeft: 1, paddingRight: 1 },
59
- React.createElement(Text, { bold: true }, "[b]"),
60
- React.createElement(Text, null, " open a browser, "),
61
- !useEw2 ? (React.createElement(React.Fragment, null,
62
- React.createElement(Text, { bold: true }, "[d]"),
63
- React.createElement(Text, null, " open Devtools, "))) : null,
64
- React.createElement(Text, { bold: true }, "[c]"),
65
- React.createElement(Text, null, " clear console, "),
66
- React.createElement(Text, { bold: true }, "[x]"),
67
- React.createElement(Text, null, " to exit"))));
68
- };
69
- const doProcess = (worker) => {
70
- const devElement = render(React.createElement(InteractionBox, { worker: worker }));
125
+ });
126
+ if (process.stdin.isTTY) {
127
+ process.stdin.setRawMode(true);
128
+ }
129
+ process.stdin.setEncoding('utf8');
130
+ process.stdin.resume();
131
+ process.stdin.on('data', onData);
71
132
  return {
72
- devElement,
73
- exit: () => {
74
- devElement.unmount();
75
- setTimeout(() => {
76
- process.exit(0);
77
- }, 500);
78
- }
133
+ devElement: {
134
+ waitUntilExit: () => exitPromise
135
+ },
136
+ exit
79
137
  };
80
138
  };
81
139
  export default doProcess;
@@ -41,7 +41,7 @@ const addDomain = {
41
41
  });
42
42
  },
43
43
  handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
44
- handleAddDomain(argv);
44
+ yield handleAddDomain(argv);
45
45
  })
46
46
  };
47
47
  export default addDomain;
@@ -17,15 +17,14 @@ const deleteDomain = {
17
17
  command: 'delete <domain>',
18
18
  describe: `🗑 ${t('domain_delete_describe').d('Delete a related domain')}`,
19
19
  builder: (yargs) => {
20
- return yargs.positional('domains', {
20
+ return yargs.positional('domain', {
21
21
  describe: t('domain_delete_positional_describe').d('The names of the related domains to delete'),
22
22
  type: 'string',
23
- array: true,
24
23
  demandOption: true
25
24
  });
26
25
  },
27
26
  handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
28
- handleDeleteDomain(argv);
27
+ yield handleDeleteDomain(argv);
29
28
  })
30
29
  };
31
30
  export default deleteDomain;