@tsed/cli-core 7.0.0-beta.1 → 7.0.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/lib/esm/CliCore.js +24 -38
  2. package/lib/esm/decorators/command.js +1 -1
  3. package/lib/esm/decorators/index.js +0 -4
  4. package/lib/esm/fn/command.js +31 -3
  5. package/lib/esm/index.js +1 -2
  6. package/lib/esm/interfaces/CommandProvider.js +1 -2
  7. package/lib/esm/interfaces/ProjectPreferences.js +1 -0
  8. package/lib/esm/interfaces/index.js +1 -2
  9. package/lib/esm/packageManagers/PackageManagersModule.js +11 -19
  10. package/lib/esm/packageManagers/index.js +1 -0
  11. package/lib/esm/packageManagers/supports/BaseManager.js +2 -6
  12. package/lib/esm/packageManagers/supports/YarnBerryManager.js +4 -1
  13. package/lib/esm/services/CliFs.js +3 -1
  14. package/lib/esm/services/CliHttpClient.js +3 -2
  15. package/lib/esm/services/CliHttpLogClient.js +1 -1
  16. package/lib/esm/services/CliLoadFile.js +5 -18
  17. package/lib/esm/services/CliPlugins.js +2 -3
  18. package/lib/esm/services/CliService.js +65 -76
  19. package/lib/esm/services/ProjectPackageJson.js +17 -4
  20. package/lib/esm/utils/createInjector.js +1 -1
  21. package/lib/esm/utils/getCommandMetadata.js +34 -7
  22. package/lib/esm/utils/index.js +1 -0
  23. package/lib/esm/utils/loadPlugins.js +5 -18
  24. package/lib/esm/utils/validate.js +23 -0
  25. package/lib/types/CliCore.d.ts +2 -9
  26. package/lib/types/decorators/command.d.ts +2 -2
  27. package/lib/types/decorators/index.d.ts +0 -4
  28. package/lib/types/fn/command.d.ts +58 -3
  29. package/lib/types/index.d.ts +1 -2
  30. package/lib/types/interfaces/CommandData.d.ts +1 -0
  31. package/lib/types/interfaces/CommandMetadata.d.ts +17 -15
  32. package/lib/types/interfaces/{CommandParameters.d.ts → CommandOptions.d.ts} +20 -3
  33. package/lib/types/interfaces/CommandProvider.d.ts +4 -22
  34. package/lib/types/interfaces/ProjectPreferences.d.ts +2 -1
  35. package/lib/types/interfaces/index.d.ts +3 -3
  36. package/lib/types/packageManagers/PackageManagersModule.d.ts +1 -2
  37. package/lib/types/packageManagers/index.d.ts +1 -0
  38. package/lib/types/services/CliHttpLogClient.d.ts +1 -1
  39. package/lib/types/services/CliLoadFile.d.ts +2 -4
  40. package/lib/types/services/CliPlugins.d.ts +0 -1
  41. package/lib/types/services/CliService.d.ts +6 -10
  42. package/lib/types/services/ProjectPackageJson.d.ts +9 -0
  43. package/lib/types/utils/index.d.ts +1 -0
  44. package/lib/types/utils/mapCommanderArgs.d.ts +1 -1
  45. package/lib/types/utils/resolveConfiguration.d.ts +1 -1
  46. package/lib/types/utils/validate.d.ts +14 -0
  47. package/package.json +4 -7
  48. package/readme.md +2 -2
  49. package/lib/esm/decorators/on.js +0 -8
  50. package/lib/esm/decorators/onAdd.js +0 -5
  51. package/lib/esm/decorators/onExec.js +0 -5
  52. package/lib/esm/decorators/onPostInstall.js +0 -5
  53. package/lib/esm/decorators/onPrompt.js +0 -5
  54. package/lib/esm/domains/CommandStoreKeys.js +0 -8
  55. package/lib/types/decorators/on.d.ts +0 -1
  56. package/lib/types/decorators/onAdd.d.ts +0 -1
  57. package/lib/types/decorators/onExec.d.ts +0 -1
  58. package/lib/types/decorators/onPostInstall.d.ts +0 -1
  59. package/lib/types/decorators/onPrompt.d.ts +0 -1
  60. package/lib/types/domains/CommandStoreKeys.d.ts +0 -7
  61. /package/lib/esm/interfaces/{CommandParameters.js → CommandOptions.js} +0 -0
