esa-cli 0.0.1-beta.1
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.
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/bin/enter.cjs +52 -0
- package/dist/README.md +175 -0
- package/dist/bin/enter.cjs +52 -0
- package/dist/cliconfig.toml +6 -0
- package/dist/commands/commit/index.js +147 -0
- package/dist/commands/commit/prodBuild.js +50 -0
- package/dist/commands/common/constant.js +54 -0
- package/dist/commands/config.js +51 -0
- package/dist/commands/deploy/helper.js +108 -0
- package/dist/commands/deploy/index.js +217 -0
- package/dist/commands/deployments/delete.js +92 -0
- package/dist/commands/deployments/index.js +27 -0
- package/dist/commands/deployments/list.js +89 -0
- package/dist/commands/dev/config/devBuild.js +26 -0
- package/dist/commands/dev/config/devEntry.js +71 -0
- package/dist/commands/dev/config/mock/cache.js +31 -0
- package/dist/commands/dev/config/mock/kv.js +87 -0
- package/dist/commands/dev/devPack.js +113 -0
- package/dist/commands/dev/doProcess.js +73 -0
- package/dist/commands/dev/index.js +240 -0
- package/dist/commands/dev/server.js +100 -0
- package/dist/commands/domain/add.js +72 -0
- package/dist/commands/domain/delete.js +74 -0
- package/dist/commands/domain/index.js +29 -0
- package/dist/commands/domain/list.js +51 -0
- package/dist/commands/init/index.js +149 -0
- package/dist/commands/lang.js +32 -0
- package/dist/commands/login/index.js +108 -0
- package/dist/commands/logout.js +44 -0
- package/dist/commands/route/add.js +115 -0
- package/dist/commands/route/delete.js +74 -0
- package/dist/commands/route/index.js +29 -0
- package/dist/commands/route/list.js +63 -0
- package/dist/commands/routine/delete.js +54 -0
- package/dist/commands/routine/index.js +27 -0
- package/dist/commands/routine/list.js +101 -0
- package/dist/commands/site/index.js +25 -0
- package/dist/commands/site/list.js +47 -0
- package/dist/commands/utils.js +139 -0
- package/dist/components/descriptionInput.js +38 -0
- package/dist/components/filterSelector.js +132 -0
- package/dist/components/mutiSelectTable.js +95 -0
- package/dist/components/selectInput.js +17 -0
- package/dist/components/selectItem.js +6 -0
- package/dist/components/yesNoPrompt.js +9 -0
- package/dist/docs/Commands_en.md +224 -0
- package/dist/docs/Commands_zh_CN.md +224 -0
- package/dist/docs/dev.png +0 -0
- package/dist/i18n/index.js +27 -0
- package/dist/i18n/locales.json +766 -0
- package/dist/index.js +80 -0
- package/dist/libs/apiService.js +914 -0
- package/dist/libs/git/index.js +52 -0
- package/dist/libs/interface.js +21 -0
- package/dist/libs/logger.js +149 -0
- package/dist/libs/request.js +98 -0
- package/dist/libs/templates/index.js +16 -0
- package/dist/package.json +93 -0
- package/dist/utils/checkDevPort.js +113 -0
- package/dist/utils/checkIsRoutineCreated.js +56 -0
- package/dist/utils/checkVersion.js +26 -0
- package/dist/utils/debounce.js +18 -0
- package/dist/utils/fileUtils/index.js +219 -0
- package/dist/utils/fileUtils/interface.js +1 -0
- package/dist/utils/install/install.ps1 +33 -0
- package/dist/utils/install/install.sh +53 -0
- package/dist/utils/installRuntime.js +80 -0
- package/dist/utils/openInBrowser.js +24 -0
- package/dist/utils/sleep.js +6 -0
- package/dist/zh_CN.md +177 -0
- package/package.json +93 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import { execSync } from 'child_process';
|
|
12
|
+
import { isInstalledGit } from '../libs/git/index.js';
|
|
13
|
+
import { getCliConfig, projectConfigPath, getRoot } from '../utils/fileUtils/index.js';
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
import t from '../i18n/index.js';
|
|
16
|
+
import { ApiService } from '../libs/apiService.js';
|
|
17
|
+
import logger from '../libs/logger.js';
|
|
18
|
+
export const checkDirectory = (isCheckGit = false) => {
|
|
19
|
+
const root = getRoot();
|
|
20
|
+
if (fs.existsSync(projectConfigPath)) {
|
|
21
|
+
try {
|
|
22
|
+
if (isCheckGit && isInstalledGit()) {
|
|
23
|
+
const gitStatus = execSync('git status --porcelain', {
|
|
24
|
+
cwd: root
|
|
25
|
+
}).toString();
|
|
26
|
+
if (gitStatus) {
|
|
27
|
+
logger.warn(t('utils_git_not_commit').d('There are uncommitted changes in the repository.'));
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
logger.error(`${t('utils_git_error').d('An error occurred while checking the Git status')}: ${error}`);
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
logger.notInProject();
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
export const bindRoutineWithDomain = (name, domain) => __awaiter(void 0, void 0, void 0, function* () {
|
|
44
|
+
const server = yield ApiService.getInstance();
|
|
45
|
+
const req = { RecordName: domain };
|
|
46
|
+
const res = yield server.getMatchSite(req);
|
|
47
|
+
if (res) {
|
|
48
|
+
const record = res;
|
|
49
|
+
const createReq = {
|
|
50
|
+
Name: name,
|
|
51
|
+
SiteId: record.data.SiteId,
|
|
52
|
+
SiteName: record.data.SiteName,
|
|
53
|
+
RecordName: domain
|
|
54
|
+
};
|
|
55
|
+
const response = yield server.createRoutineRelatedRecord(createReq);
|
|
56
|
+
const isBindSuccess = (response === null || response === void 0 ? void 0 : response.data.Status) === 'OK';
|
|
57
|
+
if (isBindSuccess) {
|
|
58
|
+
logger.success(t('utils_bind_success', { domain }).d(`Binding domain ${domain} to routine successfully`));
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
logger.error(t('utils_bind_error', { domain }).d(`Binding domain ${domain} to routine failed`));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
logger.error(t('utils_domain_error').d('Domain is not active'));
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
export const getRoutineVersionList = (name) => __awaiter(void 0, void 0, void 0, function* () {
|
|
69
|
+
var _a;
|
|
70
|
+
const server = yield ApiService.getInstance();
|
|
71
|
+
const req = { Name: name };
|
|
72
|
+
const res = yield server.getRoutine(req);
|
|
73
|
+
return ((_a = res === null || res === void 0 ? void 0 : res.data) === null || _a === void 0 ? void 0 : _a.CodeVersions) || [];
|
|
74
|
+
});
|
|
75
|
+
export function validName(name) {
|
|
76
|
+
return /^[a-zA-Z0-9-_]+$/.test(name);
|
|
77
|
+
}
|
|
78
|
+
// 校验域名是否有效
|
|
79
|
+
export function validDomain(domain) {
|
|
80
|
+
return /^(?:[a-z0-9-](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/.test(domain);
|
|
81
|
+
}
|
|
82
|
+
export function checkIsLoginSuccess() {
|
|
83
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
84
|
+
const cliConfig = getCliConfig();
|
|
85
|
+
const namedCommand = chalk.green('esa login');
|
|
86
|
+
if (!cliConfig || !cliConfig.auth) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (!cliConfig.auth.accessKeyId || !cliConfig.auth.accessKeySecret) {
|
|
90
|
+
logger.log(`❌ ${t('utils_login_error').d('Maybe you are not logged in yet.')}`);
|
|
91
|
+
logger.log(`🔔 ${t('utils_login_error_config', { namedCommand }).d(`Please run command to login: ${namedCommand}`)}`);
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const server = yield ApiService.getInstance();
|
|
95
|
+
server.updateConfig(cliConfig);
|
|
96
|
+
const res = yield server.checkLogin();
|
|
97
|
+
if (res.success) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
logger.log(res.message || '');
|
|
101
|
+
logger.log(`❌ ${t('utils_login_error').d('Maybe you are not logged in yet.')}`);
|
|
102
|
+
logger.log(`🔔 ${t('utils_login_error_config', { namedCommand }).d(`Please run command to login: ${namedCommand}`)}`);
|
|
103
|
+
return false;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
export function isValidRouteForDomain(route, domain) {
|
|
107
|
+
// 构建一个允许子域和任意路径的正则表达式
|
|
108
|
+
// 例如,匹配形式如 *.example.com/* 的URL
|
|
109
|
+
return route.includes(domain);
|
|
110
|
+
}
|
|
111
|
+
export function escapeRegExp(string) {
|
|
112
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& 表示整个被匹配的字符串
|
|
113
|
+
}
|
|
114
|
+
export const getAllSites = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
115
|
+
var _a;
|
|
116
|
+
const server = yield ApiService.getInstance();
|
|
117
|
+
const res = [];
|
|
118
|
+
while (true) {
|
|
119
|
+
const req = {
|
|
120
|
+
SiteSearchType: 'fuzzy',
|
|
121
|
+
Status: 'active',
|
|
122
|
+
PageNumber: res.length + 1,
|
|
123
|
+
PageSize: 500
|
|
124
|
+
};
|
|
125
|
+
const response = yield server.listSites(req);
|
|
126
|
+
if ((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.Sites) {
|
|
127
|
+
res.push(...response.data.Sites);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return res.map((site) => {
|
|
134
|
+
return {
|
|
135
|
+
label: site.SiteName,
|
|
136
|
+
value: site.SiteId
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import React, { useState } from 'react';
|
|
11
|
+
import { Box, render, Text } from 'ink';
|
|
12
|
+
import TextInput from 'ink-text-input';
|
|
13
|
+
export const DescriptionInput = ({ prompt, onSubmit, required }) => {
|
|
14
|
+
const [input, setInput] = useState('');
|
|
15
|
+
const [error, setError] = useState('');
|
|
16
|
+
const handleSubmit = () => {
|
|
17
|
+
if (required && !input.trim()) {
|
|
18
|
+
setError('This field is required');
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
onSubmit(input);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
25
|
+
React.createElement(Box, null,
|
|
26
|
+
React.createElement(Text, null, prompt)),
|
|
27
|
+
React.createElement(TextInput, { value: input, onChange: setInput, onSubmit: handleSubmit }),
|
|
28
|
+
error && (React.createElement(Box, null,
|
|
29
|
+
React.createElement(Text, { color: "red" }, error)))));
|
|
30
|
+
};
|
|
31
|
+
export const descriptionInput = (prompt_1, ...args_1) => __awaiter(void 0, [prompt_1, ...args_1], void 0, function* (prompt, required = false) {
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
const { unmount } = render(React.createElement(DescriptionInput, { prompt: prompt, required: required, onSubmit: (input) => {
|
|
34
|
+
unmount();
|
|
35
|
+
resolve(input);
|
|
36
|
+
} }));
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { Box, render, Text, useInput } from 'ink';
|
|
11
|
+
import TextInput from 'ink-text-input';
|
|
12
|
+
import React, { useState, useEffect } from 'react';
|
|
13
|
+
import t from '../i18n/index.js';
|
|
14
|
+
export const FilterSelector = ({ data, onSubmit, hideCount = 20 }) => {
|
|
15
|
+
const [inputValue, setInputValue] = useState('');
|
|
16
|
+
const [filteredData, setFilteredData] = useState(data);
|
|
17
|
+
const [isShowFilteredData, setIsShowFilteredData] = useState(false);
|
|
18
|
+
const [isSelectionMode, setIsSelectionMode] = useState(false);
|
|
19
|
+
const [selectedIndex, setSelectedIndex] = useState(-1);
|
|
20
|
+
const [tabPressCount, setTabPressCount] = useState(0);
|
|
21
|
+
const [showAll, setShowAll] = useState(false);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (showAll && !isSelectionMode) {
|
|
24
|
+
setFilteredData(data.filter((site) => site.label.includes(inputValue)));
|
|
25
|
+
}
|
|
26
|
+
}, [inputValue, data]);
|
|
27
|
+
const handleSubmit = () => {
|
|
28
|
+
onSubmit(inputValue);
|
|
29
|
+
};
|
|
30
|
+
useInput((input, key) => {
|
|
31
|
+
if (key.return && isSelectionMode && filteredData[selectedIndex]) {
|
|
32
|
+
setInputValue(filteredData[selectedIndex].label);
|
|
33
|
+
setIsSelectionMode(false);
|
|
34
|
+
setIsShowFilteredData(false);
|
|
35
|
+
setSelectedIndex(-1);
|
|
36
|
+
setTabPressCount(0);
|
|
37
|
+
}
|
|
38
|
+
else if (key.tab) {
|
|
39
|
+
if (tabPressCount === 0) {
|
|
40
|
+
const filteredDataInner = data.filter((site) => site.label.includes(inputValue));
|
|
41
|
+
setFilteredData(filteredDataInner);
|
|
42
|
+
if (filteredData.length === 1) {
|
|
43
|
+
setIsShowFilteredData(false);
|
|
44
|
+
setInputValue(filteredDataInner[0].label);
|
|
45
|
+
setSelectedIndex(0);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
setIsShowFilteredData(true);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else if (tabPressCount === 1) {
|
|
52
|
+
const filteredDataInner = data.filter((site) => site.label.includes(inputValue));
|
|
53
|
+
setFilteredData(filteredDataInner);
|
|
54
|
+
// 匹配结果大于等于1个时,进入选择模式
|
|
55
|
+
if ((filteredDataInner.length >= 1 &&
|
|
56
|
+
showAll &&
|
|
57
|
+
filteredDataInner.length > hideCount) ||
|
|
58
|
+
(filteredDataInner.length >= 1 &&
|
|
59
|
+
filteredDataInner.length <= hideCount)) {
|
|
60
|
+
setIsSelectionMode(true);
|
|
61
|
+
setSelectedIndex(0);
|
|
62
|
+
setInputValue(filteredDataInner[0].label);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else if (tabPressCount >= 2 &&
|
|
66
|
+
isSelectionMode &&
|
|
67
|
+
(showAll || filteredData.length <= hideCount) &&
|
|
68
|
+
filteredData.length > 1) {
|
|
69
|
+
setSelectedIndex((prevIndex) => (prevIndex + 1) % filteredData.length);
|
|
70
|
+
setInputValue(filteredData[(selectedIndex + 1) % filteredData.length].label);
|
|
71
|
+
}
|
|
72
|
+
setTabPressCount((prevCount) => prevCount + 1);
|
|
73
|
+
}
|
|
74
|
+
else if (key.downArrow && isSelectionMode) {
|
|
75
|
+
setSelectedIndex((prevIndex) => (prevIndex + 1) % filteredData.length);
|
|
76
|
+
setInputValue(filteredData[(selectedIndex + 1) % filteredData.length].label);
|
|
77
|
+
}
|
|
78
|
+
else if (key.upArrow && isSelectionMode) {
|
|
79
|
+
setSelectedIndex((prevIndex) => (prevIndex - 1 + filteredData.length) % filteredData.length);
|
|
80
|
+
setInputValue(filteredData[(selectedIndex - 1 + filteredData.length) % filteredData.length].label);
|
|
81
|
+
}
|
|
82
|
+
else if (key.leftArrow || key.rightArrow) {
|
|
83
|
+
}
|
|
84
|
+
else if (key.return) {
|
|
85
|
+
handleSubmit();
|
|
86
|
+
setTabPressCount(0);
|
|
87
|
+
}
|
|
88
|
+
else if (input === 'y' &&
|
|
89
|
+
!showAll &&
|
|
90
|
+
isShowFilteredData &&
|
|
91
|
+
(filteredData === null || filteredData === void 0 ? void 0 : filteredData.length) > hideCount) {
|
|
92
|
+
setShowAll(true);
|
|
93
|
+
setIsSelectionMode(true);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
setIsSelectionMode(false);
|
|
97
|
+
setIsShowFilteredData(false);
|
|
98
|
+
setSelectedIndex(-1);
|
|
99
|
+
setTabPressCount(0);
|
|
100
|
+
setShowAll(false);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
const renderFilterData = () => {
|
|
104
|
+
return ((showAll || filteredData.length <= hideCount) &&
|
|
105
|
+
filteredData.length > 1 && (React.createElement(React.Fragment, null,
|
|
106
|
+
React.createElement(Text, null, `👉 ${t('route_add_tab_tip').d('Press Tab to select')}`),
|
|
107
|
+
filteredData.map((item, index) => (React.createElement(Text, { key: index, inverse: index === selectedIndex }, item.label))))));
|
|
108
|
+
};
|
|
109
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
110
|
+
React.createElement(TextInput, { value: inputValue, highlightPastedText: true, onChange: (value) => {
|
|
111
|
+
if (!showAll &&
|
|
112
|
+
value[value.length - 1] === 'y' &&
|
|
113
|
+
tabPressCount === 1) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
setInputValue(value);
|
|
117
|
+
} }),
|
|
118
|
+
isShowFilteredData && (React.createElement(React.Fragment, null,
|
|
119
|
+
filteredData.length > hideCount && !showAll && (React.createElement(Text, null, t('route_add_see_more').d(`Do you wish to see all ${filteredData.length} possibilities? (y/n)`))),
|
|
120
|
+
renderFilterData()))));
|
|
121
|
+
};
|
|
122
|
+
export const promptFilterSelector = (siteList) => __awaiter(void 0, void 0, void 0, function* () {
|
|
123
|
+
return new Promise((resolve) => {
|
|
124
|
+
const { unmount } = render(React.createElement(FilterSelector, { onSubmit: (input) => {
|
|
125
|
+
unmount();
|
|
126
|
+
resolve(siteList.find((site) => site.label === input) || {
|
|
127
|
+
label: '',
|
|
128
|
+
value: ''
|
|
129
|
+
});
|
|
130
|
+
}, data: siteList }));
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import React, { useState } from 'react';
|
|
11
|
+
import { Box, render, Text, useInput } from 'ink';
|
|
12
|
+
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 }));
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from 'ink';
|
|
3
|
+
import SelectInput from 'ink-select-input';
|
|
4
|
+
import { Text } from 'ink';
|
|
5
|
+
import Item from './selectItem.js';
|
|
6
|
+
const Indicator = ({ isSelected }) => {
|
|
7
|
+
return React.createElement(Text, null, isSelected ? '👉 ' : ' ');
|
|
8
|
+
};
|
|
9
|
+
const SelectItems = ({ items, handleSelect }) => {
|
|
10
|
+
const { unmount } = render(React.createElement(SelectInput, { items: items, onSelect: onSelect, itemComponent: Item, indicatorComponent: Indicator }));
|
|
11
|
+
function onSelect(item) {
|
|
12
|
+
unmount();
|
|
13
|
+
handleSelect(item);
|
|
14
|
+
}
|
|
15
|
+
return unmount;
|
|
16
|
+
};
|
|
17
|
+
export default SelectItems;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import SelectItems from './selectInput.js';
|
|
2
|
+
const yesNoItems = [
|
|
3
|
+
{ label: 'Yes', value: 'yes' },
|
|
4
|
+
{ label: 'No', value: 'no' }
|
|
5
|
+
];
|
|
6
|
+
export const yesNoPrompt = (handleSelect, title) => {
|
|
7
|
+
console.log(title);
|
|
8
|
+
SelectItems({ items: yesNoItems, handleSelect });
|
|
9
|
+
};
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Commands
|
|
2
|
+
|
|
3
|
+
### init
|
|
4
|
+
|
|
5
|
+
Initialize a routine with a template.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
$ esa init [OPTIONS]
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- -c, --config `boolean` `optional`
|
|
12
|
+
- Generate a config file for your project.
|
|
13
|
+
|
|
14
|
+
### routine [script]
|
|
15
|
+
|
|
16
|
+
Manage your routine.
|
|
17
|
+
|
|
18
|
+
#### delete <routineName>
|
|
19
|
+
|
|
20
|
+
Delete a routine.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
$ esa routine delete <routineName>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- routineName `string` `required`
|
|
27
|
+
- The name of the routine to delete.
|
|
28
|
+
|
|
29
|
+
#### list
|
|
30
|
+
|
|
31
|
+
List all your routines.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
$ esa routine list
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### route [script]
|
|
38
|
+
|
|
39
|
+
Manage the routes bound to your routine.
|
|
40
|
+
|
|
41
|
+
#### add [route] [site]
|
|
42
|
+
|
|
43
|
+
Bind a Route to a routine.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
$ esa route add [route] [site]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
#### delete <route>
|
|
50
|
+
|
|
51
|
+
Delete a related route.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
$ esa route delete <route>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- route `string` `required`
|
|
58
|
+
- The name of the routes to delete.
|
|
59
|
+
|
|
60
|
+
#### list
|
|
61
|
+
|
|
62
|
+
List all related routes.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
$ esa route list
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### login
|
|
69
|
+
|
|
70
|
+
Login to the server.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
$ esa login
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### dev [entry]
|
|
77
|
+
|
|
78
|
+
Start a local server for developing your routine.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
$ esa dev [entry] [OPTIONS]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- entry `string` `optional`
|
|
85
|
+
- Entry file of the Routine.
|
|
86
|
+
|
|
87
|
+
- -port, --p `number` `optional`
|
|
88
|
+
- Port to listen on.
|
|
89
|
+
|
|
90
|
+
- --inspect-port `number` `optional`
|
|
91
|
+
- Chrome inspect devTool port.
|
|
92
|
+
|
|
93
|
+
- -minify, --m `boolean` `optional`
|
|
94
|
+
- Minify code during development.
|
|
95
|
+
|
|
96
|
+
- --local-upstream `string` `optional`
|
|
97
|
+
- Host to act as origin in development.
|
|
98
|
+
|
|
99
|
+
- --refresh-command `string` `optional`
|
|
100
|
+
- Provide a command to be executed before the auto-refresh on save.
|
|
101
|
+
|
|
102
|
+
### deployments [script]
|
|
103
|
+
|
|
104
|
+
Manage your deployments.
|
|
105
|
+
|
|
106
|
+
#### delete <deploymentId>
|
|
107
|
+
|
|
108
|
+
Delete one or more deployment versions.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
$ esa deployments delete <deploymentId>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
- deploymentId `string` `required`
|
|
115
|
+
- The ID of the deployments to delete.
|
|
116
|
+
|
|
117
|
+
#### list
|
|
118
|
+
|
|
119
|
+
List all deployments.
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
$ esa deployments list
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### deploy [entry]
|
|
126
|
+
|
|
127
|
+
Deploy your project.
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
$ esa deploy [entry]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
- entry `string` `optional`
|
|
134
|
+
- Entry file of the Routine.
|
|
135
|
+
|
|
136
|
+
### domain [script]
|
|
137
|
+
|
|
138
|
+
Manage the domain names bound to your routine.
|
|
139
|
+
|
|
140
|
+
#### add <domain>
|
|
141
|
+
|
|
142
|
+
Bind a domain to a routine.
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
$ esa domain add <domain>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
- domain `string` `required`
|
|
149
|
+
- The name of domain to add.
|
|
150
|
+
|
|
151
|
+
#### delete <domain>
|
|
152
|
+
|
|
153
|
+
Delete a related domain.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
$ esa domain delete <domain>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
- domains `string` `required`
|
|
160
|
+
- The names of the related domains to delete.
|
|
161
|
+
|
|
162
|
+
#### list
|
|
163
|
+
|
|
164
|
+
List all related domains.
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
$ esa domain list
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### commit [entry]
|
|
171
|
+
|
|
172
|
+
Commit your code, save as a new version.
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
$ esa commit [entry] [OPTIONS]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
- entry `string` `optional`
|
|
179
|
+
- Entry file of the Routine.
|
|
180
|
+
|
|
181
|
+
- -m, --minify `boolean` `optional`
|
|
182
|
+
- Minify code before committing.
|
|
183
|
+
|
|
184
|
+
### logout
|
|
185
|
+
|
|
186
|
+
Logout.
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
$ esa logout
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### config
|
|
193
|
+
|
|
194
|
+
Modify your local or global configuration using -l, -g.
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
$ esa config [OPTIONS]
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
- -g, --global `boolean` `optional`
|
|
201
|
+
- Edit global config file.
|
|
202
|
+
|
|
203
|
+
- -l, --local `boolean` `optional`
|
|
204
|
+
- Edit local config file.
|
|
205
|
+
|
|
206
|
+
### lang
|
|
207
|
+
|
|
208
|
+
Set the language of the CLI.
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
$ esa lang
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### site [script]
|
|
215
|
+
|
|
216
|
+
Manage your sites.
|
|
217
|
+
|
|
218
|
+
#### list
|
|
219
|
+
|
|
220
|
+
List all your sites.
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
$ esa site list
|
|
224
|
+
```
|