esa-cli 1.0.9 → 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 (42) 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 +108 -5
  15. package/dist/commands/logout.js +11 -4
  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 +21 -19
  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 +25 -1
  29. package/dist/index.js +8 -3
  30. package/dist/libs/api.js +4 -15
  31. package/dist/libs/apiService.js +8 -14
  32. package/dist/libs/logger.js +7 -2
  33. package/dist/utils/fileMd5.js +13 -3
  34. package/dist/utils/fileUtils/base.js +14 -18
  35. package/dist/utils/fileUtils/index.js +25 -3
  36. package/dist/utils/validateCredentials.js +12 -16
  37. package/package.json +18 -20
  38. package/zh_CN.md +2 -2
  39. package/dist/components/descriptionInput.js +0 -38
  40. package/dist/components/filterSelector.js +0 -132
  41. package/dist/components/selectItem.js +0 -6
  42. package/dist/components/yesNoPrompt.js +0 -9
@@ -17,7 +17,7 @@ const listDomain = {
17
17
  command: 'list',
18
18
  describe: `🔍 ${t('domain_list_describe').d('List all related domains')}`,
19
19
  handler: () => __awaiter(void 0, void 0, void 0, function* () {
20
- handleListDomains();
20
+ yield handleListDomains();
21
21
  })
22
22
  };
23
23
  export default listDomain;
@@ -257,7 +257,7 @@ export const configProjectName = (initParams) => __awaiter(void 0, void 0, void
257
257
  return true;
258
258
  }
259
259
  }));
260
- initParams.name = name;
260
+ initParams.name = name || defaultName;
261
261
  });