@@ -1,16 +1,14 @@
1
1
  import "@tsed/logger-std";
2
2
  import { join, resolve } from "node:path";
3
- import { Type } from "@tsed/core";
4
- import { inject, injectable, InjectorService } from "@tsed/di";
3
+ import { PromptCancelledError } from "@tsed/cli-prompts";
4
+ import { constant, inject, injector } from "@tsed/di";
5
5
  import { $asyncEmit } from "@tsed/hooks";
6
6
  import chalk from "chalk";
7
7
  import { Command } from "commander";
8
8
  import semver from "semver";
9
9
  import updateNotifier from "update-notifier";
10
10
  import { CliError } from "./domains/CliError.js";
11
- import { CliPackageJson } from "./services/CliPackageJson.js";
12
11
  import { CliService } from "./services/CliService.js";
13
- import { ProjectPackageJson } from "./services/ProjectPackageJson.js";
14
12
  import { createInjector } from "./utils/createInjector.js";
15
13
  import { loadPlugins } from "./utils/loadPlugins.js";
16
14
  import { resolveConfiguration } from "./utils/resolveConfiguration.js";
@@ -18,9 +16,8 @@ function isHelpManual(argv) {
18
16
  return argv.includes("-h") || argv.includes("--help");
19
17
  }
20
18
  export class CliCore {
21
- constructor() {
22
- this.injector = inject(InjectorService);
23
- this.cliService = inject(CliService);
19
+ constructor(settings) {
20
+ createInjector(settings);
24
21
  }
25
22
  static checkPrecondition(settings) {
26
23
  const { pkg } = settings;
@@ -48,41 +45,16 @@ export class CliCore {
48
45
  }
49
46
  return this;
50
47
  }
51
- static async create(settings, module = CliCore) {
52
- settings = resolveConfiguration(settings);
53
- const injector = this.createInjector(settings);
54
- settings.plugins && (await loadPlugins());
55
- await this.loadInjector(injector, module);
56
- await $asyncEmit("$onReady");
57
- return inject(CliCore);
58
- }
59
- static async bootstrap(settings, module = CliCore) {
48
+ static async bootstrap(settings) {
60
49
  if (settings.checkPrecondition) {
61
50
  this.checkPrecondition(settings);
62
51
  }
63
52
  if (settings.updateNotifier) {
64
53
  await this.updateNotifier(settings.pkg);
65
54
  }
66
- const cli = await this.create(settings, module);
67
- return cli.bootstrap();
68
- }
69
- static async loadInjector(injector, module = CliCore) {
70
- await $asyncEmit("$beforeInit");
71
- injector.addProvider(CliCore, {
72
- useClass: module
73
- });
74
- await injector.load();
75
- await injector.invoke(module);
76
- await $asyncEmit("$afterInit");
77
- injector.settings.set("loaded", true);
78
- }
79
- static async updateNotifier(pkg) {
80
- updateNotifier({ pkg, updateCheckInterval: 0 }).notify();
81
- return this;
82
- }
83
- static createInjector(settings) {
55
+ settings = resolveConfiguration(settings);
84
56
  const argv = settings.argv || process.argv;
85
- return createInjector({
57
+ return new CliCore({
86
58
  ...settings,
87
59
  name: settings.name || "tsed",
88
60
  argv,
@@ -92,7 +64,11 @@ export class CliCore {
92
64
  scriptsDir: "scripts",
93
65
  ...(settings.project || {})
94
66
  }
95
- });
67
+ }).bootstrap();
68
+ }
69
+ static async updateNotifier(pkg) {
70
+ updateNotifier({ pkg, updateCheckInterval: 0 }).notify();
71
+ return this;
96
72
  }
97
73
  static getProjectRoot(argv) {
98
74
  if (!isHelpManual(argv)) {
@@ -103,12 +79,22 @@ export class CliCore {
103
79
  }
104
80
  async bootstrap() {
105
81
  try {
106
- await this.cliService.parseArgs(this.injector.settings.get("argv"));
82
+ const cliService = inject(CliService);
83
+ constant("plugins") && (await loadPlugins());
84
+ await $asyncEmit("$beforeInit");
85
+ await injector().load();
86
+ await $asyncEmit("$afterInit");
87
+ injector().settings.set("loaded", true);
88
+ await $asyncEmit("$onReady");
89
+ await cliService.parseArgs(constant("argv"));
107
90
  }
108
91
  catch (er) {
92
+ if (er instanceof PromptCancelledError) {
93
+ console.log(chalk.yellow("Prompt cancelled."));
94
+ return this;
95
+ }
109
96
  throw new CliError({ origin: er, cli: this });
110
97
  }
111
98
  return this;
112
99
  }
113
100
  }
114
- injectable(CliCore).imports([CliPackageJson, ProjectPackageJson, CliService]);
@@ -1,6 +1,6 @@
1
1
  import { command } from "../fn/command.js";
2
2
  export function Command(options) {
3
3
  return (token) => {
4
- command(token, options);
4
+ command({ ...options, token });
5
5
  };
6
6
  }
@@ -1,5 +1 @@
1
1
  export * from "./command.js";
2
- export * from "./onAdd.js";
3
- export * from "./onExec.js";
4
- export * from "./onPostInstall.js";
5
- export * from "./onPrompt.js";
@@ -1,5 +1,33 @@
1
1
  import { injectable } from "@tsed/di";
2
- import { CommandStoreKeys } from "../domains/CommandStoreKeys.js";
3
- export function command(token, options) {
4
- return injectable(token).type("command").set(CommandStoreKeys.COMMAND, options);
2
+ import { JsonSchema } from "@tsed/schema";
3
+ JsonSchema.add("prompt", function prompt(label) {
4
+ this.customKey("x-label", label);
5
+ return this;
6
+ })
7
+ .add("when", function when(fn) {
8
+ this.customKey("x-when", fn);
9
+ return this;
10
+ })
11
+ .add("opt", function opt(v) {
12
+ this.customKey("x-opt", v);
13
+ return this;
14
+ })
15
+ .add("choices", function choices(choices) {
16
+ this.customKey("x-choices", choices);
17
+ return this;
18
+ });
19
+ export function command(options) {
20
+ if (!options.token) {
21
+ return injectable(Symbol.for(`COMMAND_${options.name}`))
22
+ .type("command")
23
+ .set("command", options)
24
+ .factory(() => {
25
+ return {
26
+ ...options,
27
+ $prompt: options.prompt,
28
+ $exec: options.handler
29
+ };
30
+ });
31
+ }
32
+ return injectable(options.token).type("command").set("command", options);
5
33
  }
package/lib/esm/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import "./utils/patchCommander.js";
2
- import Inquirer from "inquirer";
3
2
  export * from "./CliCore.js";
4
3
  export * from "./decorators/index.js";
5
4
  export * from "./fn/command.js";
@@ -7,8 +6,8 @@ export * from "./interfaces/index.js";
7
6
  export * from "./packageManagers/index.js";
8
7
  export * from "./services/index.js";
9
8
  export * from "./utils/index.js";
9
+ export * from "@tsed/cli-prompts";
10
10
  export * from "@tsed/core";
11
11
  export * from "@tsed/di";
12
12
  export * from "@tsed/logger";
13
13
  export * from "@tsed/normalize-path";
14
- export { Inquirer };
@@ -1,2 +1 @@
1
- import "inquirer-autocomplete-prompt";
2
- import AutocompletePrompt from "inquirer-autocomplete-prompt";
1
+ export {};
@@ -4,4 +4,5 @@ export var PackageManager;
4
4
  PackageManager["YARN_BERRY"] = "yarn_berry";
5
5
  PackageManager["NPM"] = "npm";
6
6
  PackageManager["PNPM"] = "pnpm";
7
+ PackageManager["BUN"] = "bun";
7
8
  })(PackageManager || (PackageManager = {}));
@@ -1,8 +1,7 @@
1
- import { Type } from "@tsed/core";
2
1
  export * from "./CliDefaultOptions.js";
3
2
  export * from "./CommandData.js";
4
3
  export * from "./CommandMetadata.js";
5
- export * from "./CommandParameters.js";
4
+ export * from "./CommandOptions.js";
6
5
  export * from "./CommandProvider.js";
7
6
  export * from "./PackageJson.js";
8
7
  export * from "./ProjectPreferences.js";
@@ -1,5 +1,4 @@
1
- import { __decorate, __metadata, __param } from "tslib";
2
- import { Inject, injectable } from "@tsed/di";
1
+ import { Inject, inject, injectable, injectMany } from "@tsed/di";
3
2
  import { EMPTY, throwError } from "rxjs";
4
3
  import { catchError } from "rxjs/operators";
5
4
  import { ProjectPackageJson } from "../services/ProjectPackageJson.js";
@@ -18,17 +17,19 @@ function mapPackagesWithInvalidVersion(deps) {
18
17
  .filter(([, version]) => !isValidVersion(version))
19
18
  .map(toString);
20
19
  }
21
- let PackageManagersModule = class PackageManagersModule {
22
- constructor(packageManagers) {
23
- this.packageManagers = packageManagers;
24
- this.packageManagers = packageManagers.filter((manager) => manager.has());
20
+ export class PackageManagersModule {
21
+ constructor() {
22
+ this.projectPackageJson = inject(ProjectPackageJson);
23
+ this.packageManagers = injectMany("package:manager").filter((manager) => {
24
+ return manager.has();
25
+ });
25
26
  }
26
27
  init(options = {}) {
27
28
  const packageManager = this.get(options.packageManager);
28
29
  options.packageManager = packageManager.name;
29
30
  options = {
30
31
  ...options,
31
- cwd: this.projectPackageJson.dir,
32
+ cwd: this.projectPackageJson.cwd,
32
33
  env: {
33
34
  ...process.env,
34
35
  GH_TOKEN: this.projectPackageJson.GH_TOKEN
@@ -45,7 +46,7 @@ let PackageManagersModule = class PackageManagersModule {
45
46
  const deps = mapPackagesWithInvalidVersion(this.projectPackageJson.dependencies);
46
47
  options = {
47
48
  ...options,
48
- cwd: this.projectPackageJson.dir,
49
+ cwd: this.projectPackageJson.cwd,
49
50
  env: {
50
51
  ...process.env,
51
52
  GH_TOKEN: this.projectPackageJson.GH_TOKEN
@@ -116,14 +117,5 @@ let PackageManagersModule = class PackageManagersModule {
116
117
  });
117
118
  return this.get().runScript(scriptName, options).pipe(errorPipe());
118
119
  }
119
- };
120
- __decorate([
121
- Inject(),
122
- __metadata("design:type", ProjectPackageJson)
123
- ], PackageManagersModule.prototype, "projectPackageJson", void 0);
124
- PackageManagersModule = __decorate([
125
- __param(0, Inject("package:manager")),
126
- __metadata("design:paramtypes", [Array])
127
- ], PackageManagersModule);
128
- export { PackageManagersModule };
129
- injectable(PackageManagersModule).imports([YarnManager, YarnBerryManager, NpmManager, PNpmManager, BunManager]);
120
+ }
121
+ injectable(PackageManagersModule).imports([YarnBerryManager, YarnManager, NpmManager, PNpmManager, BunManager]);
@@ -2,4 +2,5 @@ export * from "./PackageManagersModule.js";
2
2
  export * from "./supports/BaseManager.js";
3
3
  export * from "./supports/NpmManager.js";
4
4
  export * from "./supports/PNpmManager.js";
5
+ export * from "./supports/YarnBerryManager.js";
5
6
  export * from "./supports/YarnManager.js";
@@ -1,10 +1,10 @@
1
- import { __decorate, __metadata } from "tslib";
2
- import { Inject } from "@tsed/di";
1
+ import { inject } from "@tsed/di";
3
2
  import { Observable } from "rxjs";
4
3
  import { CliExeca } from "../../services/CliExeca.js";
5
4
  export class BaseManager {
6
5
  constructor() {
7
6
  this.verboseOpt = "--verbose";
7
+ this.cliExeca = inject(CliExeca);
8
8
  }
9
9
  has() {
10
10
  try {
@@ -23,7 +23,3 @@ export class BaseManager {
23
23
  return this.cliExeca.run(this.cmd, [cmd, options.verbose && this.verboseOpt, ...args].filter(Boolean), options);
24
24
  }
25
25
  }
26
- __decorate([
27
- Inject(CliExeca),
28
- __metadata("design:type", CliExeca)
29
- ], BaseManager.prototype, "cliExeca", void 0);
@@ -29,7 +29,10 @@ let YarnBerryManager = class YarnBerryManager extends BaseManager {
29
29
  nodeLinker: "node-modules"
30
30
  });
31
31
  // then switch to berry
32
- this.cliExeca.runSync(this.cmd, ["set", "version", "berry"]);
32
+ try {
33
+ this.cliExeca.runSync(this.cmd, ["set", "version", "berry"]);
34
+ }
35
+ catch (er) { }
33
36
  }
34
37
  add(deps, options) {
35
38
  return this.run("add", [...deps], options);
@@ -51,7 +51,9 @@ export class CliFs extends RealFileSystemHost {
51
51
  return this.raw.ensureDirSync(path, options);
52
52
  }
53
53
  findUpFile(root, file) {
54
- return [join(root, file), join(root, "..", file), join(root, "..", "..", file), join(root, "..", "..", "..", file)].find((path) => this.fileExistsSync(path));
54
+ return [join(root, file), join(root, "..", file), join(root, "..", "..", file), join(root, "..", "..", "..", file)].find((path) => {
55
+ return this.fileExistsSync(path) || this.raw.existsSync(path);
56
+ });
55
57
  }
56
58
  async importModule(mod, root = process.cwd()) {
57
59
  try {
@@ -41,10 +41,11 @@ export class CliHttpClient extends CliHttpLogClient {
41
41
  return this.mapResponse(result, options);
42
42
  }
43
43
  getRequestParameters(method, endpoint, options) {
44
+ const url = (this.host || "") + endpoint.replace(this.host || "", "");
44
45
  options = {
45
46
  method,
46
- url: (this.host || "") + endpoint.replace(this.host || "", ""),
47
47
  ...options,
48
+ url,
48
49
  params: options.params || options.qs,
49
50
  data: options.data,
50
51
  headers: {
@@ -53,7 +54,7 @@ export class CliHttpClient extends CliHttpLogClient {
53
54
  ...(options.headers || {})
54
55
  }
55
56
  };
56
- this.configureProxy(endpoint, options);
57
+ this.configureProxy(url, options);
57
58
  return options;
58
59
  }
59
60
  configureProxy(endpoint, options) {
@@ -11,7 +11,7 @@ let CliHttpLogClient = class CliHttpLogClient {
11
11
  this.callee = options.callee || "http";
12
12
  }
13
13
  onSuccess(options) {
14
- return this.logger.debug({
14
+ this.logger.debug({
15
15
  ...this.formatLog(options),
16
16
  status: "OK"
17
17
  });
@@ -1,21 +1,12 @@
1
1
  import { extname } from "node:path";
2
2
  import { inject, injectable } from "@tsed/di";
3
- import { default as Ajv } from "ajv";
3
+ import { validate } from "../utils/validate.js";
4
4
  import { CliFs } from "./CliFs.js";
5
5
  import { CliYaml } from "./CliYaml.js";
6
6
  export class CliLoadFile {
7
- // @ts-ignore
8
- #ajv;
9
7
  constructor() {
10
8
  this.cliYaml = inject(CliYaml);
11
9
  this.cliFs = inject(CliFs);
12
- const options = {
13
- verbose: false,
14
- coerceTypes: true,
15
- strict: false
16
- };
17
- // @ts-ignore
18
- this.#ajv = new Ajv(options);
19
10
  }
20
11
  /**
21
12
  * Load a configuration file from yaml, json
@@ -33,18 +24,14 @@ export class CliLoadFile {
33
24
  throw new Error("Unsupported format file");
34
25
  }
35
26
  if (schema) {
36
- const validate = this.#ajv.compile(schema);
37
- const isValid = validate(config);
27
+ const { isValid, errors, value } = validate(config, schema);
38
28
  if (!isValid) {
39
- const [error] = validate.errors;
40
- throw new Error([
41
- `${error.instancePath.replace(/\//gi, ".")} `,
42
- error.message,
43
- error.params?.allowedValues && `. Allowed values: ${error.params?.allowedValues}`
44
- ]
29
+ const [error] = errors;
30
+ throw new Error([`${error.path.replace(/\//gi, ".")} `, error.message, error.expected && `. Allowed values: ${error.expected}`]
45
31
  .filter(Boolean)
46
32
  .join(""));
47
33
  }
34
+ return value;
48
35
  }
49
36
  return config;
50
37
  }
@@ -1,6 +1,6 @@
1
1
  import { constant, inject, injectable } from "@tsed/di";
2
+ import { $asyncEmit } from "@tsed/hooks";
2
3
  import chalk from "chalk";
3
- import { CommandStoreKeys } from "../domains/CommandStoreKeys.js";
4
4
  import { PackageManagersModule } from "../packageManagers/PackageManagersModule.js";
5
5
  import { createSubTasks } from "../utils/createTasksRunner.js";
6
6
  import { loadPlugins } from "../utils/loadPlugins.js";
@@ -19,7 +19,6 @@ export class CliPlugins {
19
19
  this.name = constant("name", "");
20
20
  this.loadPlugins = loadPlugins;
21
21
  this.npmRegistryClient = inject(NpmRegistryClient);
22
- this.cliHooks = inject(CliHooks);
23
22
  this.packageJson = inject(ProjectPackageJson);
24
23
  this.packageManagers = inject(PackageManagersModule);
25
24
  }
@@ -33,7 +32,7 @@ export class CliPlugins {
33
32
  return {
34
33
  title: `Run plugin '${chalk.cyan(plugin)}'`,
35
34
  task: () => {
36
- return this.cliHooks.emit(CommandStoreKeys.ADD, plugin, ctx);
35
+ return $asyncEmit("$onAddPlugin", [plugin, ctx]);
37
36
  }
38
37
  };
39
38
  });
@@ -1,21 +1,18 @@
1
- import { classOf } from "@tsed/core";
1
+ import { PromptRunner } from "@tsed/cli-prompts";
2
+ import { classOf, isArrowFn } from "@tsed/core";
2
3
  import { configuration, constant, context, destroyInjector, DIContext, getContext, inject, injectable, injector, logger, Provider, runInContext } from "@tsed/di";
3
4
  import { $asyncAlter, $asyncEmit } from "@tsed/hooks";
4
5
  import { pascalCase } from "change-case";
5
6
  import { Argument, Command } from "commander";
6
- import Inquirer from "inquirer";
7
- import inquirer_autocomplete_prompt from "inquirer-autocomplete-prompt";
8
7
  import { v4 } from "uuid";
9
- import { CommandStoreKeys } from "../domains/CommandStoreKeys.js";
10
8
  import { PackageManagersModule } from "../packageManagers/index.js";
11
9
  import { createSubTasks, createTasksRunner } from "../utils/createTasksRunner.js";
12
10
  import { getCommandMetadata } from "../utils/getCommandMetadata.js";
13
- import { mapCommanderOptions } from "../utils/index.js";
11
+ import { mapCommanderOptions, validate } from "../utils/index.js";
14
12
  import { mapCommanderArgs } from "../utils/mapCommanderArgs.js";
15
13
  import { parseOption } from "../utils/parseOption.js";
16
14
  import { CliHooks } from "./CliHooks.js";
17
15
  import { ProjectPackageJson } from "./ProjectPackageJson.js";
18
- Inquirer.registerPrompt("autocomplete", inquirer_autocomplete_prompt);
19
16
  export class CliService {
20
17
  constructor() {
21
18
  this.reinstallAfterRun = constant("project.reinstallAfterRun", false);
@@ -24,6 +21,7 @@ export class CliService {
24
21
  this.hooks = inject(CliHooks);
25
22
  this.projectPkg = inject(ProjectPackageJson);
26
23
  this.packageManagers = inject(PackageManagersModule);
24
+ this.promptRunner = inject(PromptRunner);
27
25
  this.commands = new Map();
28
26
  }
29
27
  /**
@@ -44,78 +42,59 @@ export class CliService {
44
42
  */
45
43
  runLifecycle(cmdName, data = {}, $ctx) {
46
44
  return runInContext($ctx, async () => {
45
+ $ctx.set("dispatchCmd", cmdName);
47
46
  await $asyncEmit("$loadPackageJson");
48
- data = await this.beforePrompt(cmdName, data);
49
- $ctx.set("data", data);
50
- data = await this.prompt(cmdName, data);
51
- await this.dispatch(cmdName, data, $ctx);
47
+ try {
48
+ data = await this.prompt(cmdName, data, $ctx);
49
+ await this.exec(cmdName, data, $ctx);
50
+ await $asyncEmit("$onFinish", [data]);
51
+ }
52
+ catch (er) {
53
+ await $asyncEmit("$onFinish", [data, er]);
54
+ throw er;
55
+ }
56
+ finally {
57
+ await destroyInjector();
58
+ }
52
59
  });
53
60
  }
54
- async dispatch(cmdName, data, $ctx) {
55
- try {
56
- $ctx.set("dispatchCmd", cmdName);
57
- $ctx.set("data", data);
58
- await this.exec(cmdName, data, $ctx);
59
- }
60
- catch (er) {
61
- await $asyncEmit("$onFinish", er);
62
- await destroyInjector();
63
- throw er;
64
- }
65
- await $asyncEmit("$onFinish");
66
- await destroyInjector();
67
- }
68
61
  async exec(cmdName, data, $ctx) {
69
- const initialTasks = await this.getTasks(cmdName, data);
70
- if (initialTasks.length) {
71
- const tasks = [
72
- ...initialTasks,
73
- {
62
+ const tasks = await this.getTasks(cmdName, data);
63
+ $ctx.set("data", data);
64
+ if (tasks.length) {
65
+ if (this.reinstallAfterRun && (this.projectPkg.rewrite || this.projectPkg.reinstall)) {
66
+ tasks.push({
74
67
  title: "Install dependencies",
75
- enabled: () => this.reinstallAfterRun && (this.projectPkg.rewrite || this.projectPkg.reinstall),
76
68
  task: createSubTasks(() => this.packageManagers.install(data), { ...data, concurrent: false })
77
- },
78
- ...(await this.getPostInstallTasks(cmdName, data))
79
- ];
80
- return createTasksRunner(tasks, this.mapData(cmdName, data, $ctx));
81
- }
82
- }
83
- /**
84
- * Run prompt for a given command
85
- * @param cmdName
86
- * @param data Initial data
87
- */
88
- async beforePrompt(cmdName, data = {}) {
89
- const provider = this.commands.get(cmdName);
90
- const instance = inject(provider.useClass);
91
- const verbose = data.verbose;
92
- if (instance.$beforePrompt) {
93
- data = await instance.$beforePrompt(JSON.parse(JSON.stringify(data)));
94
- data.verbose = verbose;
69
+ }, ...(await this.getPostInstallTasks(cmdName, data)));
70
+ }
71
+ data = this.mapData(cmdName, data, $ctx);
72
+ $ctx.set("data", data);
73
+ return createTasksRunner(tasks, data);
95
74
  }
96
- return data;
97
75
  }
98
76
  /**
99
77
  * Run prompt for a given command
100
78
  * @param cmdName
101
- * @param ctx Initial data
79
+ * @param data
80
+ * @param $ctx
102
81
  */
103
- async prompt(cmdName, ctx = {}) {
82
+ async prompt(cmdName, data = {}, $ctx) {
104
83
  const provider = this.commands.get(cmdName);
105
- const instance = inject(provider.useClass);
84
+ const instance = inject(provider.token);
85
+ $ctx.set("data", data);
106
86
  if (instance.$prompt) {
107
- const questions = [
108
- ...(await instance.$prompt(ctx)),
109
- ...(await this.hooks.emit(CommandStoreKeys.PROMPT_HOOKS, cmdName, ctx))
110
- ];
111
- if (questions.length) {
112
- ctx = {
113
- ...ctx,
114
- ...(await Inquirer.prompt(questions))
87
+ const questions = await instance.$prompt(data);
88
+ if (questions) {
89
+ const answers = await this.promptRunner.run(questions, data);
90
+ data = {
91
+ ...data,
92
+ ...answers
115
93
  };
116
94
  }
117
95
  }
118
- return ctx;
96
+ $ctx.set("data", data);
97
+ return data;
119
98
  }
120
99
  /**
121
100
  * Run lifecycle
@@ -127,14 +106,10 @@ export class CliService {
127
106
  const provider = this.commands.get(cmdName);
128
107
  const instance = inject(provider.token);
129
108
  data = this.mapData(cmdName, data, $ctx);
130
- if (instance.$beforeExec) {
131
- await instance.$beforeExec(data);
132
- }
133
- return [
134
- ...(await instance.$exec(data)),
135
- ...(await this.hooks.emit(CommandStoreKeys.EXEC_HOOKS, cmdName, data)),
136
- ...(await $asyncAlter(`$alter${pascalCase(cmdName)}Tasks`, [], [data]))
137
- ].map((opts) => {
109
+ const tasks = [];
110
+ tasks.push(...((await instance.$exec(data)) || []));
111
+ tasks.push(...(await $asyncAlter(`$alter${pascalCase(cmdName)}Tasks`, [], [data])));
112
+ return tasks.map((opts) => {
138
113
  return {
139
114
  ...opts,
140
115
  task: async (arg, task) => {
@@ -148,32 +123,45 @@ export class CliService {
148
123
  }
149
124
  async getPostInstallTasks(cmdName, data) {
150
125
  const provider = this.commands.get(cmdName);
151
- const instance = inject(provider.useClass);
126
+ const instance = inject(provider.token);
152
127
  data = this.mapData(cmdName, data, getContext());
153
128
  return [
154
129
  ...(instance.$postInstall ? await instance.$postInstall(data) : []),
155
- ...(await this.hooks.emit(CommandStoreKeys.POST_INSTALL_HOOKS, cmdName, data)),
156
130
  ...(await $asyncAlter(`$alter${pascalCase(cmdName)}PostInstallTasks`, [], [data])),
157
131
  ...(instance.$afterPostInstall ? await instance.$afterPostInstall(data) : [])
158
132
  ];
159
133
  }
160
134
  createCommand(metadata) {
161
- const { args, name, options, description, alias, allowUnknownOption } = metadata;
135
+ const { name, description, alias, inputSchema } = metadata;
162
136
  if (this.commands.has(name)) {
163
137
  return this.commands.get(name).command;
164
138
  }
165
- let cmd = this.program.command(name);
139
+ const { args, options, allowUnknownOption } = metadata.getOptions();
166
140
  const onAction = (commandName) => {
167
141
  const [, ...rawArgs] = cmd.args;
168
142
  const mappedArgs = mapCommanderArgs(args, this.program.args.filter((arg) => commandName === arg));
169
143
  const allOpts = mapCommanderOptions(commandName, this.program.commands);
170
- const data = {
144
+ let data = {
171
145
  ...allOpts,
172
146
  verbose: !!this.program.opts().verbose,
173
147
  ...mappedArgs,
174
148
  ...cmd.opts(),
175
149
  rawArgs
176
150
  };
151
+ if (inputSchema) {
152
+ const schema = isArrowFn(inputSchema) ? inputSchema() : inputSchema;
153
+ const { isValid, errors, value } = validate(data, schema);
154
+ if (isValid) {
155
+ data = value;
156
+ }
157
+ else {
158
+ logger().error({
159
+ event: "VALIDATION_ERROR",
160
+ errors
161
+ });
162
+ throw new Error("Validation error");
163
+ }
164
+ }
177
165
  const $ctx = new DIContext({
178
166
  id: v4(),
179
167
  injector: injector(),
@@ -187,6 +175,7 @@ export class CliService {
187
175
  configuration().set("command.metadata", metadata);
188
176
  return this.runLifecycle(name, data, $ctx);
189
177
  };
178
+ let cmd = this.program.command(name);
190
179
  if (alias) {
191
180
  cmd = cmd.alias(alias);
192
181
  }
@@ -205,7 +194,7 @@ export class CliService {
205
194
  }
206
195
  mapData(cmdName, data, $ctx) {
207
196
  const provider = this.commands.get(cmdName);
208
- const instance = inject(provider.useClass);
197
+ const instance = inject(provider.token);
209
198
  const verbose = data.verbose;
210
199
  data.commandName ||= cmdName;
211
200
  if (instance.$mapContext) {
@@ -227,7 +216,7 @@ export class CliService {
227
216
  * @param provider
228
217
  */
229
218
  build(provider) {
230
- const metadata = getCommandMetadata(provider.useClass);
219
+ const metadata = getCommandMetadata(provider.token);
231
220
  if (metadata.name) {
232
221
  if (this.commands.has(metadata.name)) {
233
222
  throw Error(`The ${metadata.name} command is already registered. Change your command name used by the class ${classOf(provider.useClass)}`);