pan-stack-cli 0.1.0 → 0.1.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/README.md CHANGED
@@ -58,12 +58,21 @@ commands:
58
58
  description: Deploy backend local
59
59
  exec: scripts/deploy.sh
60
60
 
61
+ install:
62
+ alias: i
63
+ description: Cài dependencies (pan i hoặc pan install)
64
+ shell: |
65
+ set -euo pipefail
66
+ pnpm install
67
+
61
68
  lint:
62
69
  shell: |
63
70
  set -euo pipefail
64
71
  pnpm lint
65
72
  ```
66
73
 
74
+ Alias: `alias: i` hoặc `aliases: i, ins` (cách nhau bằng dấu phẩy).
75
+
67
76
  ### Schema `.pan.yaml` (workspace root)
68
77
 
69
78
  Lệnh orchestration — `cwd` tương đối workspace root; dùng `PAN_WORKSPACE_ROOT` trong shell:
package/bin/pan.js CHANGED
@@ -95,8 +95,10 @@ function printAllCommands(sources) {
95
95
  const targetHint = command.hasTargets
96
96
  ? ` [${command.targets.join("|")}]`
97
97
  : "";
98
+ const aliasHint =
99
+ command.aliases.length > 0 ? ` (${command.aliases.join(", ")})` : "";
98
100
  const desc = command.description ? ` — ${command.description}` : "";
99
- console.log(` ${command.name}${targetHint}${desc}`);
101
+ console.log(` ${command.name}${aliasHint}${targetHint}${desc}`);
100
102
  }
101
103
  }
102
104
  }
@@ -126,12 +128,13 @@ async function main() {
126
128
  return;
127
129
  }
128
130
 
131
+ const allSources = loadAllCommandSources();
129
132
  const matches = findSourcesWithCommand(command);
130
133
  if (matches.length > 1 && !project && process.stdin.isTTY) {
131
134
  console.log(formatProjectList(matches, command));
132
135
  }
133
136
 
134
- await runWithProjectResolution(matches, command, args, project);
137
+ await runWithProjectResolution(matches, command, args, project, allSources);
135
138
  }
136
139
 
137
140
  main().catch((error) => {
package/lib/discover.js CHANGED
@@ -6,6 +6,7 @@ const {
6
6
  loadManifest,
7
7
  loadPanConfig,
8
8
  getCommandDefinition,
9
+ commandAliases,
9
10
  validateRepo,
10
11
  cleanValue,
11
12
  } = require("./manifest");
@@ -166,9 +167,18 @@ function sourceMatchesProject(source, projectFlag) {
166
167
  );
167
168
  }
168
169
 
170
+ function sourceHasCommand(source, commandName) {
171
+ if (source.commands[commandName]) {
172
+ return true;
173
+ }
174
+ return Object.values(source.commands || {}).some((def) =>
175
+ commandAliases(def).includes(commandName)
176
+ );
177
+ }
178
+
169
179
  function findSourcesWithCommand(commandName, startDir = process.cwd()) {
170
- return loadAllCommandSources(startDir).filter(
171
- (source) => source.commands[commandName]
180
+ return loadAllCommandSources(startDir).filter((source) =>
181
+ sourceHasCommand(source, commandName)
172
182
  );
173
183
  }
174
184
 
package/lib/manifest.js CHANGED
@@ -261,6 +261,57 @@ function loadPanConfig(configPath, meta = {}) {
261
261
  };
262
262
  }
263
263
 
264
+ function commandAliases(def) {
265
+ if (!def) {
266
+ return [];
267
+ }
268
+ const result = [];
269
+ if (def.alias) {
270
+ result.push(cleanValue(def.alias));
271
+ }
272
+ if (def.aliases) {
273
+ const raw = def.aliases;
274
+ if (Array.isArray(raw)) {
275
+ result.push(...raw.map((item) => cleanValue(item)));
276
+ } else {
277
+ const value = cleanValue(raw);
278
+ if (value.startsWith("[") && value.endsWith("]")) {
279
+ result.push(
280
+ ...value
281
+ .slice(1, -1)
282
+ .split(",")
283
+ .map((item) => cleanValue(item))
284
+ .filter(Boolean)
285
+ );
286
+ } else if (value.includes(",")) {
287
+ result.push(
288
+ ...value
289
+ .split(",")
290
+ .map((item) => cleanValue(item))
291
+ .filter(Boolean)
292
+ );
293
+ } else if (value) {
294
+ result.push(value);
295
+ }
296
+ }
297
+ }
298
+ return [...new Set(result.filter(Boolean))];
299
+ }
300
+
301
+ function resolveCommandName(sources, commandName) {
302
+ if (sources.some((source) => source.commands[commandName])) {
303
+ return commandName;
304
+ }
305
+ for (const source of sources) {
306
+ for (const [primaryName, def] of Object.entries(source.commands || {})) {
307
+ if (commandAliases(def).includes(commandName)) {
308
+ return primaryName;
309
+ }
310
+ }
311
+ }
312
+ return commandName;
313
+ }
314
+
264
315
  function getCommandDefinition(source, commandName, subArg) {
265
316
  const def = source.commands[commandName];
266
317
  if (!def) {
@@ -287,6 +338,7 @@ function getCommandDefinition(source, commandName, subArg) {
287
338
  function listCommands(source) {
288
339
  return Object.entries(source.commands || {}).map(([name, def]) => ({
289
340
  name,
341
+ aliases: commandAliases(def),
290
342
  description: def.description || "",
291
343
  hasTargets: Boolean(def.targets),
292
344
  targets: def.targets ? Object.keys(def.targets) : [],
@@ -306,6 +358,8 @@ module.exports = {
306
358
  loadPanConfig,
307
359
  manifestRepos,
308
360
  validateRepo,
361
+ commandAliases,
362
+ resolveCommandName,
309
363
  getCommandDefinition,
310
364
  listCommands,
311
365
  commandBaseDir,
@@ -2,7 +2,11 @@ const fs = require("fs");
2
2
  const path = require("path");
3
3
  const { spawn, spawnSync } = require("child_process");
4
4
  const readline = require("readline");
5
- const { getCommandDefinition, commandBaseDir } = require("./manifest");
5
+ const {
6
+ getCommandDefinition,
7
+ commandBaseDir,
8
+ resolveCommandName,
9
+ } = require("./manifest");
6
10
  const { sourceMatchesProject } = require("./discover");
7
11
 
8
12
  function needDir(dir) {
@@ -134,7 +138,15 @@ function promptProjectChoice(matches, commandName) {
134
138
  });
135
139
  }
136
140
 
137
- async function runWithProjectResolution(matches, commandName, args, projectFlag) {
141
+ async function runWithProjectResolution(
142
+ matches,
143
+ commandName,
144
+ args,
145
+ projectFlag,
146
+ allSources = matches
147
+ ) {
148
+ const resolvedName = resolveCommandName(allSources, commandName);
149
+
138
150
  if (matches.length === 0) {
139
151
  throw new Error(
140
152
  `Không tìm thấy lệnh '${commandName}' trong bất kỳ .pan.yaml nào.`
@@ -152,7 +164,7 @@ async function runWithProjectResolution(matches, commandName, args, projectFlag)
152
164
  source = matches[0];
153
165
  } else {
154
166
  const withTargetArg = matches.filter((item) => {
155
- const def = item.commands[commandName];
167
+ const def = item.commands[resolvedName];
156
168
  return def?.targets && args[0] && def.targets[args[0]];
157
169
  });
158
170
  if (withTargetArg.length === 1) {
@@ -169,7 +181,7 @@ async function runWithProjectResolution(matches, commandName, args, projectFlag)
169
181
  }
170
182
  }
171
183
 
172
- runProjectCommand(source, commandName, args);
184
+ runProjectCommand(source, resolvedName, args);
173
185
  }
174
186
 
175
187
  function runGitScript(scriptName, args) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pan-stack-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "CLI điều phối PAN Stack — git đa repo và lệnh dự án từ .project.yaml",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -24,4 +24,4 @@
24
24
  "url": "https://github.com/doiya46/pan-stack.git",
25
25
  "directory": ".pan/pan-stack-cli"
26
26
  }
27
- }
27
+ }