262
262
  export const configCategory = (initParams) => __awaiter(void 0, void 0, void 0, function* () {
263
263
  if (initParams.category || initParams.framework || initParams.template) {
@@ -429,7 +429,7 @@ export const applyFileEdits = (initParams) => __awaiter(void 0, void 0, void 0,
429
429
  // Very small glob subset: *, ?, {a,b,c}
430
430
  let escaped = pattern
431
431
  .replace(/[-/\\^$+?.()|[\]{}]/g, '\\$&') // escape regex specials first
432
- .replace(/\\\*/g, '.*')
432
+ .replace(/\*/g, '.*')
433
433
  .replace(/\\\?/g, '.');
434
434
  // restore and convert {a,b} to (a|b)
435
435
  escaped = escaped.replace(/\\\{([^}]+)\\\}/g, (_, inner) => {
@@ -455,7 +455,10 @@ export const applyFileEdits = (initParams) => __awaiter(void 0, void 0, void 0,
455
455
  let matchedFiles = [];
456
456
  if (edit.matchType === 'exact') {
457
457
  const absExact = path.join(targetPath, edit.match);
458
- matchedFiles = fs.existsSync(absExact) ? [edit.match] : [];
458
+ matchedFiles =
459
+ fs.existsSync(absExact) || edit.createIfMissing !== false
460
+ ? [edit.match]
461
+ : [];
459
462
  }
460
463
  else if (edit.matchType === 'glob') {
461
464
  const regex = toRegexFromGlob(edit.match);
@@ -482,8 +485,8 @@ export const applyFileEdits = (initParams) => __awaiter(void 0, void 0, void 0,
482
485
  const abs = path.join(targetPath, rel);
483
486
  if (payload == null)
484
487
  continue;
485
- if (!fs.existsSync(abs))
486
- continue; // Only overwrite existing files
488
+ if (!fs.existsSync(abs) && edit.createIfMissing === false)
489
+ continue;
487
490
  fs.ensureDirSync(path.dirname(abs));
488
491
  fs.writeFileSync(abs, payload, 'utf-8');
489
492
  }
@@ -497,7 +500,7 @@ export const applyFileEdits = (initParams) => __awaiter(void 0, void 0, void 0,
497
500
  }
498
501
  });
499
502
  export const installESACli = (initParams) => __awaiter(void 0, void 0, void 0, function* () {
500
- if (!initParams.installEsaCli) {
503
+ if (typeof initParams.installEsaCli !== 'boolean') {
501
504
  const install = (yield promptParameter({
502
505
  type: 'confirm',
503
506
  question: 'Do you want to install esa-cli as a dev dependency?',
@@ -567,7 +570,7 @@ export const initGit = (initParams) => __awaiter(void 0, void 0, void 0, functio
567
570
  log.step('You have not installed Git, Git skipped');
568
571
  return true;
569
572
  }
570
- if (!initParams.git) {
573
+ if (typeof initParams.git !== 'boolean') {
571
574
  const initGit = (yield promptParameter({
572
575
  type: 'confirm',
573
576
  question: t('init_git').d('Do you want to init git in your project?'),
@@ -612,7 +615,8 @@ export function getGitVersion() {
612
615
  }
613
616
  export function isGitInstalled() {
614
617
  return __awaiter(this, void 0, void 0, function* () {
615
- return (yield getGitVersion()) !== '' && (yield getGitVersion()) !== null;
618
+ const gitVersion = yield getGitVersion();
619
+ return gitVersion !== '' && gitVersion !== null;
616
620
  });
617
621
  }
618
622
  /**
@@ -57,8 +57,7 @@ const init = {
57
57
  })
58
58
  .option('install-esa-cli', {
59
59
  describe: 'Install esa-cli as a dev dependency',
60
- type: 'boolean',
61
- default: false
60
+ type: 'boolean'
62
61
  });
63
62
  },
64
63
  handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
@@ -67,7 +66,7 @@ const init = {
67
66
  })
68
67
  };
69
68
  export default init;
70
- const handleInit = (argv) => __awaiter(void 0, void 0, void 0, function* () {
69
+ export const handleInit = (argv) => __awaiter(void 0, void 0, void 0, function* () {
71
70
  yield checkAndUpdatePackage('esa-template');
72
71
  const initParams = getInitParamsFromArgv(argv);
73
72
  yield create(initParams);
@@ -96,7 +95,7 @@ const config = (initParams) => __awaiter(void 0, void 0, void 0, function* () {
96
95
  });
97
96
  const deploy = (initParams) => __awaiter(void 0, void 0, void 0, function* () {
98
97
  intro(`Deploy an application with ESA ${chalk.gray('Step 3 of 3')}`);
99
- if (!initParams.deploy) {
98
+ if (typeof initParams.deploy !== 'boolean') {
100
99
  const deploy = (yield promptParameter({
101
100
  type: 'confirm',
102
101
  question: t('auto_deploy').d('Do you want to deploy your project?'),
@@ -13,11 +13,42 @@ import t from '../../i18n/index.js';
13
13
  import logger from '../../libs/logger.js';
14
14
  import { getCliConfig, updateCliConfigFile, generateDefaultConfig } from '../../utils/fileUtils/index.js';
15
15
  import { validateCredentials } from '../../utils/validateCredentials.js';
16
+ /** Parse STS token string: "AccessKeyId,AccessKeySecret,SecurityToken" or JSON */
17
+ function parseStsToken(raw) {
18
+ var _a, _b, _c;
19
+ const s = raw.trim();
20
+ if (!s)
21
+ return null;
22
+ if (s.startsWith('{')) {
23
+ try {
24
+ const o = JSON.parse(s);
25
+ const accessKeyId = (_a = o.AccessKeyId) !== null && _a !== void 0 ? _a : o.accessKeyId;
26
+ const accessKeySecret = (_b = o.AccessKeySecret) !== null && _b !== void 0 ? _b : o.accessKeySecret;
27
+ const securityToken = (_c = o.SecurityToken) !== null && _c !== void 0 ? _c : o.securityToken;
28
+ if (accessKeyId && accessKeySecret && securityToken) {
29
+ return { accessKeyId, accessKeySecret, securityToken };
30
+ }
31
+ }
32
+ catch (_d) {
33
+ return null;
34
+ }
35
+ return null;
36
+ }
37
+ const parts = s.split(',').map((p) => p.trim());
38
+ if (parts.length >= 3) {
39
+ return {
40
+ accessKeyId: parts[0],
41
+ accessKeySecret: parts[1],
42
+ securityToken: parts.slice(2).join(',').trim()
43
+ };
44
+ }
45
+ return null;
46
+ }
16
47
  const login = {
17
48
  command: 'login',
18
49
  describe: `🔑 ${t('login_describe').d('Login to the server')}`,
19
50
  builder: (yargs) => {
20
- var _a, _b;
51
+ var _a, _b, _c;
21
52
  return yargs
22
53
  .option('access-key-id', {
23
54
  alias: 'ak',
@@ -28,18 +59,28 @@ const login = {
28
59
  alias: 'sk',
29
60
  describe: (_b = t('login_option_access_key_secret')) === null || _b === void 0 ? void 0 : _b.d('AccessKey Secret'),
30
61
  type: 'string'
62
+ })
63
+ .option('sts-token', {
64
+ describe: (_c = t('login_option_sts_token')) === null || _c === void 0 ? void 0 : _c.d('STS token: AccessKeyId,AccessKeySecret,SecurityToken (comma-separated, one-shot)'),
65
+ type: 'string'
31
66
  });
32
67
  },
33
68
  handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
34
- handleLogin(argv);
69
+ yield handleLogin(argv);
35
70
  })
36
71
  };
37
72
  export default login;
38
73
  export function handleLogin(argv) {
39
74
  return __awaiter(this, void 0, void 0, function* () {
40
75
  generateDefaultConfig();
41
- if (process.env.ESA_ACCESS_KEY_ID && process.env.ESA_ACCESS_KEY_SECRET) {
42
- const result = yield validateCredentials(process.env.ESA_ACCESS_KEY_ID, process.env.ESA_ACCESS_KEY_SECRET);
76
+ const envAccessKeyId = process.env.ALIBABA_CLOUD_ACCESS_KEY_ID ||
77
+ process.env.ESA_ACCESS_KEY_ID;
78
+ const envAccessKeySecret = process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET ||
79
+ process.env.ESA_ACCESS_KEY_SECRET;
80
+ const envSecurityToken = process.env.ALIBABA_CLOUD_SECURITY_TOKEN ||
81
+ process.env.ESA_SECURITY_TOKEN;
82
+ if (envAccessKeyId && envAccessKeySecret) {
83
+ const result = yield validateCredentials(envAccessKeyId, envAccessKeySecret, envSecurityToken);
43
84
  if (result.valid) {
44
85
  logger.log(t('login_get_credentials_from_environment_variables').d('Get credentials from environment variables'));
45
86
  logger.success(t('login_success').d('Login success!'));
@@ -49,6 +90,27 @@ export function handleLogin(argv) {
49
90
  }
50
91
  return;
51
92
  }
93
+ const stsTokenRaw = argv === null || argv === void 0 ? void 0 : argv['sts-token'];
94
+ if (stsTokenRaw) {
95
+ const parsed = parseStsToken(stsTokenRaw);
96
+ if (!parsed) {
97
+ logger.error(t('login_sts_token_format_invalid').d('Invalid STS token format. Use: AccessKeyId,AccessKeySecret,SecurityToken'));
98
+ return;
99
+ }
100
+ const result = yield validateCredentials(parsed.accessKeyId, parsed.accessKeySecret, parsed.securityToken);
101
+ if (result.valid) {
102
+ logger.success(t('login_success').d('Login success!'));
103
+ updateCliConfigFile(Object.assign({ auth: {
104
+ accessKeyId: parsed.accessKeyId,
105
+ accessKeySecret: parsed.accessKeySecret,
106
+ securityToken: parsed.securityToken
107
+ } }, (result.endpoint ? { endpoint: result.endpoint } : {})));
108
+ }
109
+ else {
110
+ logger.error(result.message || 'Login failed');
111
+ }
112
+ return;
113
+ }
52
114
  const accessKeyId = argv === null || argv === void 0 ? void 0 : argv['access-key-id'];
53
115
  const accessKeySecret = argv === null || argv === void 0 ? void 0 : argv['access-key-secret'];
54
116
  if (accessKeyId && accessKeySecret) {
@@ -73,7 +135,7 @@ export function handleLogin(argv) {
73
135
  cliConfig.auth &&
74
136
  cliConfig.auth.accessKeyId &&
75
137
  cliConfig.auth.accessKeySecret) {
76
- const loginStatus = yield validateCredentials(cliConfig.auth.accessKeyId, cliConfig.auth.accessKeySecret);
138
+ const loginStatus = yield validateCredentials(cliConfig.auth.accessKeyId, cliConfig.auth.accessKeySecret, cliConfig.auth.securityToken);
77
139
  if (loginStatus.valid) {
78
140
  logger.warn(t('login_already').d('You are already logged in.'));
79
141
  const selected = (yield clackSelect({
@@ -99,6 +161,47 @@ export function handleLogin(argv) {
99
161
  }
100
162
  export function interactiveLogin() {
101
163
  return __awaiter(this, void 0, void 0, function* () {
164
+ const loginMethod = (yield clackSelect({
165
+ message: t('login_method_select').d('Choose login method'),
166
+ options: [
167
+ {
168
+ label: t('login_method_aksk').d('AK/SK (AccessKey ID + AccessKey Secret)'),
169
+ value: 'aksk'
170
+ },
171
+ {
172
+ label: t('login_method_sts').d('STS Token (one-shot: AccessKeyId,AccessKeySecret,SecurityToken)'),
173
+ value: 'sts'
174
+ }
175
+ ]
176
+ }));
177
+ if (isCancel(loginMethod)) {
178
+ return;
179
+ }
180
+ if (loginMethod === 'sts') {
181
+ const stsInput = (yield clackText({
182
+ message: t('login_sts_token_prompt').d('Enter STS token (AccessKeyId,AccessKeySecret,SecurityToken):')
183
+ }));
184
+ if (isCancel(stsInput))
185
+ return;
186
+ const parsed = parseStsToken(stsInput);
187
+ if (!parsed) {
188
+ logger.error(t('login_sts_token_format_invalid').d('Invalid STS token format. Use: AccessKeyId,AccessKeySecret,SecurityToken'));
189
+ return;
190
+ }
191
+ const loginStatus = yield validateCredentials(parsed.accessKeyId, parsed.accessKeySecret, parsed.securityToken);
192
+ if (loginStatus.valid) {
193
+ yield updateCliConfigFile(Object.assign({ auth: {
194
+ accessKeyId: parsed.accessKeyId,
195
+ accessKeySecret: parsed.accessKeySecret,
196
+ securityToken: parsed.securityToken
197
+ } }, (loginStatus.endpoint ? { endpoint: loginStatus.endpoint } : {})));
198
+ logger.success(t('login_success').d('Login success!'));
199
+ }
200
+ else {
201
+ logger.error(loginStatus.message || 'Login failed');
202
+ }
203
+ return;
204
+ }
102
205
  const styledUrl = chalk.underline.blue('https://ram.console.aliyun.com/manage/ak');
103
206
  logger.log(`🔑 ${chalk.underline(t('login_get_ak_sk').d(`Please go to the following link to get your account's AccessKey ID and AccessKey Secret`))}`);
104
207
  logger.log(`👉 ${styledUrl}`);
@@ -16,9 +16,9 @@ const logout = {
16
16
  builder: (yargs) => {
17
17
  return yargs;
18
18
  },
19
- handler: () => {
20
- handleLogout();
21
- }
19
+ handler: () => __awaiter(void 0, void 0, void 0, function* () {
20
+ yield handleLogout();
21
+ })
22
22
  };
23
23
  export default logout;
24
24
  export function handleLogout() {
@@ -28,11 +28,18 @@ export function handleLogout() {
28
28
  return;
29
29
  }
30
30
  if (!cliConfig.auth) {
31
- cliConfig.auth = { accessKeyId: '', accessKeySecret: '' };
31
+ cliConfig.auth = {
32
+ accessKeyId: '',
33
+ accessKeySecret: '',
34
+ securityToken: ''
35
+ };
32
36
  }
33
37
  else {
34
38
  cliConfig.auth.accessKeyId = '';
35
39
  cliConfig.auth.accessKeySecret = '';
40
+ if ('securityToken' in cliConfig.auth) {
41
+ cliConfig.auth.securityToken = '';
42
+ }
36
43
  }
37
44
  yield updateCliConfigFile(cliConfig);
38
45
  logger.success(t('logout_success').d('Logout successfully'));
@@ -47,7 +47,7 @@ const addRoute = {
47
47
  });
48
48
  },
49
49
  handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
50
- handlerAddRoute(argv);
50
+ yield handlerAddRoute(argv);
51
51
  })
52
52
  };
53
53
  export function handlerAddRoute(argv) {
@@ -34,7 +34,7 @@ const deleteRoute = {
34
34
  });
35
35
  },
36
36
  handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
37
- handleDeleteRoute(argv);
37
+ yield handleDeleteRoute(argv);
38
38
  })
39
39
  };
40
40
  export default deleteRoute;
@@ -19,7 +19,7 @@ const listRoute = {
19
19
  command: 'list',
20
20
  describe: `🔍 ${t('route_list_describe').d('List all related routes')}`,
21
21
  handler: () => __awaiter(void 0, void 0, void 0, function* () {
22
- handleListRoutes();
22
+ yield handleListRoutes();
23
23
  })
24
24
  };
25
25
  export default listRoute;
@@ -20,13 +20,12 @@ const deleteCommand = {
20
20
  .positional('projectName', {
21
21
  describe: t('delete_routineName_positional_describe').d('The name of the project to delete'),
22
22
  type: 'string',
23
- array: true,
24
23
  demandOption: true
25
24
  })
26
25
  .usage(`${t('common_usage').d('Usage')}: $0 delete <projectName>`);
27
26
  },
28
27
  handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
29
- handleDelete(argv);
28
+ yield handleDelete(argv);
30
29
  })
31
30
  };
32
31
  export default deleteCommand;
@@ -27,7 +27,7 @@ const list = {
27
27
  .usage(`${t('common_usage').d('Usage')}: \$0 list [--keyword <keyword>]`);
28
28
  },
29
29
  handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
30
- handleList(argv);
30
+ yield handleList(argv);
31
31
  })
32
32
  };
33
33
  export default list;
@@ -19,7 +19,7 @@ const list = {
19
19
  return yargs.usage(`${t('common_usage').d('Usage')}: \$0 list []`);
20
20
  },
21
21
  handler: () => __awaiter(void 0, void 0, void 0, function* () {
22
- handleList();
22
+ yield handleList();
23
23
  })
24
24
  };
25
25
  export default list;
@@ -16,7 +16,7 @@ import { ApiService } from '../libs/apiService.js';
16
16
  import { isInstalledGit } from '../libs/git/index.js';
17
17
  import logger from '../libs/logger.js';
18
18
  import { getRoot } from '../utils/fileUtils/base.js';
19
- import { getCliConfig, projectConfigPath } from '../utils/fileUtils/index.js';
19
+ import { getApiConfig, getCliConfig, projectConfigPath } from '../utils/fileUtils/index.js';
20
20
  import { validateCredentials } from '../utils/validateCredentials.js';
21
21
  import { getRoutineDetails } from './common/utils.js';
22
22
  export const checkDirectory = (isCheckGit = false) => {
@@ -78,28 +78,30 @@ export function validDomain(domain) {
78
78
  }
79
79
  export function checkIsLoginSuccess() {
80
80
  return __awaiter(this, void 0, void 0, function* () {
81
- var _a, _b;
81
+ var _a, _b, _c;
82
82
  const cliConfig = getCliConfig();
83
- let accessKeyId = process.env.ESA_ACCESS_KEY_ID || ((_a = cliConfig === null || cliConfig === void 0 ? void 0 : cliConfig.auth) === null || _a === void 0 ? void 0 : _a.accessKeyId);
84
- let accessKeySecret = process.env.ESA_ACCESS_KEY_SECRET || ((_b = cliConfig === null || cliConfig === void 0 ? void 0 : cliConfig.auth) === null || _b === void 0 ? void 0 : _b.accessKeySecret);
83
+ let accessKeyId = process.env.ALIBABA_CLOUD_ACCESS_KEY_ID ||
84
+ process.env.ESA_ACCESS_KEY_ID ||
85
+ ((_a = cliConfig === null || cliConfig === void 0 ? void 0 : cliConfig.auth) === null || _a === void 0 ? void 0 : _a.accessKeyId);
86
+ let accessKeySecret = process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET ||
87
+ process.env.ESA_ACCESS_KEY_SECRET ||
88
+ ((_b = cliConfig === null || cliConfig === void 0 ? void 0 : cliConfig.auth) === null || _b === void 0 ? void 0 : _b.accessKeySecret);
89
+ const securityToken = process.env.ALIBABA_CLOUD_SECURITY_TOKEN ||
90
+ process.env.ESA_SECURITY_TOKEN ||
91
+ ((_c = cliConfig === null || cliConfig === void 0 ? void 0 : cliConfig.auth) === null || _c === void 0 ? void 0 : _c.securityToken);
85
92
  if (accessKeyId && accessKeySecret) {
86
- const result = yield validateCredentials(accessKeyId, accessKeySecret);
93
+ const result = yield validateCredentials(accessKeyId, accessKeySecret, securityToken);
87
94
  const server = yield ApiService.getInstance();
88
95
  if (result.valid) {
89
- server.updateConfig({
90
- auth: {
91
- accessKeyId,
92
- accessKeySecret
93
- },
94
- endpoint: result.endpoint
95
- });
96
- api.updateConfig({
97
- auth: {
98
- accessKeyId,
99
- accessKeySecret
100
- },
101
- endpoint: result.endpoint
102
- });
96
+ const auth = {
97
+ accessKeyId,
98
+ accessKeySecret
99
+ };
100
+ if (securityToken)
101
+ auth.securityToken = securityToken;
102
+ const fileConfig = getApiConfig();
103
+ server.updateConfig(Object.assign(Object.assign({}, fileConfig), { auth }));
104
+ api.updateConfig(Object.assign(Object.assign({}, fileConfig), { auth }));
103
105
  return true;
104
106
  }
105
107
  }
@@ -7,89 +7,22 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { Box, render, Text, useInput } from 'ink';
11
- import React, { useState } from 'react';
10
+ import { multiselect, isCancel, cancel } from '@clack/prompts';
12
11
  import t from '../i18n/index.js';
13
- export const MultiSelectTable = ({ items, itemsPerRow, onSubmit, boxWidth = 25 }) => {
14
- const [selectedIndexes, setSelectedIndexes] = useState(new Set());
15
- const [cursorRow, setCursorRow] = useState(0);
16
- const [cursorCol, setCursorCol] = useState(0);
17
- const rows = [];
18
- for (let i = 0; i < items.length; i += itemsPerRow) {
19
- rows.push(items.slice(i, i + itemsPerRow));
20
- }
21
- const totalRows = Math.ceil(items.length / itemsPerRow);
22
- const toggleSelect = (row, col) => {
23
- const key = `${row}:${col}`;
24
- setSelectedIndexes((prevSelectedIndexes) => {
25
- const newSelectedIndexes = new Set(prevSelectedIndexes);
26
- if (newSelectedIndexes.has(key)) {
27
- newSelectedIndexes.delete(key);
28
- }
29
- else {
30
- newSelectedIndexes.add(key);
31
- }
32
- return newSelectedIndexes;
33
- });
34
- };
35
- const handleSubmission = () => {
36
- const selectedItems = Array.from(selectedIndexes).map((key) => {
37
- const [row, col] = key.split(':').map(Number);
38
- return rows[row][col];
39
- });
40
- onSubmit(selectedItems);
41
- };
42
- useInput((input, key) => {
43
- if (key.leftArrow) {
44
- setCursorCol((prev) => Math.max(prev - 1, 0));
45
- }
46
- else if (key.rightArrow) {
47
- setCursorCol((prev) => Math.min(prev + 1, itemsPerRow - 1));
48
- }
49
- else if (key.upArrow) {
50
- setCursorRow((prev) => Math.max(prev - 1, 0));
51
- }
52
- else if (key.downArrow) {
53
- setCursorRow((prev) => Math.min(prev + 1, totalRows - 1));
54
- }
55
- else if (input === ' ') {
56
- toggleSelect(cursorRow, cursorCol);
57
- }
58
- else if (key.tab) {
59
- setCursorCol((prevCol) => {
60
- let newCol = prevCol + 1;
61
- let newRow = cursorRow;
62
- if (newCol >= itemsPerRow) {
63
- newCol = 0;
64
- newRow = cursorRow + 1;
65
- if (newRow >= totalRows) {
66
- newRow = 0;
67
- }
68
- setCursorRow(newRow);
69
- }
70
- return newCol;
71
- });
72
- }
73
- else if (key.return) {
74
- handleSubmission();
75
- }
76
- });
77
- return (React.createElement(Box, { flexDirection: "column" },
78
- rows.map((rowItems, row) => (React.createElement(Box, { key: row, flexDirection: "row" }, rowItems.map((item, col) => (React.createElement(Box, { key: `${row}:${col}`, width: boxWidth },
79
- React.createElement(Text, { color: cursorRow === row && cursorCol === col ? 'green' : undefined },
80
- selectedIndexes.has(`${row}:${col}`) ? '✅' : ' ',
81
- item.label))))))),
82
- React.createElement(Box, { flexDirection: "column" },
83
- React.createElement(Text, null,
84
- "\uD83D\uDD14",
85
- ' ',
86
- t('deploy_select_table_tip').d('Use arrow keys to move, space to select, and enter to submit.')))));
87
- };
88
- export const displayMultiSelectTable = (items_1, ...args_1) => __awaiter(void 0, [items_1, ...args_1], void 0, function* (items, itemsPerRow = 7, boxWidth = 25) {
89
- return new Promise((resolve) => {
90
- const { unmount } = render(React.createElement(MultiSelectTable, { items: items, itemsPerRow: itemsPerRow, onSubmit: (selectedItems) => {
91
- unmount();
92
- resolve(selectedItems.map((item) => item.label));
93
- }, boxWidth: boxWidth }));
12
+ /**
13
+ * Multi-select prompt based on @clack/prompts.
14
+ * Replaces the previous ink grid table; keeps the same signature so
15
+ * existing call sites stay unchanged (itemsPerRow/boxWidth are obsolete).
16
+ */
17
+ export const displayMultiSelectTable = (items_1, ...args_1) => __awaiter(void 0, [items_1, ...args_1], void 0, function* (items, _itemsPerRow = 7, _boxWidth = 25) {
18
+ const value = yield multiselect({
19
+ message: t('deploy_select_table_tip').d('Use arrow keys to move, space to select, and enter to submit.'),
20
+ options: items.map((item) => ({ label: item.label, value: item.label })),
21
+ required: false
94
22
  });
23
+ if (isCancel(value)) {
24
+ cancel('Operation cancelled.');
25
+ process.exit(130);
26
+ }
27
+ return value;
95
28
  });
@@ -7,62 +7,38 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { Box, render, Text } from 'ink';
11
- import TextInput from 'ink-text-input';
12
- import React, { useState } from 'react';
10
+ import { text, isCancel } from '@clack/prompts';
11
+ import chalk from 'chalk';
13
12
  import t from '../i18n/index.js';
14
- export const RouteBuilder = ({ siteName, onSubmit, onCancel }) => {
15
- const [prefix, setPrefix] = useState('');
16
- const [suffix, setSuffix] = useState('');
17
- const [currentInput, setCurrentInput] = useState('prefix');
18
- const [error, setError] = useState('');
19
- const handleSubmit = () => {
20
- if (currentInput === 'prefix') {
21
- setCurrentInput('suffix');
22
- return;
23
- }
24
- // Build complete route, add dot before prefix and slash before suffix if not empty
25
- const prefixWithDot = prefix ? `${prefix}.` : '';
26
- const suffixWithDot = suffix ? `/${suffix}` : '';
27
- const route = `${prefixWithDot}${siteName}${suffixWithDot}`;
28
- onSubmit(route);
29
- };
30
- const handleCancel = () => {
31
- onCancel();
32
- };
33
- const currentPrompt = currentInput === 'prefix'
34
- ? t('route_builder_prefix_prompt')
13
+ import logger from '../libs/logger.js';
14
+ /**
15
+ * Interactively build a route for the given site (prefix + suffix),
16
+ * based on @clack/prompts. Returns the built route, or null if cancelled.
17
+ */
18
+ export const routeBuilder = (siteName) => __awaiter(void 0, void 0, void 0, function* () {
19
+ logger.log(`Building route for site: ${chalk.cyan(siteName)}`);
20
+ const prefix = yield text({
21
+ message: t('route_builder_prefix_prompt')
35
22
  .d(`Enter route prefix for ${siteName} (e.g., abc, def):`)
36
- .replace('${siteName}', siteName)
37
- : t('route_builder_suffix_prompt')
38
- .d(`Enter route suffix for ${siteName} (e.g., *, users/*):`)
39
- .replace('${siteName}', siteName);
23
+ .replace('${siteName}', siteName),
24
+ defaultValue: ''
25
+ });
26
+ if (isCancel(prefix)) {
27
+ return null;
28
+ }
40
29
  const prefixWithDot = prefix ? `${prefix}.` : '';
41
- const suffixWithDot = suffix ? `/${suffix}` : '';
42
- const preview = `Preview: ${prefixWithDot}${siteName}${suffixWithDot}`;
43
- return (React.createElement(Box, { flexDirection: "column" },
44
- React.createElement(Box, null,
45
- React.createElement(Text, null, "Building route for site: "),
46
- React.createElement(Text, { color: "cyan" }, siteName)),
47
- React.createElement(Box, { marginTop: 1 },
48
- React.createElement(Text, null, currentPrompt)),
49
- React.createElement(Box, { marginTop: 1 },
50
- React.createElement(TextInput, { value: currentInput === 'prefix' ? prefix : suffix, onChange: currentInput === 'prefix' ? setPrefix : setSuffix, onSubmit: handleSubmit })),
51
- preview && (React.createElement(Box, { marginTop: 1 },
52
- React.createElement(Text, { color: "green" }, preview))),
53
- React.createElement(Box, { marginTop: 1 },
54
- React.createElement(Text, { color: "gray" }, t('route_builder_instructions').d('Press Enter to continue, Ctrl+C to cancel'))),
55
- error && (React.createElement(Box, { marginTop: 1 },
56
- React.createElement(Text, { color: "red" }, error)))));
57
- };
58
- export const routeBuilder = (siteName) => __awaiter(void 0, void 0, void 0, function* () {
59
- return new Promise((resolve) => {
60
- const { unmount } = render(React.createElement(RouteBuilder, { siteName: siteName, onSubmit: (route) => {
61
- unmount();
62
- resolve(route);
63
- }, onCancel: () => {
64
- unmount();
65
- resolve(null);
66
- } }));
30
+ const suffix = yield text({
31
+ message: t('route_builder_suffix_prompt')
32
+ .d(`Enter route suffix for ${siteName} (e.g., *, users/*):`)
33
+ .replace('${siteName}', siteName),
34
+ placeholder: `Preview: ${prefixWithDot}${siteName}`,
35
+ defaultValue: ''
67
36
  });
37
+ if (isCancel(suffix)) {
38
+ return null;
39
+ }
40
+ const suffixWithSlash = suffix ? `/${suffix}` : '';
41
+ const route = `${prefixWithDot}${siteName}${suffixWithSlash}`;
42
+ logger.log(chalk.green(`Preview: ${route}`));
43
+ return route;
68
44
  });