esa-cli 0.0.2-beta.21 → 0.0.2-beta.22

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.
@@ -69,6 +69,11 @@ export function handleCommit(argv) {
69
69
  }
70
70
  logger.startSubStep('Generating code version');
71
71
  const res = yield generateCodeVersion(projectName, description, argv === null || argv === void 0 ? void 0 : argv.entry, argv === null || argv === void 0 ? void 0 : argv.assets, argv === null || argv === void 0 ? void 0 : argv.minify);
72
+ const { isSuccess } = res || {};
73
+ if (!isSuccess) {
74
+ logger.endSubStep('Generate version failed');
75
+ exit(1);
76
+ }
72
77
  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;
73
78
  if (!codeVersion) {
74
79
  logger.endSubStep('Missing CodeVersion in response');
@@ -61,6 +61,9 @@ export function commitRoutineWithAssets(requestParams, zipBuffer) {
61
61
  if (uploadSuccess) {
62
62
  break;
63
63
  }
64
+ if (i < 2) {
65
+ yield new Promise((resolve) => setTimeout(resolve, 2000));
66
+ }
64
67
  }
65
68
  return {
66
69
  isSuccess: uploadSuccess,
@@ -120,100 +123,97 @@ export function generateCodeVersion(projectName_1, description_1, entry_1, asset
120
123
  return __awaiter(this, arguments, void 0, function* (projectName, description, entry, assets, minify = false, projectPath) {
121
124
  var _a;
122
125
  const { zip, sourceList, dynamicSources } = yield compress(entry, assets, minify, projectPath);
123
- try {
124
- // Pretty print upload directory tree
125
- const buildTree = (paths, decorateTopLevel) => {
126
- const root = { children: new Map(), isFile: false };
127
- const sorted = [...paths].sort((a, b) => a.localeCompare(b));
128
- for (const p of sorted) {
129
- const parts = p.split('/').filter(Boolean);
130
- let node = root;
131
- for (let i = 0; i < parts.length; i++) {
132
- const part = parts[i];
133
- if (!node.children.has(part)) {
134
- node.children.set(part, { children: new Map(), isFile: false });
135
- }
136
- const child = node.children.get(part);
137
- if (i === parts.length - 1)
138
- child.isFile = true;
139
- node = child;
126
+ // Pretty print upload directory tree
127
+ const buildTree = (paths, decorateTopLevel) => {
128
+ const root = { children: new Map(), isFile: false };
129
+ const sorted = [...paths].sort((a, b) => a.localeCompare(b));
130
+ for (const p of sorted) {
131
+ const parts = p.split('/').filter(Boolean);
132
+ let node = root;
133
+ for (let i = 0; i < parts.length; i++) {
134
+ const part = parts[i];
135
+ if (!node.children.has(part)) {
136
+ node.children.set(part, { children: new Map(), isFile: false });
140
137
  }
138
+ const child = node.children.get(part);
139
+ if (i === parts.length - 1)
140
+ child.isFile = true;
141
+ node = child;
141
142
  }
142
- const lines = [];
143
- const render = (node, prefix, depth) => {
144
- const entries = [...node.children.entries()];
145
- entries.forEach(([_name, _child], idx) => {
146
- const isLast = idx === entries.length - 1;
147
- const connector = isLast ? '└ ' : '├ ';
148
- const nextPrefix = prefix + (isLast ? ' ' : '│ ');
149
- const displayName = depth === 0 ? decorateTopLevel(_name) : _name;
150
- lines.push(prefix + connector + displayName);
151
- render(_child, nextPrefix, depth + 1);
152
- });
153
- };
154
- render(root, '', 0);
155
- return lines.length ? lines : ['-'];
156
- };
157
- const header = chalk.hex('#22c55e')('UPLOAD') + ' Files to be uploaded (source paths)';
158
- logger.block();
159
- logger.log(header);
160
- const dynamicSet = new Set(dynamicSources);
161
- const LIMIT = 300;
162
- const staticPaths = sourceList
163
- .filter((p) => !dynamicSet.has(p))
164
- .sort((a, b) => a.localeCompare(b));
165
- const dynamicPaths = sourceList
166
- .filter((p) => dynamicSet.has(p))
167
- .sort((a, b) => a.localeCompare(b));
168
- let omitted = 0;
169
- let shownStatic = staticPaths;
170
- if (staticPaths.length > LIMIT) {
171
- shownStatic = staticPaths.slice(0, LIMIT);
172
- omitted = staticPaths.length - LIMIT;
173
143
  }
174
- // Compute top-level markers based on whether a top-level bucket contains dynamic/static files
175
- const topLevelStats = new Map();
176
- const addStat = (p, isDynamic) => {
177
- const top = p.split('/')[0] || p;
178
- const stat = topLevelStats.get(top) || {
179
- hasDynamic: false,
180
- hasStatic: false
181
- };
182
- if (isDynamic)
183
- stat.hasDynamic = true;
184
- else
185
- stat.hasStatic = true;
186
- topLevelStats.set(top, stat);
144
+ const lines = [];
145
+ const render = (node, prefix, depth) => {
146
+ const entries = [...node.children.entries()];
147
+ entries.forEach(([_name, _child], idx) => {
148
+ const isLast = idx === entries.length - 1;
149
+ const connector = isLast ? '└ ' : '├ ';
150
+ const nextPrefix = prefix + (isLast ? ' ' : '│ ');
151
+ const displayName = depth === 0 ? decorateTopLevel(_name) : _name;
152
+ lines.push(prefix + connector + displayName);
153
+ render(_child, nextPrefix, depth + 1);
154
+ });
187
155
  };
188
- dynamicPaths.forEach((p) => addStat(p, true));
189
- shownStatic.forEach((p) => addStat(p, false));
190
- const dynamicMarker = chalk.bold.yellowBright(' (dynamic)');
191
- const staticMarker = chalk.bold.greenBright(' (static)');
192
- const decorateTopLevel = (name) => {
193
- const stat = topLevelStats.get(name);
194
- if (!stat)
195
- return name;
196
- if (stat.hasDynamic && stat.hasStatic) {
197
- return `${name}${dynamicMarker}${staticMarker}`;
198
- }
199
- if (stat.hasDynamic)
200
- return `${name}${dynamicMarker}`;
201
- if (stat.hasStatic)
202
- return `${name}${staticMarker}`;
203
- return name;
156
+ render(root, '', 0);
157
+ return lines.length ? lines : ['-'];
158
+ };
159
+ const header = chalk.hex('#22c55e')('UPLOAD') + ' Files to be uploaded (source paths)';
160
+ logger.block();
161
+ logger.log(header);
162
+ const dynamicSet = new Set(dynamicSources);
163
+ const LIMIT = 300;
164
+ const staticPaths = sourceList
165
+ .filter((p) => !dynamicSet.has(p))
166
+ .sort((a, b) => a.localeCompare(b));
167
+ const dynamicPaths = sourceList
168
+ .filter((p) => dynamicSet.has(p))
169
+ .sort((a, b) => a.localeCompare(b));
170
+ let omitted = 0;
171
+ let shownStatic = staticPaths;
172
+ if (staticPaths.length > LIMIT) {
173
+ shownStatic = staticPaths.slice(0, LIMIT);
174
+ omitted = staticPaths.length - LIMIT;
175
+ }
176
+ // Compute top-level markers based on whether a top-level bucket contains dynamic/static files
177
+ const topLevelStats = new Map();
178
+ const addStat = (p, isDynamic) => {
179
+ const top = p.split('/')[0] || p;
180
+ const stat = topLevelStats.get(top) || {
181
+ hasDynamic: false,
182
+ hasStatic: false
204
183
  };
205
- const combined = [...dynamicPaths, ...shownStatic];
206
- const treeLines = buildTree(combined, decorateTopLevel);
207
- for (const line of treeLines) {
208
- logger.log(line);
209
- }
210
- if (omitted > 0) {
211
- const note = chalk.gray(`仅展示前 ${LIMIT} 个静态文件,已省略 ${omitted} 个`);
212
- logger.log(note);
184
+ if (isDynamic)
185
+ stat.hasDynamic = true;
186
+ else
187
+ stat.hasStatic = true;
188
+ topLevelStats.set(top, stat);
189
+ };
190
+ dynamicPaths.forEach((p) => addStat(p, true));
191
+ shownStatic.forEach((p) => addStat(p, false));
192
+ const dynamicMarker = chalk.bold.yellowBright(' (dynamic)');
193
+ const staticMarker = chalk.bold.greenBright(' (static)');
194
+ const decorateTopLevel = (name) => {
195
+ const stat = topLevelStats.get(name);
196
+ if (!stat)
197
+ return name;
198
+ if (stat.hasDynamic && stat.hasStatic) {
199
+ return `${name}${dynamicMarker}${staticMarker}`;
213
200
  }
214
- logger.block();
201
+ if (stat.hasDynamic)
202
+ return `${name}${dynamicMarker}`;
203
+ if (stat.hasStatic)
204
+ return `${name}${staticMarker}`;
205
+ return name;
206
+ };
207
+ const combined = [...dynamicPaths, ...shownStatic];
208
+ const treeLines = buildTree(combined, decorateTopLevel);
209
+ for (const line of treeLines) {
210
+ logger.log(line);
215
211
  }
216
- catch (_b) { }
212
+ if (omitted > 0) {
213
+ const note = chalk.gray(`Only show the first ${LIMIT} static files, omitted ${omitted} files`);
214
+ logger.log(note);
215
+ }
216
+ logger.block();
217
217
  const projectConfig = getProjectConfig(projectPath);
218
218
  const notFoundStrategy = normalizeNotFoundStrategy((_a = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.assets) === null || _a === void 0 ? void 0 : _a.notFoundStrategy);
219
219
  logger.startSubStep('Generating code version');
@@ -75,6 +75,7 @@ export function handleDeploy(argv) {
75
75
  getRoot().split(/[\\/]/).pop() ||
76
76
  '', true, true);
77
77
  }
78
+ exit(success ? 0 : 1);
78
79
  });
79
80
  }
80
81
  export default deploy;
@@ -52,7 +52,7 @@ export function handleDeleteDeployments(argv) {
52
52
  const isInteractive = argv.i;
53
53
  if (isInteractive) {
54
54
  const { allVersions, stagingVersions, productionVersions } = yield getRoutineCodeVersions(projectConfig.name);
55
- // 显示正在部署的版本信息
55
+ // Show information about versions being deployed
56
56
  if (stagingVersions.length > 0 || productionVersions.length > 0) {
57
57
  logger.log(chalk.yellow('⚠️ Currently deploying versions:'));
58
58
  if (stagingVersions.length > 0) {
@@ -64,7 +64,7 @@ export function handleDeleteDeployments(argv) {
64
64
  logger.log('');
65
65
  }
66
66
  logger.log(t('delete_deployments_table_title').d(' Version ID Description'));
67
- // 过滤掉正在部署的版本
67
+ // Filter out versions being deployed
68
68
  const selectList = allVersions
69
69
  .filter((item) => {
70
70
  var _a, _b;
@@ -16,7 +16,7 @@ import { getRoot, getDirName } from '../../../utils/fileUtils/base.js';
16
16
  import { getDevConf } from '../../../utils/fileUtils/index.js';
17
17
  import { EW2Path } from '../../../utils/installEw2.js';
18
18
  import devBuild from '../build.js';
19
- // 生成可用的Ew2端口
19
+ // Generate available Ew2 port
20
20
  const generateEw2Port = () => __awaiter(void 0, void 0, void 0, function* () {
21
21
  let ew2port = 3322;
22
22
  let portAvailable = yield checkPort(ew2port);
@@ -60,7 +60,7 @@ conf_path = "${erConfPath}"
60
60
  fs.promises.writeFile(erConfPath, erConf)
61
61
  ]);
62
62
  };
63
- // 生成入口文件
63
+ // Generate entry file
64
64
  const generateEntry = (id, projectEntry, userRoot, port) => __awaiter(void 0, void 0, void 0, function* () {
65
65
  const __dirname = getDirName(import.meta.url);
66
66
  const devDir = path.resolve(userRoot, '.dev');
@@ -85,20 +85,20 @@ const generateEntry = (id, projectEntry, userRoot, port) => __awaiter(void 0, vo
85
85
  .replace(/'\$userPath'/g, `'${projectEntry.replace(/\\/g, '/')}'`)
86
86
  .replace(/\$userPort/g, `${port}`));
87
87
  });
88
- // 前期准备
88
+ // Preliminary preparation
89
89
  const prepare = (configPath, entry, port, localUpstream, userRoot) => __awaiter(void 0, void 0, void 0, function* () {
90
90
  const options = {};
91
91
  const currentOptions = { entry, port, localUpstream };
92
- // 支持同时跑多个 worker
92
+ // Support running multiple workers simultaneously
93
93
  const id = new Date().getTime().toString();
94
94
  // @ts-ignore
95
95
  global.id = id;
96
- // 生成入口文件
96
+ // Generate entry file
97
97
  yield generateEntry(id, entry, userRoot, port);
98
- // 生成 Ew2 配置
98
+ // Generate Ew2 configuration
99
99
  const ew2port = yield generateEw2Port();
100
100
  yield writeEw2config(id, ew2port, userRoot);
101
- // 给每一次 dev 的配置项,在一个文件中通过 id 区分
101
+ // Configuration items for each dev session, distinguished by id in one file
102
102
  if (fs.existsSync(configPath)) {
103
103
  const currentConfig = fs
104
104
  .readFileSync(configPath, 'utf-8')
@@ -106,7 +106,7 @@ const prepare = (configPath, entry, port, localUpstream, userRoot) => __awaiter(
106
106
  const currentConfigObj = JSON.parse(currentConfig);
107
107
  const currentIds = Object.keys(currentConfigObj);
108
108
  if (currentIds[0] && /^\d+$/.test(currentIds[0])) {
109
- // 删除没有用到的入口
109
+ // Remove unused entries
110
110
  for (let currentId of currentIds) {
111
111
  const unused = yield checkPort(currentConfigObj[currentId].port);
112
112
  if (unused) {
@@ -155,7 +155,7 @@ const devPack = () => __awaiter(void 0, void 0, void 0, function* () {
155
155
  }
156
156
  else {
157
157
  logger.notInProject();
158
- process.exit(0);
158
+ process.exit(1);
159
159
  }
160
160
  return prepare(path.resolve(userRoot, '.dev/devConfig.js'), projectEntry, port, localUpstream, userRoot)
161
161
  .then(() => {
@@ -10,9 +10,15 @@ class EdgeKV {
10
10
  try {
11
11
  const kvJson = fs.readFileSync(kvPath, 'utf8');
12
12
  const kvJsonObj = JSON.parse(kvJson);
13
+ console.log(kvJsonObj);
13
14
  Object.keys(kvJsonObj).forEach((namespace) => {
14
- EdgeKV.store.set(namespace, new Map(Object.entries(kvJsonObj[namespace])));
15
+ const childMap = new Map();
16
+ Object.keys(kvJsonObj[namespace]).forEach((key) => {
17
+ childMap.set(key, JSON.stringify(kvJsonObj[namespace][key]));
18
+ });
19
+ EdgeKV.store.set(namespace, childMap);
15
20
  });
21
+ console.log(EdgeKV.store);
16
22
  }
17
23
  catch (err) {
18
24
  console.log(t('kv_parse_failed').d('kv.json parse failed, use empty local kv store.'));
@@ -103,7 +103,7 @@ class EdgeKV {
103
103
  headers: { 'Content-Type': 'application/json' }
104
104
  }
105
105
  );
106
- // 判断是否存在key
106
+ // Check if key exists
107
107
  let isGetFailed = false;
108
108
  fetchRes.headers.forEach((v, k) => {
109
109
  if (k === 'kv-get-empty') {
@@ -187,7 +187,7 @@ class Ew2Server {
187
187
  agent: new HttpProxyAgent(`http://127.0.0.1:${ew2Port}`)
188
188
  });
189
189
  const workerHeaders = Object.fromEntries(workerRes.headers.entries());
190
- // 解决 gzip 兼容性问题,防止net::ERR_CONTENT_DECODING_FAILED
190
+ // Solve gzip compatibility issue, prevent net::ERR_CONTENT_DECODING_FAILED
191
191
  workerHeaders['content-encoding'] = 'identity';
192
192
  if (workerRes.body) {
193
193
  res.writeHead(workerRes.status, workerHeaders);
@@ -236,7 +236,6 @@ class Ew2Server {
236
236
  const key = url.searchParams.get('key');
237
237
  const namespace = url.searchParams.get('namespace');
238
238
  const body = yield this.parseKVBody(req);
239
- console.log(body);
240
239
  if (!key || !namespace) {
241
240
  return {
242
241
  success: false
@@ -187,7 +187,7 @@ const dev = {
187
187
  yield worker.start();
188
188
  }
189
189
  catch (err) {
190
- console.log('Track err', err);
190
+ logger.error(`Worker start failed: ${err}`);
191
191
  process.exit(1);
192
192
  }
193
193
  const ignored = (path) => {
@@ -40,12 +40,12 @@ const generateEntry = (id, projectEntry, userRoot) => __awaiter(void 0, void 0,
40
40
  const prepare = (configPath, entry, port, localUpstream, userRoot) => __awaiter(void 0, void 0, void 0, function* () {
41
41
  const options = {};
42
42
  const currentOptions = { entry, port, localUpstream };
43
- // 支持同时跑多个 deno
43
+ // Support running multiple deno instances simultaneously
44
44
  const id = new Date().getTime().toString();
45
45
  // @ts-ignore
46
46
  global.id = id;
47
47
  yield generateEntry(id, entry, userRoot);
48
- // 给每一次 dev 的配置项,在一个文件中通过 id 区分
48
+ // Configuration items for each dev session, distinguished by id in one file
49
49
  if (fs.existsSync(configPath)) {
50
50
  const currentConfig = fs
51
51
  .readFileSync(configPath, 'utf-8')
@@ -101,7 +101,7 @@ const devPack = () => __awaiter(void 0, void 0, void 0, function* () {
101
101
  }
102
102
  else {
103
103
  logger.notInProject();
104
- process.exit(0);
104
+ process.exit(1);
105
105
  }
106
106
  return prepare(path.resolve(userRoot, '.dev/devConfig.js'), projectEntry, port, localUpstream, userRoot)
107
107
  .then(() => {
@@ -100,7 +100,7 @@ export function checkAndUpdatePackage(packageName) {
100
100
  const spinner = logger.ora;
101
101
  spinner.text = t('checking_template_update').d('Checking esa-template updates...');
102
102
  spinner.start();
103
- // 获取当前安装的版本
103
+ // Get currently installed version
104
104
  const __dirname = getDirName(import.meta.url);
105
105
  const packageJsonPath = path.join(__dirname, '../../../');
106
106
  let versionInfo;
@@ -124,7 +124,7 @@ export function checkAndUpdatePackage(packageName) {
124
124
  }
125
125
  const match = versionInfo.match(new RegExp(`(${packageName})@([0-9.]+)`));
126
126
  const currentVersion = match ? match[2] : '';
127
- // 获取最新版本
127
+ // Get latest version
128
128
  const latestVersion = execSync(`npm view ${packageName} version`, {
129
129
  cwd: packageJsonPath
130
130
  })
@@ -173,11 +173,10 @@ export function checkAndUpdatePackage(packageName) {
173
173
  });
174
174
  }
175
175
  export const getFrameworkConfig = (framework) => {
176
- // 从init目录读取template.jsonc
176
+ // Read template.jsonc from init directory
177
177
  const templatePath = path.join(getDirName(import.meta.url), 'template.jsonc');
178
178
  const jsonc = fs.readFileSync(templatePath, 'utf-8');
179
179
  const json = JSON.parse(jsonc);
180
- console.log(json);
181
180
  return json[framework];
182
181
  };
183
182
  /**
@@ -185,7 +184,7 @@ export const getFrameworkConfig = (framework) => {
185
184
  * @returns 框架全部配置
186
185
  */
187
186
  export const getAllFrameworkConfig = () => {
188
- // 从init目录读取template.jsonc
187
+ // Read template.jsonc from init directory
189
188
  const templatePath = path.join(getDirName(import.meta.url), 'template.jsonc');
190
189
  const jsonc = fs.readFileSync(templatePath, 'utf-8');
191
190
  const json = JSON.parse(jsonc);
@@ -232,7 +231,7 @@ export function getInitParamsFromArgv(argv) {
232
231
  params.deploy = Boolean(a.deploy);
233
232
  return params;
234
233
  }
235
- // 配置项目名称
234
+ // Configure project name
236
235
  export const configProjectName = (initParams) => __awaiter(void 0, void 0, void 0, function* () {
237
236
  if (initParams.name) {
238
237
  log.step(`Project name configured ${initParams.name}`);
@@ -593,7 +592,7 @@ export const initGit = (initParams) => __awaiter(void 0, void 0, void 0, functio
593
592
  export function getGitVersion() {
594
593
  return __awaiter(this, void 0, void 0, function* () {
595
594
  try {
596
- const stdout = yield execCommand(['git', '--version'], {
595
+ let stdout = yield execCommand(['git', '--version'], {
597
596
  useSpinner: false,
598
597
  silent: true,
599
598
  captureOutput: true
@@ -609,7 +608,7 @@ export function getGitVersion() {
609
608
  }
610
609
  export function isGitInstalled() {
611
610
  return __awaiter(this, void 0, void 0, function* () {
612
- return (yield getGitVersion()) !== null;
611
+ return (yield getGitVersion()) !== '' && (yield getGitVersion()) !== null;
613
612
  });
614
613
  }
615
614
  /**
@@ -685,7 +684,6 @@ function ensureGitignore(projectRoot, assetsDirectory) {
685
684
  ? `${existingContent.replace(/\n$/, '')}\n${toAppend.join('\n')}\n`
686
685
  : `${toAppend.join('\n')}\n`;
687
686
  fs.writeFileSync(gitignorePath, newContent, 'utf-8');
688
- logger.log('Updated .gitignore');
689
687
  }
690
688
  catch (_a) {
691
689
  // Do not fail init due to .gitignore issues
@@ -55,7 +55,7 @@
55
55
  "astro": {
56
56
  "label": "Astro",
57
57
  "useGit": false,
58
- "command": "npm create-astro@4.13.1",
58
+ "command": "npm create astro@4.13.1",
59
59
  "params": "--no-install",
60
60
  "assets": {
61
61
  "directory": "./dist"
@@ -14,6 +14,7 @@ import logger from '../../libs/logger.js';
14
14
  import { validRoutine } from '../../utils/checkIsRoutineCreated.js';
15
15
  import { getProjectConfig } from '../../utils/fileUtils/index.js';
16
16
  import { checkDirectory, checkIsLoginSuccess } from '../utils.js';
17
+ import { routeBuilder } from '../../components/routeBuilder.js';
17
18
  import { transferRouteToRuleString } from './helper.js';
18
19
  const addRoute = {
19
20
  command: 'add [route] [site]',
@@ -78,7 +79,6 @@ export function handlerAddRoute(argv) {
78
79
  name: i.SiteName,
79
80
  value: i.SiteId
80
81
  }));
81
- // 获取路由名称,支持直接通过参数传入
82
82
  let routeName = argv.alias;
83
83
  if (!routeName) {
84
84
  const response = yield inquirer.prompt([
@@ -99,35 +99,18 @@ export function handlerAddRoute(argv) {
99
99
  let siteName = argv.site;
100
100
  let siteId;
101
101
  if (!siteName) {
102
- if (argv._.length > 2) {
103
- siteName = argv._[2];
104
- }
105
- // 如果仍未提供站点名称,则提示选择
106
- if (!siteName) {
107
- const response = yield inquirer.prompt([
108
- {
109
- type: 'list',
110
- name: 'routeSite',
111
- message: t('create_route_site').d('Select a site that is active in your account:'),
112
- choices: siteList
113
- }
114
- ]);
115
- siteId = response.routeSite;
116
- }
117
- else {
118
- // 根据站点名称查找对应的站点ID
119
- const matchedSite = siteList.find((site) => site.name === siteName);
120
- if (matchedSite) {
121
- siteId = matchedSite.value;
122
- }
123
- else {
124
- logger.error(t('site_not_found').d(`Site "${siteName}" not found in your account`));
125
- return;
102
+ const response = yield inquirer.prompt([
103
+ {
104
+ type: 'list',
105
+ name: 'routeSite',
106
+ message: t('create_route_site').d('Select a site that is active in your account:'),
107
+ choices: siteList
126
108
  }
127
- }
109
+ ]);
110
+ siteId = response.routeSite;
128
111
  }
129
112
  else {
130
- // 根据站点名称查找对应的站点ID
113
+ // Find corresponding site ID by site name
131
114
  const matchedSite = siteList.find((site) => site.name === siteName);
132
115
  if (matchedSite) {
133
116
  siteId = matchedSite.value;
@@ -137,40 +120,24 @@ export function handlerAddRoute(argv) {
137
120
  return;
138
121
  }
139
122
  }
140
- // 获取路由值,支持直接通过参数传入
141
123
  let inputRoute = argv.route;
142
124
  if (!inputRoute) {
143
- // 如果参数中提供了路由值,使用它
144
- if (argv._.length > 1) {
145
- inputRoute = argv._[1];
146
- }
147
- // 如果仍未提供路由值,则提示输入
148
- if (!inputRoute) {
149
- const response = yield inquirer.prompt([
150
- {
151
- type: 'input',
152
- name: 'inputRoute',
153
- message: t('create_route_route').d('Enter a Route (e.g., example.com/*):'),
154
- validate: (input) => {
155
- if (!input) {
156
- return t('route_input_required').d('Route is required');
157
- }
158
- if (!input.includes('*') && !input.includes('/')) {
159
- return t('route_format_invalid').d('Route format is invalid. Please include wildcard (*) or path (/)');
160
- }
161
- return true;
162
- }
163
- }
164
- ]);
165
- inputRoute = response.inputRoute;
125
+ // Get selected site name for route building
126
+ const selectedSite = siteList.find((site) => site.value === siteId);
127
+ const displaySiteName = selectedSite ? selectedSite.name : siteName;
128
+ // Use route builder
129
+ const builtRoute = yield routeBuilder(displaySiteName);
130
+ if (!builtRoute) {
131
+ logger.info(t('route_build_cancelled').d('Route building cancelled'));
132
+ return;
166
133
  }
134
+ inputRoute = builtRoute;
167
135
  }
168
136
  const rule = transferRouteToRuleString(inputRoute);
169
137
  if (!rule) {
170
138
  logger.error(t('route_format_invalid').d('Invalid route format'));
171
139
  return;
172
140
  }
173
- // 获取站点名称用于显示
174
141
  const selectedSite = siteList.find((site) => site.value === siteId);
175
142
  const displaySiteName = selectedSite ? selectedSite.name : siteName;
176
143
  const req = {
@@ -72,8 +72,7 @@ export const transferRouteToRuleString = (routePath) => {
72
72
  export const transferRuleStringToRoute = (ruleStr) => {
73
73
  if (!ruleStr) {
74
74
  return '';
75
- }
76
- // 去掉外层括号并按 " and " 分割
75
+ } // Remove outer brackets and split by " and "
77
76
  const cleanedRule = ruleStr.replace(/^\(|\)$/g, '');
78
77
  const parts = cleanedRule.split(' and ');
79
78
  if (parts.length !== 2) {
@@ -81,36 +80,36 @@ export const transferRuleStringToRoute = (ruleStr) => {
81
80
  }
82
81
  let host = '';
83
82
  let uriPath = '';
84
- // 处理host部分
83
+ // Process host part
85
84
  const hostPart = parts[0].trim();
86
85
  if (hostPart.startsWith(`${RuleMatchOperatorEndsWith}(${RuleMatchTypeHost},`)) {
87
- // host匹配eq时的逻辑
86
+ // Logic when host matches eq
88
87
  // ends_with(http.host, "value")
89
88
  const match = hostPart.match(/ends_with\(http\.host,\s*"([^"]+)"\)/);
90
89
  if (match) {
91
- host = `*${match[1]}`; // 加前缀 *
90
+ host = `*${match[1]}`; // Add prefix *
92
91
  }
93
92
  }
94
93
  else if (hostPart.startsWith(`${RuleMatchTypeHost} ${RuleMatchOperatorEq}`)) {
95
- // host匹配eq时的逻辑
94
+ // Logic when host matches eq
96
95
  // http.host eq "value"
97
96
  const match = hostPart.match(/http\.host eq "([^"]+)"/);
98
97
  if (match) {
99
98
  host = match[1];
100
99
  }
101
100
  }
102
- // 处理uriPath 部分
101
+ // Process uriPath part
103
102
  const uriPathPart = parts[1].trim();
104
103
  if (uriPathPart.startsWith(`${RuleMatchOperatorStartsWith}(${RuleMatchTypeUriPath},`)) {
105
- // uriPath匹配startsWith时的逻辑
104
+ // Logic when uriPath matches startsWith
106
105
  // starts_with(http.request.uri.path, "value")
107
106
  const match = uriPathPart.match(/starts_with\(http\.request\.uri\.path,\s*"([^"]+)"\)/);
108
107
  if (match) {
109
- uriPath = `${match[1]}*`; // 加后缀 *
108
+ uriPath = `${match[1]}*`; // Add suffix *
110
109
  }
111
110
  }
112
111
  else if (uriPathPart.startsWith(`${RuleMatchTypeUriPath} ${RuleMatchOperatorEq}`)) {
113
- // uriPath匹配eq时的逻辑
112
+ // Logic when uriPath matches eq
114
113
  // http.request.uri.path eq "value"
115
114
  const match = uriPathPart.match(/http\.request\.uri\.path eq "([^"]+)"/);
116
115
  if (match) {
@@ -18,37 +18,55 @@ const list = {
18
18
  command: 'list',
19
19
  describe: `📋 ${t('list_describe').d('List all your routines')}`,
20
20
  builder: (yargs) => {
21
- return yargs.usage(`${t('common_usage').d('Usage')}: \$0 list []`);
21
+ return yargs
22
+ .option('keyword', {
23
+ alias: 'k',
24
+ describe: t('deploy_option_keyword').d('Keyword to search for routines'),
25
+ type: 'string'
26
+ })
27
+ .usage(`${t('common_usage').d('Usage')}: \$0 list [--keyword <keyword>]`);
22
28
  },
23
29
  handler: (argv) => __awaiter(void 0, void 0, void 0, function* () {
24
30
  handleList(argv);
25
31
  })
26
32
  };
27
33
  export default list;
34
+ export function getAllRoutines(options) {
35
+ return __awaiter(this, void 0, void 0, function* () {
36
+ var _a;
37
+ const server = yield ApiService.getInstance();
38
+ const allRoutines = [];
39
+ let pageNumber = 1;
40
+ const pageSize = (options === null || options === void 0 ? void 0 : options.PageSize) || 50;
41
+ while (true) {
42
+ const res = yield server.listUserRoutines({
43
+ RegionId: options === null || options === void 0 ? void 0 : options.RegionId,
44
+ PageNumber: pageNumber,
45
+ PageSize: pageSize,
46
+ SearchKeyWord: options === null || options === void 0 ? void 0 : options.SearchKeyWord
47
+ });
48
+ if (!((_a = res === null || res === void 0 ? void 0 : res.body) === null || _a === void 0 ? void 0 : _a.Routines)) {
49
+ break;
50
+ }
51
+ allRoutines.push(...res.body.Routines);
52
+ const totalCount = res.body.TotalCount;
53
+ const currentCount = allRoutines.length;
54
+ if (currentCount >= totalCount) {
55
+ break;
56
+ }
57
+ pageNumber++;
58
+ }
59
+ return allRoutines;
60
+ });
61
+ }
28
62
  export function handleList(argv) {
29
63
  return __awaiter(this, void 0, void 0, function* () {
30
- var _a, _b;
31
- const { site } = argv;
32
64
  const isSuccess = yield checkIsLoginSuccess();
33
65
  if (!isSuccess)
34
66
  return;
35
- const server = yield ApiService.getInstance();
36
- if (site) {
37
- const req = {
38
- SiteSearchType: 'fuzzy',
39
- Status: 'active',
40
- PageNumber: 1,
41
- PageSize: 50
42
- };
43
- const res = yield server.listSites(req);
44
- const siteList = (_a = res === null || res === void 0 ? void 0 : res.data.Sites) !== null && _a !== void 0 ? _a : [];
45
- const siteNameList = siteList === null || siteList === void 0 ? void 0 : siteList.map((item) => item.SiteName);
46
- logger.log(chalk.bold.bgGray(`📃 ${t('list_site_name_title').d('List all of site names')}:`));
47
- logger.tree(siteNameList);
48
- return;
49
- }
50
- const res = yield server.listUserRoutines();
51
- const routineList = (_b = res === null || res === void 0 ? void 0 : res.body) === null || _b === void 0 ? void 0 : _b.Routines;
67
+ const routineList = yield getAllRoutines({
68
+ SearchKeyWord: argv.keyword
69
+ });
52
70
  if (routineList) {
53
71
  logger.log(chalk.bold.bgGray(`📃 ${t('list_routine_name_title').d('List all of routine')}:`));
54
72
  displayRoutineList(routineList);
@@ -71,7 +71,7 @@ export const bindRoutineWithDomain = (name, domain) => __awaiter(void 0, void 0,
71
71
  export function validName(name) {
72
72
  return /^[a-zA-Z0-9-_]+$/.test(name);
73
73
  }
74
- // 校验域名是否有效
74
+ // Validate if domain is valid
75
75
  export function validDomain(domain) {
76
76
  return /^(?:[a-z0-9-](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/.test(domain);
77
77
  }
@@ -131,12 +131,12 @@ export function validateLoginCredentials(accessKeyId_1, accessKeySecret_1, endpo
131
131
  });
132
132
  }
133
133
  export function isValidRouteForDomain(route, domain) {
134
- // 构建一个允许子域和任意路径的正则表达式
135
- // 例如,匹配形式如 *.example.com/* 的URL
134
+ // Build a regex that allows subdomains and arbitrary paths
135
+ // For example, match URLs like *.example.com/*
136
136
  return route.includes(domain);
137
137
  }
138
138
  export function escapeRegExp(string) {
139
- return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& 表示整个被匹配的字符串
139
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& represents the entire matched string
140
140
  }
141
141
  export const getAllSites = () => __awaiter(void 0, void 0, void 0, function* () {
142
142
  var _a;
@@ -51,7 +51,7 @@ export const FilterSelector = ({ data, onSubmit, hideCount = 20 }) => {
51
51
  else if (tabPressCount === 1) {
52
52
  const filteredDataInner = data.filter((site) => site.label.includes(inputValue));
53
53
  setFilteredData(filteredDataInner);
54
- // 匹配结果大于等于1个时,进入选择模式
54
+ // Enter selection mode when match results >= 1
55
55
  if ((filteredDataInner.length >= 1 &&
56
56
  showAll &&
57
57
  filteredDataInner.length > hideCount) ||
@@ -0,0 +1,68 @@
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 } from 'ink';
11
+ import TextInput from 'ink-text-input';
12
+ import React, { useState } from 'react';
13
+ 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')
35
+ .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);
40
+ 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
+ } }));
67
+ });
68
+ });
@@ -204,8 +204,8 @@
204
204
  "zh_CN": "读取 CLI 配置错误"
205
205
  },
206
206
  "dev_error_no_project_config": {
207
- "en": "Error reading project configuration esa.toml file.",
208
- "zh_CN": "读取项目配置 esa.toml 文件错误"
207
+ "en": "Error reading project configuration esa.jsonc (recommended) or esa.toml file.",
208
+ "zh_CN": "读取项目配置 esa.jsonc (推荐) 或 esa.toml 文件错误"
209
209
  },
210
210
  "dev_build_start": {
211
211
  "en": "Starting build process",
@@ -1271,8 +1271,44 @@
1271
1271
  "en": "Global update failed. You may need elevated permissions (sudo) or use yarn/pnpm:",
1272
1272
  "zh_CN": "全局更新失败。你可能需要更高权限(sudo)或使用 yarn/pnpm:"
1273
1273
  },
1274
+ "deploy_option_keyword": {
1275
+ "en": "Keyword to search for routines",
1276
+ "zh_CN": ""
1277
+ },
1278
+ "route_input_method": {
1279
+ "en": "How would you like to input the route?",
1280
+ "zh_CN": "您希望如何输入路由?"
1281
+ },
1282
+ "route_input_builder": {
1283
+ "en": "Use route builder (prefix + suffix)",
1284
+ "zh_CN": "使用路由构建器(前缀 + 后缀)"
1285
+ },
1286
+ "route_input_manual": {
1287
+ "en": "Enter route manually",
1288
+ "zh_CN": "手动输入路由"
1289
+ },
1290
+ "route_build_cancelled": {
1291
+ "en": "Route building cancelled",
1292
+ "zh_CN": "路由构建已取消"
1293
+ },
1294
+ "route_builder_prefix_prompt": {
1295
+ "en": "Enter route prefix for ${siteName} (e.g., api, v1):",
1296
+ "zh_CN": "为 ${siteName} 输入路由前缀(例如:api, v1):"
1297
+ },
1298
+ "route_builder_suffix_prompt": {
1299
+ "en": "Enter route suffix for ${siteName} (e.g., *, users/*):",
1300
+ "zh_CN": "为 ${siteName} 输入路由后缀(例如:*, users/*):"
1301
+ },
1302
+ "route_builder_preview": {
1303
+ "en": "Preview: ${siteName}${prefix}${suffix}",
1304
+ "zh_CN": "预览:{route}"
1305
+ },
1306
+ "route_builder_instructions": {
1307
+ "en": "Press Enter to continue, Ctrl+C to cancel",
1308
+ "zh_CN": "按回车键继续,Ctrl+C 取消"
1309
+ },
1274
1310
  "kv_parse_failed": {
1275
1311
  "en": "kv.json parse failed, use empty local kv store.",
1276
- "zh_CN": ""
1312
+ "zh_CN": "kv.json 解析失败,使用空本地 kv 存储。"
1277
1313
  }
1278
1314
  }
@@ -90,7 +90,6 @@ export class ApiService {
90
90
  }
91
91
  }
92
92
  catch (error) {
93
- console.log('error', error);
94
93
  return {
95
94
  success: false,
96
95
  message: t('login_failed').d('An error occurred while trying to log in.')
@@ -101,15 +100,15 @@ export class ApiService {
101
100
  quickDeployRoutine(edgeRoutine) {
102
101
  return __awaiter(this, void 0, void 0, function* () {
103
102
  try {
104
- // 上传代码到unstable版本
103
+ // Upload code to unstable version
105
104
  const stagingRes = yield this.getRoutineStagingCodeUploadInfo(edgeRoutine);
106
105
  if (stagingRes) {
107
- // 生产版本
106
+ // Production version
108
107
  const commitRes = yield this.commitRoutineStagingCode({
109
108
  Name: edgeRoutine.name,
110
109
  CodeDescription: edgeRoutine.description
111
110
  });
112
- // 发布到生产环境
111
+ // Deploy to production environment
113
112
  if (commitRes) {
114
113
  const deployRes = yield this.publishRoutineCodeVersion({
115
114
  Name: edgeRoutine.name,
@@ -219,7 +218,7 @@ export class ApiService {
219
218
  return null;
220
219
  });
221
220
  }
222
- listUserRoutines() {
221
+ listUserRoutines(requestParams) {
223
222
  return __awaiter(this, void 0, void 0, function* () {
224
223
  try {
225
224
  let params = {
@@ -236,7 +235,9 @@ export class ApiService {
236
235
  return this;
237
236
  }
238
237
  };
239
- let request = new $OpenApi.OpenApiRequest();
238
+ let request = new $OpenApi.OpenApiRequest({
239
+ query: requestParams || {}
240
+ });
240
241
  let runtime = {
241
242
  toMap: function () {
242
243
  return this;
@@ -180,7 +180,7 @@ class Logger {
180
180
  this.block();
181
181
  this.log('If there is code to deploy, you can either:');
182
182
  this.subLog(`- Specify an entry-point to your Routine via the command line (ex: ${chalk.green('esa deploy src/index.ts')})`);
183
- this.subLog('- Or add the following to your "esa.jsonc" file:');
183
+ this.subLog('- Or create an "esa.jsonc" file (recommended):');
184
184
  console.log('```jsonc\n' +
185
185
  '{\n' +
186
186
  ' "name": "my-routine",\n' +
@@ -188,7 +188,7 @@ class Logger {
188
188
  ' "dev": { "port": 18080 }\n' +
189
189
  '}\n' +
190
190
  '```');
191
- this.subLog('- Or, if you prefer TOML, add to your "esa.toml":');
191
+ this.subLog('- Or, if you prefer TOML, create an "esa.toml" file:');
192
192
  console.log('```toml\n' +
193
193
  'name = "my-routine"\n' +
194
194
  'entry = "src/index.ts"\n' +
@@ -197,7 +197,7 @@ class Logger {
197
197
  'port = 18080\n' +
198
198
  '```\n');
199
199
  this.log('If you are deploying a directory of static assets, you can either:');
200
- this.subLog(`- Add the assets directory to your "esa.jsonc" and run ${chalk.green('esa deploy -a ./dist')}`);
200
+ this.subLog(`- Create an "esa.jsonc" file (recommended) and run ${chalk.green('esa deploy -a ./dist')}`);
201
201
  console.log('```jsonc\n' +
202
202
  '{\n' +
203
203
  ' "name": "my-routine",\n' +
@@ -206,7 +206,7 @@ class Logger {
206
206
  ' }\n' +
207
207
  '}\n' +
208
208
  '```');
209
- this.subLog(`- Or add to your "esa.toml" and run ${chalk.green('esa deploy -a ./dist')}`);
209
+ this.subLog(`- Or create an "esa.toml" file and run ${chalk.green('esa deploy -a ./dist')}`);
210
210
  console.log('```toml\n' +
211
211
  'name = "my-routine"\n' +
212
212
  '\n' +
@@ -26,7 +26,7 @@ export function validRoutine(name) {
26
26
  const isCreatedRoutine = yield isRoutineExist(name);
27
27
  if (!isCreatedRoutine) {
28
28
  logger.warn(`${t('routine_not_exist').d('Routine does not exist, please create a new one. Run command:')} ${chalk.greenBright('esa deploy')}`);
29
- exit(0);
29
+ exit(1);
30
30
  }
31
31
  });
32
32
  }
@@ -43,10 +43,10 @@ const compress = (scriptEntry_1, assetsDir_1, ...args_1) => __awaiter(void 0, [s
43
43
  let assetsDirectory = assetsDir || ((_a = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.assets) === null || _a === void 0 ? void 0 : _a.directory);
44
44
  const routineType = checkEdgeRoutineType(scriptEntry, assetsDir, projectPath);
45
45
  if (!projectConfig && !scriptEntry && !assetsDir) {
46
- logger.error('esa.jsonc or esa.toml is not found and script entry or assets directory is not provided by command line');
47
- exit(0);
46
+ logger.error('esa.jsonc (recommended) or esa.toml is not found and script entry or assets directory is not provided by command line');
47
+ exit(1);
48
48
  }
49
- // 参数优先:如果有参数则使用参数,否则使用配置文件中的值
49
+ // Parameter priority: use parameters if available, otherwise use values from config file
50
50
  const entry = scriptEntry || (projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.entry);
51
51
  if (routineType === EDGE_ROUTINE_TYPE.NOT_EXIST) {
52
52
  const errorMessage = [
@@ -61,7 +61,7 @@ const compress = (scriptEntry_1, assetsDir_1, ...args_1) => __awaiter(void 0, [s
61
61
  chalk.cyan('🔍 Possible issue causes:'),
62
62
  chalk.white(' 1. Entry file path is incorrect or file does not exist'),
63
63
  chalk.white(' 2. Assets directory path is incorrect or directory does not exist'),
64
- chalk.white(` 3. Project configuration file ${chalk.yellow('esa.jsonc')} or ${chalk.yellow('esa.toml')} format error`),
64
+ chalk.white(` 3. Project configuration file ${chalk.yellow('esa.jsonc')} (recommended) or ${chalk.yellow('esa.toml')} format error`),
65
65
  chalk.white(` 4. Relative path format error, please use ${chalk.yellow('./xxx')} format`),
66
66
  '',
67
67
  chalk.yellow.bold(`📍 Please check if the following ${chalk.red('absolute paths')} are correct:`),
@@ -83,7 +83,7 @@ const compress = (scriptEntry_1, assetsDir_1, ...args_1) => __awaiter(void 0, [s
83
83
  ''
84
84
  ].join('\n');
85
85
  logger.error(errorMessage);
86
- exit(0);
86
+ exit(1);
87
87
  }
88
88
  if (routineType === EDGE_ROUTINE_TYPE.JS_ONLY ||
89
89
  routineType === EDGE_ROUTINE_TYPE.JS_AND_ASSETS) {
@@ -92,8 +92,8 @@ function isBinDirInPath(binDir) {
92
92
  */
93
93
  function addBinDirToPath(binDir) {
94
94
  return __awaiter(this, void 0, void 0, function* () {
95
- // 使用 setx 添加到 PATH
96
- // setx PATH 的长度有2047字符的限制
95
+ // Use setx to add to PATH
96
+ // setx has a 2047 character limit for PATH
97
97
  const command = `setx Path "%Path%;${binDir}"`;
98
98
  try {
99
99
  yield execAsync(command);
@@ -17,8 +17,31 @@ import { getDirName, getRoot } from './base.js';
17
17
  const projectConfigFile = 'esa.toml';
18
18
  const __dirname = getDirName(import.meta.url);
19
19
  const root = getRoot();
20
- export const projectConfigPath = path.join(root, projectConfigFile);
20
+ // Function to get the actual project config file path (supports both .jsonc and .toml)
21
+ export const getProjectConfigPath = (filePath = root) => {
22
+ const configFormats = ['esa.jsonc', 'esa.toml'];
23
+ for (const format of configFormats) {
24
+ const configPath = path.join(filePath, format);
25
+ if (fs.existsSync(configPath)) {
26
+ return configPath;
27
+ }
28
+ }
29
+ // Default to .jsonc if no config file exists
30
+ return path.join(filePath, 'esa.jsonc');
31
+ };
32
+ export const projectConfigPath = getProjectConfigPath();
21
33
  export const cliConfigPath = path.join(os.homedir(), '.esa/config/default.toml');
34
+ // Function to get the actual config file path (supports both .toml and .jsonc)
35
+ export const getCliConfigPath = () => {
36
+ const configDir = path.join(os.homedir(), '.esa/config');
37
+ const jsoncPath = path.join(configDir, 'default.jsonc');
38
+ const tomlPath = path.join(configDir, 'default.toml');
39
+ // Check if JSONC file exists first, then fallback to TOML
40
+ if (fs.existsSync(jsoncPath)) {
41
+ return jsoncPath;
42
+ }
43
+ return tomlPath;
44
+ };
22
45
  export const hiddenConfigDir = path.join(os.homedir(), '.esa/config');
23
46
  export const generateHiddenConfigDir = () => {
24
47
  if (!fs.existsSync(hiddenConfigDir)) {
@@ -28,7 +51,7 @@ export const generateHiddenConfigDir = () => {
28
51
  export const generateToml = (path) => {
29
52
  if (!fs.existsSync(path)) {
30
53
  fs.writeFileSync(path, '', 'utf-8');
31
- // 添加默认的endpoint
54
+ // Add default endpoint
32
55
  const defaultConfig = {
33
56
  endpoint: 'esa.cn-hangzhou.aliyuncs.com'
34
57
  };
@@ -37,20 +60,34 @@ export const generateToml = (path) => {
37
60
  };
38
61
  export const generateDefaultConfig = () => {
39
62
  generateHiddenConfigDir();
40
- generateToml(cliConfigPath);
63
+ const configPath = getCliConfigPath();
64
+ generateToml(configPath);
41
65
  };
42
66
  export function updateProjectConfigFile(configUpdate_1) {
43
67
  return __awaiter(this, arguments, void 0, function* (configUpdate, filePath = root) {
44
- const configPath = path.join(filePath, projectConfigFile);
68
+ const configPath = getProjectConfigPath(filePath);
45
69
  try {
46
70
  let configFileContent = yield fsPromises.readFile(configPath, 'utf8');
47
- let config = toml.parse(configFileContent);
48
- config = Object.assign(Object.assign({}, config), configUpdate);
49
- const updatedConfigString = toml.stringify(config);
71
+ let config;
72
+ let updatedConfigString;
73
+ // Detect file format based on file extension
74
+ if (configPath.endsWith('.jsonc') || configPath.endsWith('.json')) {
75
+ // Handle JSONC format
76
+ const jsonContent = configFileContent.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '');
77
+ config = JSON.parse(jsonContent);
78
+ config = Object.assign(Object.assign({}, config), configUpdate);
79
+ updatedConfigString = JSON.stringify(config, null, 2) + '\n';
80
+ }
81
+ else {
82
+ // Handle TOML format (default)
83
+ config = toml.parse(configFileContent);
84
+ config = Object.assign(Object.assign({}, config), configUpdate);
85
+ updatedConfigString = toml.stringify(config);
86
+ }
50
87
  yield fsPromises.writeFile(configPath, updatedConfigString);
51
88
  }
52
89
  catch (error) {
53
- logger.error(`Error updating TOML file: ${error}`);
90
+ logger.error(`Error updating config file: ${error}`);
54
91
  logger.pathEacces(__dirname);
55
92
  }
56
93
  });
@@ -58,16 +95,30 @@ export function updateProjectConfigFile(configUpdate_1) {
58
95
  export function updateCliConfigFile(configUpdate) {
59
96
  return __awaiter(this, void 0, void 0, function* () {
60
97
  try {
61
- let configFileContent = yield fsPromises.readFile(cliConfigPath, 'utf8');
62
- let config = toml.parse(configFileContent);
63
- config = Object.assign(Object.assign({}, config), configUpdate);
64
- const updatedConfigString = toml.stringify(config);
65
- yield fsPromises.writeFile(cliConfigPath, updatedConfigString);
98
+ const configPath = getCliConfigPath();
99
+ let configFileContent = yield fsPromises.readFile(configPath, 'utf8');
100
+ let config;
101
+ let updatedConfigString;
102
+ // Detect file format based on file extension
103
+ if (configPath.endsWith('.jsonc') || configPath.endsWith('.json')) {
104
+ // Handle JSONC format
105
+ const jsonContent = configFileContent.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '');
106
+ config = JSON.parse(jsonContent);
107
+ config = Object.assign(Object.assign({}, config), configUpdate);
108
+ updatedConfigString = JSON.stringify(config, null, 2) + '\n';
109
+ }
110
+ else {
111
+ // Handle TOML format (default)
112
+ config = toml.parse(configFileContent);
113
+ config = Object.assign(Object.assign({}, config), configUpdate);
114
+ updatedConfigString = toml.stringify(config);
115
+ }
116
+ yield fsPromises.writeFile(configPath, updatedConfigString);
66
117
  }
67
118
  catch (error) {
68
- logger.error(`Error updating TOML file: ${error}`);
119
+ logger.error(`Error updating config file: ${error}`);
69
120
  logger.pathEacces(__dirname);
70
- throw new Error('Login error');
121
+ throw new Error('Config update error');
71
122
  }
72
123
  });
73
124
  }
@@ -88,7 +139,6 @@ export function readConfigFile(configPath) {
88
139
  }
89
140
  }
90
141
  catch (error) {
91
- console.log(error);
92
142
  logger.error(`Error parsing config file: ${error}`);
93
143
  return null;
94
144
  }
@@ -96,7 +146,8 @@ export function readConfigFile(configPath) {
96
146
  return null;
97
147
  }
98
148
  export function getCliConfig() {
99
- const res = readConfigFile(cliConfigPath);
149
+ const configPath = getCliConfigPath();
150
+ const res = readConfigFile(configPath);
100
151
  if (!res) {
101
152
  return null;
102
153
  }
@@ -133,7 +184,7 @@ export function getConfigurations() {
133
184
  }
134
185
  }
135
186
  export function generateConfigFile(projectName_1, initConfigs_1, targetDir_1) {
136
- return __awaiter(this, arguments, void 0, function* (projectName, initConfigs, targetDir, configFormat = 'toml', notFoundStrategy) {
187
+ return __awaiter(this, arguments, void 0, function* (projectName, initConfigs, targetDir, configFormat = 'jsonc', notFoundStrategy) {
137
188
  var _a, _b;
138
189
  const outputDir = targetDir !== null && targetDir !== void 0 ? targetDir : process.cwd();
139
190
  const currentDirName = path.basename(outputDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esa-cli",
3
- "version": "0.0.2-beta.21",
3
+ "version": "0.0.2-beta.22",
4
4
  "description": "A CLI for operating Alibaba Cloud ESA EdgeRoutine (Edge Functions).",
5
5
  "main": "bin/enter.cjs",
6
6
  "type": "module",