esa-cli 0.0.2-beta.13 → 0.0.2-beta.15

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.
@@ -17,6 +17,7 @@ const transport = new DailyRotateFile({
17
17
  });
18
18
  class Logger {
19
19
  constructor() {
20
+ this.spinnerText = '';
20
21
  const { combine, timestamp, label, printf } = format;
21
22
  const customFormat = printf(({ level, message, label: printLabel, timestamp: printTimestamp }) => {
22
23
  var _a;
@@ -65,6 +66,60 @@ class Logger {
65
66
  setLogLevel(level) {
66
67
  this.logger.level = level;
67
68
  }
69
+ /**
70
+ * Start a sub-step: show a spinner with the provided message.
71
+ * If a spinner is already running, just update its text.
72
+ */
73
+ startSubStep(message) {
74
+ this.spinnerText = message;
75
+ this.spinner.text = message;
76
+ if (!this.spinner.isSpinning) {
77
+ this.spinner.start();
78
+ }
79
+ }
80
+ /**
81
+ * End a sub-step: stop loading and replace spinner with `├` and final message.
82
+ * This overwrites the previous spinner line with the provided message.
83
+ */
84
+ endSubStep(message) {
85
+ // console.log(chalk.gray('├') + ' ' + this.spinnerText);
86
+ try {
87
+ if (this.spinner && this.spinner.isSpinning) {
88
+ this.spinner.stop();
89
+ }
90
+ }
91
+ catch (_a) { }
92
+ console.log(chalk.gray(`│ `));
93
+ console.log(chalk.gray('├ ') + this.spinnerText);
94
+ console.log(chalk.gray(`│ ${message}`));
95
+ }
96
+ stopSpinner() {
97
+ try {
98
+ if (this.spinner && this.spinner.isSpinning) {
99
+ this.spinner.stop();
100
+ }
101
+ }
102
+ catch (_a) { }
103
+ }
104
+ /**
105
+ * Prepare terminal output just before showing an interactive prompt.
106
+ * - Stops any active spinner
107
+ * - Replaces the previous line with a clean `╰ <text>` indicator
108
+ */
109
+ prepareForPrompt(text) {
110
+ this.stopSpinner();
111
+ const content = `╰ ${text || ''}`;
112
+ this.replacePrevLine(content);
113
+ }
114
+ /**
115
+ * Consolidate interactive prompt output after completion by replacing
116
+ * the previous N lines with a concise summary line.
117
+ * Defaults to 2 lines (prompt + answer line in most cases).
118
+ */
119
+ consolidateAfterPrompt(summary, linesToReplace = 2) {
120
+ const content = `├ ${summary}`;
121
+ this.replacePrevLines(linesToReplace, content);
122
+ }
68
123
  log(message) {
69
124
  console.log(message);
70
125
  }
@@ -155,25 +210,27 @@ class Logger {
155
210
  }
156
211
  console.log(lines.join('\n'));
157
212
  }
158
- // Cloudflare-like step rendering helpers
159
- cfStepHeader(title, step, total) {
213
+ StepHeader(title, step, total) {
160
214
  console.log(`\n╭ ${title} ${chalk.green(`Step ${step} of ${total}`)}`);
161
215
  console.log('│');
162
216
  }
163
- cfStepItem(prompt) {
217
+ StepItem(prompt) {
164
218
  console.log(`├ ${prompt}`);
165
219
  }
166
- cfStepKV(key, value) {
220
+ StepStart(prompt) {
221
+ console.log(`╭ ${prompt}`);
222
+ }
223
+ StepKV(key, value) {
167
224
  const orange = chalk.hex('#FFA500');
168
225
  console.log(`│ ${orange(key)} ${value}`);
169
226
  }
170
- cfStepSpacer() {
227
+ StepSpacer() {
171
228
  console.log('│');
172
229
  }
173
- cfStepEnd(str) {
230
+ StepEnd(str) {
174
231
  console.log(`╰ ${str || ''}`);
175
232
  }
176
- cfStepEndInline() {
233
+ StepEndInline() {
177
234
  try {
178
235
  process.stdout.write('╰ ');
179
236
  }
@@ -12,6 +12,7 @@ import chalk from 'chalk';
12
12
  import t from '../i18n/index.js';
13
13
  import { ApiService } from '../libs/apiService.js';
14
14
  import logger from '../libs/logger.js';
15
+ import { log } from '@clack/prompts';
15
16
  export function isRoutineExist(name) {
16
17
  return __awaiter(this, void 0, void 0, function* () {
17
18
  const server = yield ApiService.getInstance();
@@ -38,20 +39,31 @@ export function ensureRoutineExists(name) {
38
39
  const isExist = yield isRoutineExist(name);
39
40
  // If routine does not exist, create a new routine
40
41
  if (!isExist) {
41
- logger.log(t('first_deploy').d('This is the first time to deploy, we will create a new routine for you.'));
42
+ logger.startSubStep(`Creating routine ${chalk.gray(name)}`);
42
43
  const server = yield ApiService.getInstance();
43
44
  const createRes = yield server.createRoutine({
44
45
  name: name,
45
- description: ''
46
+ description: '',
47
+ hasAssets: true
46
48
  });
47
49
  const isSuccess = (createRes === null || createRes === void 0 ? void 0 : createRes.data.Status) === 'OK';
48
50
  if (isSuccess) {
49
- logger.success(t('routine_create_success').d('Routine created successfully.'));
51
+ // logger.endSubStep(
52
+ // t('routine_create_success').d('Routine created successfully.')
53
+ // );
54
+ logger.endSubStep('Routine created successfully');
55
+ // tlog.success(
56
+ // t('routine_create_success').d('Routine created successfully.')
57
+ // );
50
58
  }
51
59
  else {
52
- logger.error(t('routine_create_fail').d('Routine created failed.'));
60
+ logger.endSubStep('Routine created failed');
61
+ // tlog.error(t('routine_create_fail').d('Routine created failed.'));
53
62
  exit();
54
63
  }
55
64
  }
65
+ else {
66
+ log.step('Routine has already exists');
67
+ }
56
68
  });
57
69
  }
@@ -0,0 +1,149 @@
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 { spawn } from 'child_process';
11
+ import { cancel, spinner } from '@clack/prompts';
12
+ import chalk from 'chalk';
13
+ /**
14
+ * Execute a shell command with rich options (spinner, capture, env, cwd).
15
+ */
16
+ export const execCommand = (command_1, ...args_1) => __awaiter(void 0, [command_1, ...args_1], void 0, function* (command, options = {}) {
17
+ const { startText, doneText, silent = false, captureOutput = false, useSpinner = true, realtimeOutput = false, interactive = false, env, cwd, transformOutput, fallbackOutput, errorMessage } = options;
18
+ // Determine stdio mode based on options
19
+ // If realtimeOutput is true, we need to pipe to capture and display output in real-time
20
+ // If spinner is used without realtimeOutput, pipe to avoid TTY contention
21
+ // If silent is true, pipe to suppress output
22
+ // If captureOutput is true, pipe to capture output
23
+ // If interactive is true, always inherit stdio so prompts can be shown and accept input
24
+ const shouldPipe = !interactive && (realtimeOutput || useSpinner || silent || captureOutput);
25
+ const stdio = interactive
26
+ ? 'inherit'
27
+ : shouldPipe
28
+ ? 'pipe'
29
+ : 'inherit';
30
+ // start
31
+ const startMsg = startText || `Running: ${command.join(' ')}`;
32
+ const s = spinner();
33
+ // When realtimeOutput is enabled, don't use spinner as it conflicts with real-time output
34
+ if (useSpinner && !realtimeOutput && !interactive) {
35
+ s.start(startMsg);
36
+ }
37
+ else if (!silent) {
38
+ if (interactive) {
39
+ console.log(chalk.gray('│'));
40
+ console.log(chalk.gray('├ ') + startMsg);
41
+ }
42
+ else {
43
+ console.log(startMsg);
44
+ }
45
+ }
46
+ try {
47
+ let stdout = '';
48
+ let stderr = '';
49
+ // Use spawn for string[] commands to avoid shell quoting issues
50
+ const program = command[0];
51
+ const args = command.slice(1);
52
+ yield new Promise((resolve, reject) => {
53
+ var _a, _b;
54
+ const child = spawn(program, args, {
55
+ stdio,
56
+ cwd,
57
+ env: Object.assign(Object.assign({}, process.env), env),
58
+ shell: false
59
+ });
60
+ if (stdio === 'pipe') {
61
+ (_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (chunk) => {
62
+ const chunkStr = String(chunk);
63
+ stdout += chunkStr;
64
+ // Real-time output: display immediately if enabled and not silent
65
+ if (realtimeOutput && !silent) {
66
+ process.stdout.write(chunkStr);
67
+ }
68
+ });
69
+ (_b = child.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (chunk) => {
70
+ const chunkStr = String(chunk);
71
+ stderr += chunkStr;
72
+ // Real-time output: display immediately if enabled and not silent
73
+ if (realtimeOutput && !silent) {
74
+ process.stderr.write(chunkStr);
75
+ }
76
+ });
77
+ }
78
+ child.on('error', (error) => {
79
+ reject(error);
80
+ });
81
+ child.on('close', (code) => {
82
+ if (code && code !== 0) {
83
+ reject({ stdout, stderr, message: `Exit code ${code}` });
84
+ }
85
+ else {
86
+ resolve();
87
+ }
88
+ });
89
+ });
90
+ // Preserve original output for optional mirroring
91
+ const bufferedStdout = stdout;
92
+ const bufferedStderr = stderr;
93
+ if (transformOutput) {
94
+ stdout = transformOutput(stdout);
95
+ }
96
+ const endMsg = typeof doneText === 'function' ? doneText(stdout) : doneText || 'Done';
97
+ if (useSpinner && !realtimeOutput && !interactive) {
98
+ s.stop(endMsg);
99
+ }
100
+ else if (!silent) {
101
+ if (interactive) {
102
+ console.log(chalk.gray('├ ') + endMsg);
103
+ }
104
+ else {
105
+ console.log(endMsg);
106
+ }
107
+ }
108
+ // If spinner was used and user expects output (silent=false, captureOutput=false),
109
+ // and realtimeOutput is not enabled, print the buffered child output now to avoid interfering with spinner rendering.
110
+ if (useSpinner &&
111
+ !silent &&
112
+ !captureOutput &&
113
+ !realtimeOutput &&
114
+ !interactive) {
115
+ if (bufferedStdout)
116
+ process.stdout.write(bufferedStdout);
117
+ if (bufferedStderr)
118
+ process.stderr.write(bufferedStderr);
119
+ }
120
+ return { success: true, stdout, stderr };
121
+ }
122
+ catch (err) {
123
+ const e = err;
124
+ const stdout = (e === null || e === void 0 ? void 0 : e.stdout) ? String(e.stdout) : '';
125
+ const stderr = (e === null || e === void 0 ? void 0 : e.stderr) ? String(e.stderr) : (e === null || e === void 0 ? void 0 : e.message) || '';
126
+ const msg = (fallbackOutput && fallbackOutput(err)) ||
127
+ (stderr ? `Command failed: ${stdout}` : 'Command failed');
128
+ if (useSpinner && !realtimeOutput && !interactive)
129
+ s.stop(msg);
130
+ else if (!silent)
131
+ console.error(msg);
132
+ // Mirror buffered outputs on failure when spinner is used and not silent and realtimeOutput is not enabled
133
+ if (useSpinner &&
134
+ !silent &&
135
+ !captureOutput &&
136
+ !realtimeOutput &&
137
+ !interactive) {
138
+ if (stdout)
139
+ process.stdout.write(stdout);
140
+ if (stderr)
141
+ process.stderr.write(stderr);
142
+ }
143
+ if (errorMessage) {
144
+ cancel(errorMessage);
145
+ }
146
+ return { success: false, stdout, stderr };
147
+ }
148
+ });
149
+ export default execCommand;
@@ -24,8 +24,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
24
24
  };
25
25
  import fs from 'fs';
26
26
  import path from 'path';
27
+ import { exit } from 'process';
27
28
  import AdmZip from 'adm-zip';
29
+ import chalk from 'chalk';
28
30
  import prodBuild from '../commands/commit/prodBuild.js';
31
+ import t from '../i18n/index.js';
32
+ import logger from '../libs/logger.js';
29
33
  import { checkEdgeRoutineType, EDGE_ROUTINE_TYPE } from './checkAssetsExist.js';
30
34
  import { getProjectConfig, readEdgeRoutineFile } from './fileUtils/index.js';
31
35
  const compress = (scriptEntry_1, assetsDir_1, ...args_1) => __awaiter(void 0, [scriptEntry_1, assetsDir_1, ...args_1], void 0, function* (scriptEntry, assetsDir, minify = false, projectPath) {
@@ -33,15 +37,51 @@ const compress = (scriptEntry_1, assetsDir_1, ...args_1) => __awaiter(void 0, [s
33
37
  let code;
34
38
  const zip = new AdmZip();
35
39
  const projectConfig = getProjectConfig(projectPath);
40
+ let assetsDirectory = assetsDir || ((_a = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.assets) === null || _a === void 0 ? void 0 : _a.directory);
41
+ assetsDirectory = path.resolve(projectPath !== null && projectPath !== void 0 ? projectPath : '', assetsDirectory !== null && assetsDirectory !== void 0 ? assetsDirectory : '');
36
42
  const routineType = checkEdgeRoutineType(scriptEntry, assetsDir, projectPath);
37
43
  if (!projectConfig) {
38
44
  throw new Error('Project config not found');
39
45
  }
40
46
  // 参数优先:如果有参数则使用参数,否则使用配置文件中的值
41
47
  const entry = scriptEntry || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.entry);
42
- let assetsDirectory = assetsDir || ((_a = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.assets) === null || _a === void 0 ? void 0 : _a.directory);
43
48
  if (routineType === EDGE_ROUTINE_TYPE.NOT_EXIST) {
44
- throw new Error('Entry file not found in project config');
49
+ const errorMessage = [
50
+ chalk.red.bold('❌ File upload failed'),
51
+ '',
52
+ chalk.cyan('📋 Current configuration information:'),
53
+ `${chalk.white(' 📁 Project path:')} ${chalk.yellow(projectPath || chalk.gray(t('compress_not_specified').d('Not specified')))}`,
54
+ `${chalk.white(' 📄 Entry file:')} ${chalk.yellow(scriptEntry ||
55
+ (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.entry) ||
56
+ chalk.gray(t('compress_not_configured').d('Not configured')))}`,
57
+ `${chalk.white(' 🗂️ Assets directory:')} ${chalk.yellow(assetsDirectory || chalk.gray(t('compress_not_configured').d('Not configured')))}`,
58
+ '',
59
+ chalk.cyan('🔍 Possible issue causes:'),
60
+ chalk.white(' 1. Entry file path is incorrect or file does not exist'),
61
+ chalk.white(' 2. Assets directory path is incorrect or directory does not exist'),
62
+ chalk.white(` 3. Project configuration file ${chalk.yellow('esa.jsonc')} or ${chalk.yellow('esa.toml')} format error`),
63
+ chalk.white(` 4. Relative path format error, please use ${chalk.yellow('./xxx')} format`),
64
+ '',
65
+ chalk.yellow.bold(`📍 Please check if the following ${chalk.red('absolute paths')} are correct:`),
66
+ ...(scriptEntry || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.entry)
67
+ ? [
68
+ `${chalk.white(' 📄 Entry file:')} ${chalk.cyan.bold(path.resolve(projectPath !== null && projectPath !== void 0 ? projectPath : '', scriptEntry || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.entry) || ''))} ${chalk.gray(t('compress_check_file_exists').d('(Check if file exists)'))}`
69
+ ]
70
+ : []),
71
+ ...(assetsDirectory
72
+ ? [
73
+ `${chalk.white(' 🗂️ Assets directory:')} ${chalk.cyan.bold(path.resolve(projectPath !== null && projectPath !== void 0 ? projectPath : '', assetsDirectory))} ${chalk.gray(t('compress_check_directory_exists').d('(Check if directory exists)'))}`
74
+ ]
75
+ : []),
76
+ ...(!scriptEntry && !(projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.entry) && !assetsDirectory
77
+ ? [
78
+ chalk.yellow.bold(' ⚠️ You need to configure at least one of entry file or assets directory')
79
+ ]
80
+ : []),
81
+ ''
82
+ ].join('\n');
83
+ logger.error(errorMessage);
84
+ exit(0);
45
85
  }
46
86
  if (routineType === EDGE_ROUTINE_TYPE.JS_ONLY ||
47
87
  routineType === EDGE_ROUTINE_TYPE.JS_AND_ASSETS) {
@@ -73,8 +113,6 @@ const compress = (scriptEntry_1, assetsDir_1, ...args_1) => __awaiter(void 0, [s
73
113
  };
74
114
  addDirectoryToZip(assetsDirectory, 'assets');
75
115
  }
76
- //输出zip
77
- zip.writeZip('testassets.zip');
78
116
  return zip;
79
117
  });
80
118
  export default compress;
@@ -88,6 +88,7 @@ export function readConfigFile(configPath) {
88
88
  }
89
89
  }
90
90
  catch (error) {
91
+ console.log(error);
91
92
  logger.error(`Error parsing config file: ${error}`);
92
93
  return null;
93
94
  }
@@ -136,7 +137,7 @@ export function getConfigurations() {
136
137
  }
137
138
  }
138
139
  export function generateConfigFile(projectName_1, initConfigs_1, targetDir_1) {
139
- return __awaiter(this, arguments, void 0, function* (projectName, initConfigs, targetDir, configFormat = 'toml', isSinglePageApplication = false) {
140
+ return __awaiter(this, arguments, void 0, function* (projectName, initConfigs, targetDir, configFormat = 'toml', notFoundStrategy) {
140
141
  var _a, _b;
141
142
  const outputDir = targetDir !== null && targetDir !== void 0 ? targetDir : process.cwd();
142
143
  const currentDirName = path.basename(outputDir);
@@ -148,45 +149,40 @@ export function generateConfigFile(projectName_1, initConfigs_1, targetDir_1) {
148
149
  let genConfig;
149
150
  if (configFormat === 'jsonc') {
150
151
  newFilePath = path.join(outputDir, 'esa.jsonc');
151
- const assetsBlock = assetsDirectory
152
- ? `,
153
- "assets": {
154
- "directory": "${assetsDirectory}"
155
- ${isSinglePageApplication
156
- ? `,
157
- "notFoundStrategy": "singlePageApplication"`
158
- : ''}
159
- }`
160
- : '';
161
- const entryBlock = entry
162
- ? `,
163
- "entry": "${entry}"`
164
- : '';
165
- genConfig = `{
166
- "name": "${name}"${entryBlock}${assetsBlock},
167
- "dev": {
168
- "port": ${port}
169
- }
170
- }`;
152
+ const configObj = { name };
153
+ if (entry)
154
+ configObj.entry = entry;
155
+ if (assetsDirectory) {
156
+ configObj.assets = { directory: assetsDirectory };
157
+ if (notFoundStrategy) {
158
+ configObj.assets.notFoundStrategy =
159
+ notFoundStrategy;
160
+ }
161
+ }
162
+ configObj.dev = { port };
163
+ genConfig = JSON.stringify(configObj, null, 2) + '\n';
171
164
  }
172
165
  else {
173
166
  // Default to TOML format
174
167
  newFilePath = path.join(outputDir, 'esa.toml');
175
- const assetsBlock = assetsDirectory
176
- ? `
177
- [assets]
178
- directory = "${assetsDirectory}"
179
- `
180
- : '';
181
- genConfig = `name = "${name}"
182
- entry = "${entry}"
183
- ${assetsBlock}${isSinglePageApplication ? 'notFoundStrategy = "singlePageApplication"' : ''}
184
- [dev]
185
- port = ${port}
186
- `;
168
+ const configObj = {
169
+ name,
170
+ dev: { port }
171
+ };
172
+ if (entry)
173
+ configObj.entry = entry;
174
+ if (assetsDirectory) {
175
+ configObj.assets = { directory: assetsDirectory };
176
+ if (notFoundStrategy) {
177
+ configObj.assets.notFoundStrategy =
178
+ notFoundStrategy;
179
+ }
180
+ }
181
+ genConfig = toml.stringify(configObj);
187
182
  }
188
183
  if (fs.existsSync(newFilePath)) {
189
- logger.error(t('generate_config_error').d(`${path.basename(newFilePath)} already exists`));
184
+ logger.error(`${path.basename(newFilePath)}` +
185
+ t('generate_config_error').d('already exists'));
190
186
  return;
191
187
  }
192
188
  else {
@@ -0,0 +1,97 @@
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 { confirm as clackConfirm, isCancel, multiselect as clackMultiselect, select as clackSelect, text as clackText, cancel as clackCancel } from '@clack/prompts';
11
+ import multiLevelSelect from '../components/mutiLevelSelect.js';
12
+ function normalizeChoices(choices) {
13
+ if (!choices)
14
+ return undefined;
15
+ return choices.map((c) => {
16
+ if (typeof c === 'string')
17
+ return { label: c, value: c };
18
+ return { label: c.name, value: String(c.value), hint: c.hint };
19
+ });
20
+ }
21
+ export function promptParameter(param) {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ var _a;
24
+ const { type, question, defaultValue, validate } = param;
25
+ let value = '';
26
+ const msg = question;
27
+ if (type === 'text') {
28
+ const v = yield clackText({
29
+ message: msg,
30
+ placeholder: typeof defaultValue === 'string' ? defaultValue : undefined,
31
+ initialValue: typeof defaultValue === 'string' ? defaultValue : undefined,
32
+ validate: validate
33
+ ? (val) => {
34
+ if (val === undefined)
35
+ return 'Value is required';
36
+ const res = validate(val);
37
+ return res === true ? undefined : res;
38
+ }
39
+ : undefined
40
+ });
41
+ if (isCancel(v)) {
42
+ clackCancel('Operation cancelled.');
43
+ process.exit(130);
44
+ }
45
+ value = v;
46
+ }
47
+ else if (type === 'confirm') {
48
+ const v = yield clackConfirm({
49
+ message: msg,
50
+ initialValue: (_a = defaultValue) !== null && _a !== void 0 ? _a : false
51
+ });
52
+ if (isCancel(v)) {
53
+ clackCancel('Operation cancelled.');
54
+ process.exit(130);
55
+ }
56
+ value = v;
57
+ }
58
+ else if (type === 'select') {
59
+ const options = normalizeChoices(param.choices) || [];
60
+ const v = yield clackSelect({
61
+ message: msg,
62
+ options,
63
+ initialValue: defaultValue || undefined
64
+ });
65
+ if (isCancel(v)) {
66
+ clackCancel('Operation cancelled.');
67
+ process.exit(130);
68
+ }
69
+ value = v;
70
+ }
71
+ else if (type === 'multiselect') {
72
+ const options = normalizeChoices(param.choices) || [];
73
+ const initialValues = (defaultValue || []).map((v) => String(v));
74
+ const v = yield clackMultiselect({
75
+ message: msg,
76
+ options,
77
+ initialValues
78
+ });
79
+ if (isCancel(v)) {
80
+ clackCancel('Operation cancelled.');
81
+ process.exit(130);
82
+ }
83
+ value = v;
84
+ }
85
+ else if (type === 'multiLevelSelect') {
86
+ const items = (param.treeItems || []);
87
+ const v = yield multiLevelSelect(items, msg);
88
+ if (v === null) {
89
+ clackCancel('Operation cancelled.');
90
+ process.exit(130);
91
+ }
92
+ value = v;
93
+ }
94
+ return value;
95
+ });
96
+ }
97
+ export default promptParameter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esa-cli",
3
- "version": "0.0.2-beta.13",
3
+ "version": "0.0.2-beta.15",
4
4
  "description": "A CLI for operating Alibaba Cloud ESA EdgeRoutine (Edge Functions).",
5
5
  "main": "bin/enter.cjs",
6
6
  "type": "module",
@@ -72,6 +72,7 @@
72
72
  "vitest": "^2.0.4"
73
73
  },
74
74
  "dependencies": {
75
+ "@clack/prompts": "1.0.0-alpha.4",
75
76
  "@alicloud/esa20240910": "2.25.0",
76
77
  "@alicloud/openapi-client": "^0.4.7",
77
78
  "@babel/generator": "^7.26.3",
@@ -90,6 +91,7 @@
90
91
  "esbuild-plugin-less": "^1.3.8",
91
92
  "form-data": "^4.0.0",
92
93
  "fs-extra": "^11.2.0",
94
+ "haikunator": "^2.1.2",
93
95
  "http-proxy-agent": "^7.0.2",
94
96
  "ink": "^5.0.1",
95
97
  "ink-select-input": "^6.0.0",
@@ -1 +0,0 @@
1
-
@@ -1,73 +0,0 @@
1
- # ESLint 配置说明
2
-
3
- 本项目已配置ESLint来检查代码质量,包括以下功能:
4
-
5
- ## 已配置的检查规则
6
-
7
- ### 1. Import 排序检查
8
-
9
- - **规则**: `import/order`
10
- - **功能**: 自动排序import语句,按照以下顺序:
11
- 1. Node.js 内置模块 (如 `fs`, `path`)
12
- 2. 第三方模块 (如 `react`, `lodash`)
13
- 3. 内部模块 (项目内部文件)
14
- 4. 父级模块 (`../`)
15
- 5. 同级模块 (`./`)
16
- 6. 当前目录的 index 文件
17
- - **格式**: 每组之间用空行分隔,按字母顺序排列
18
-
19
- ### 2. 引号检查
20
-
21
- - **规则**: `quotes`, `@typescript-eslint/quotes`
22
- - **功能**: 强制使用单引号
23
- - **例外**: 允许在需要转义的情况下使用双引号,支持模板字符串
24
-
25
- ### 3. 多余变量检查
26
-
27
- - **规则**: `@typescript-eslint/no-unused-vars`
28
- - **功能**: 检查未使用的变量、参数和导入
29
- - **例外**: 以 `_` 开头的变量会被忽略(如 `_unusedVariable`)
30
-
31
- ### 4. 其他检查
32
-
33
- - **重复导入检查**: `import/no-duplicates`
34
- - **未使用表达式检查**: `no-unused-expressions`
35
- - **TypeScript类型检查**: 警告使用 `any` 类型
36
-
37
- ## 使用方法
38
-
39
- ### 检查代码
40
-
41
- ```bash
42
- npm run eslint
43
- ```
44
-
45
- ### 自动修复
46
-
47
- ```bash
48
- npm run eslint -- --fix
49
- ```
50
-
51
- ### 检查特定文件
52
-
53
- ```bash
54
- npx eslint src/commands/example.ts
55
- ```
56
-
57
- ## 配置说明
58
-
59
- 配置文件位于 `.eslintrc.cjs`,主要包含:
60
-
61
- - **解析器**: `@typescript-eslint/parser` 用于解析TypeScript代码
62
- - **插件**:
63
- - `react` 和 `react-hooks` 用于React相关检查
64
- - `import` 用于import相关检查
65
- - `@typescript-eslint` 用于TypeScript特定检查
66
- - **规则**: 详细的代码质量规则配置
67
- - **设置**: TypeScript import解析器配置
68
-
69
- ## 注意事项
70
-
71
- 1. 如果遇到TypeScript版本警告,这是正常的,不影响功能
72
- 2. 未使用的变量请以 `_` 开头命名,这样ESLint会忽略它们
73
- 3. 自动修复功能可以解决大部分格式问题,但逻辑问题需要手动修复