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

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 (57) hide show
  1. package/lib/esm/CliCore.js +19 -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/interfaces/ProjectPreferences.js +1 -0
  6. package/lib/esm/interfaces/index.js +1 -1
  7. package/lib/esm/packageManagers/PackageManagersModule.js +11 -19
  8. package/lib/esm/packageManagers/index.js +1 -0
  9. package/lib/esm/packageManagers/supports/BaseManager.js +2 -6
  10. package/lib/esm/packageManagers/supports/YarnBerryManager.js +4 -1
  11. package/lib/esm/services/CliFs.js +3 -1
  12. package/lib/esm/services/CliHttpClient.js +3 -2
  13. package/lib/esm/services/CliHttpLogClient.js +1 -1
  14. package/lib/esm/services/CliLoadFile.js +5 -18
  15. package/lib/esm/services/CliPlugins.js +2 -3
  16. package/lib/esm/services/CliService.js +59 -71
  17. package/lib/esm/services/ProjectPackageJson.js +17 -4
  18. package/lib/esm/utils/createInjector.js +1 -1
  19. package/lib/esm/utils/getCommandMetadata.js +34 -7
  20. package/lib/esm/utils/index.js +1 -0
  21. package/lib/esm/utils/loadPlugins.js +5 -18
  22. package/lib/esm/utils/validate.js +23 -0
  23. package/lib/types/CliCore.d.ts +2 -9
  24. package/lib/types/decorators/command.d.ts +2 -2
  25. package/lib/types/decorators/index.d.ts +0 -4
  26. package/lib/types/fn/command.d.ts +58 -3
  27. package/lib/types/interfaces/CommandMetadata.d.ts +17 -15
  28. package/lib/types/interfaces/{CommandParameters.d.ts → CommandOptions.d.ts} +20 -3
  29. package/lib/types/interfaces/CommandProvider.d.ts +0 -10
  30. package/lib/types/interfaces/ProjectPreferences.d.ts +2 -1
  31. package/lib/types/interfaces/index.d.ts +3 -3
  32. package/lib/types/packageManagers/PackageManagersModule.d.ts +1 -2
  33. package/lib/types/packageManagers/index.d.ts +1 -0
  34. package/lib/types/services/CliHttpLogClient.d.ts +1 -1
  35. package/lib/types/services/CliLoadFile.d.ts +2 -4
  36. package/lib/types/services/CliPlugins.d.ts +0 -1
  37. package/lib/types/services/CliService.d.ts +4 -10
  38. package/lib/types/services/ProjectPackageJson.d.ts +9 -0
  39. package/lib/types/utils/index.d.ts +1 -0
  40. package/lib/types/utils/mapCommanderArgs.d.ts +1 -1
  41. package/lib/types/utils/resolveConfiguration.d.ts +1 -1
  42. package/lib/types/utils/validate.d.ts +14 -0
  43. package/package.json +2 -2
  44. package/readme.md +1 -1
  45. package/lib/esm/decorators/on.js +0 -8
  46. package/lib/esm/decorators/onAdd.js +0 -5
  47. package/lib/esm/decorators/onExec.js +0 -5
  48. package/lib/esm/decorators/onPostInstall.js +0 -5
  49. package/lib/esm/decorators/onPrompt.js +0 -5
  50. package/lib/esm/domains/CommandStoreKeys.js +0 -8
  51. package/lib/types/decorators/on.d.ts +0 -1
  52. package/lib/types/decorators/onAdd.d.ts +0 -1
  53. package/lib/types/decorators/onExec.d.ts +0 -1
  54. package/lib/types/decorators/onPostInstall.d.ts +0 -1
  55. package/lib/types/decorators/onPrompt.d.ts +0 -1
  56. package/lib/types/domains/CommandStoreKeys.d.ts +0 -7
  57. /package/lib/esm/interfaces/{CommandParameters.js → CommandOptions.js} +0 -0
@@ -1,16 +1,13 @@
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 { constant, inject, injector } from "@tsed/di";
5
4
  import { $asyncEmit } from "@tsed/hooks";
6
5
  import chalk from "chalk";
7
6
  import { Command } from "commander";
8
7
  import semver from "semver";
9
8
  import updateNotifier from "update-notifier";
10
9
  import { CliError } from "./domains/CliError.js";
11
- import { CliPackageJson } from "./services/CliPackageJson.js";
12
10
  import { CliService } from "./services/CliService.js";
13
- import { ProjectPackageJson } from "./services/ProjectPackageJson.js";
14
11
  import { createInjector } from "./utils/createInjector.js";
15
12
  import { loadPlugins } from "./utils/loadPlugins.js";
16
13
  import { resolveConfiguration } from "./utils/resolveConfiguration.js";
@@ -18,9 +15,8 @@ function isHelpManual(argv) {
18
15
  return argv.includes("-h") || argv.includes("--help");
19
16
  }
20
17
  export class CliCore {
21
- constructor() {
22
- this.injector = inject(InjectorService);
23
- this.cliService = inject(CliService);
18
+ constructor(settings) {
19
+ createInjector(settings);
24
20
  }
25
21
  static checkPrecondition(settings) {
26
22
  const { pkg } = settings;
@@ -48,41 +44,16 @@ export class CliCore {
48
44
  }
49
45
  return this;
50
46
  }
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) {
47
+ static async bootstrap(settings) {
60
48
  if (settings.checkPrecondition) {
61
49
  this.checkPrecondition(settings);
62
50
  }
63
51
  if (settings.updateNotifier) {
64
52
  await this.updateNotifier(settings.pkg);
65
53
  }
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) {
54
+ settings = resolveConfiguration(settings);
84
55
  const argv = settings.argv || process.argv;
85
- return createInjector({
56
+ return new CliCore({
86
57
  ...settings,
87
58
  name: settings.name || "tsed",
88
59
  argv,
@@ -92,7 +63,11 @@ export class CliCore {
92
63
  scriptsDir: "scripts",
93
64
  ...(settings.project || {})
94
65
  }
95
- });
66
+ }).bootstrap();
67
+ }
68
+ static async updateNotifier(pkg) {
69
+ updateNotifier({ pkg, updateCheckInterval: 0 }).notify();
70
+ return this;
96
71
  }
97
72
  static getProjectRoot(argv) {
98
73
  if (!isHelpManual(argv)) {
@@ -103,7 +78,14 @@ export class CliCore {
103
78
  }
104
79
  async bootstrap() {
105
80
  try {
106
- await this.cliService.parseArgs(this.injector.settings.get("argv"));
81
+ const cliService = inject(CliService);
82
+ constant("plugins") && (await loadPlugins());
83
+ await $asyncEmit("$beforeInit");
84
+ await injector().load();
85
+ await $asyncEmit("$afterInit");
86
+ injector().settings.set("loaded", true);
87
+ await $asyncEmit("$onReady");
88
+ await cliService.parseArgs(constant("argv"));
107
89
  }
108
90
  catch (er) {
109
91
  throw new CliError({ origin: er, cli: this });
@@ -111,4 +93,3 @@ export class CliCore {
111
93
  return this;
112
94
  }
113
95
  }
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
  }
@@ -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 = {}));
@@ -2,7 +2,7 @@ import { Type } from "@tsed/core";
2
2
  export * from "./CliDefaultOptions.js";
3
3
  export * from "./CommandData.js";
4
4
  export * from "./CommandMetadata.js";
5
- export * from "./CommandParameters.js";
5
+ export * from "./CommandOptions.js";
6
6
  export * from "./CommandProvider.js";
7
7
  export * from "./PackageJson.js";
8
8
  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,4 +1,4 @@
1
- import { classOf } from "@tsed/core";
1
+ import { classOf, isArrowFn } from "@tsed/core";
2
2
  import { configuration, constant, context, destroyInjector, DIContext, getContext, inject, injectable, injector, logger, Provider, runInContext } from "@tsed/di";
3
3
  import { $asyncAlter, $asyncEmit } from "@tsed/hooks";
4
4
  import { pascalCase } from "change-case";
@@ -6,11 +6,10 @@ import { Argument, Command } from "commander";
6
6
  import Inquirer from "inquirer";
7
7
  import inquirer_autocomplete_prompt from "inquirer-autocomplete-prompt";
8
8
  import { v4 } from "uuid";
9
- import { CommandStoreKeys } from "../domains/CommandStoreKeys.js";
10
9
  import { PackageManagersModule } from "../packageManagers/index.js";
11
10
  import { createSubTasks, createTasksRunner } from "../utils/createTasksRunner.js";
12
11
  import { getCommandMetadata } from "../utils/getCommandMetadata.js";
13
- import { mapCommanderOptions } from "../utils/index.js";
12
+ import { mapCommanderOptions, validate } from "../utils/index.js";
14
13
  import { mapCommanderArgs } from "../utils/mapCommanderArgs.js";
15
14
  import { parseOption } from "../utils/parseOption.js";
16
15
  import { CliHooks } from "./CliHooks.js";
@@ -44,78 +43,57 @@ export class CliService {
44
43
  */
45
44
  runLifecycle(cmdName, data = {}, $ctx) {
46
45
  return runInContext($ctx, async () => {
47
- 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);
52
- });
53
- }
54
- async dispatch(cmdName, data, $ctx) {
55
- try {
56
46
  $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);
47
+ await $asyncEmit("$loadPackageJson");
48
+ data = await this.prompt(cmdName, data, $ctx);
49
+ try {
50
+ await this.exec(cmdName, data, $ctx);
51
+ }
52
+ catch (er) {
53
+ await $asyncEmit("$onFinish", [data, er]);
54
+ await destroyInjector();
55
+ throw er;
56
+ }
57
+ await $asyncEmit("$onFinish", [data]);
62
58
  await destroyInjector();
63
- throw er;
64
- }
65
- await $asyncEmit("$onFinish");
66
- await destroyInjector();
59
+ });
67
60
  }
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
- ];
87
+ const questions = [...(await instance.$prompt(data))];
111
88
  if (questions.length) {
112
- ctx = {
113
- ...ctx,
89
+ data = {
90
+ ...data,
114
91
  ...(await Inquirer.prompt(questions))
115
92
  };
116
93
  }
117
94
  }
118
- return ctx;
95
+ $ctx.set("data", data);
96
+ return data;
119
97
  }
120
98
  /**
121
99
  * Run lifecycle
@@ -127,14 +105,10 @@ export class CliService {
127
105
  const provider = this.commands.get(cmdName);
128
106
  const instance = inject(provider.token);
129
107
  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) => {
108
+ const tasks = [];
109
+ tasks.push(...((await instance.$exec(data)) || []));
110
+ tasks.push(...(await $asyncAlter(`$alter${pascalCase(cmdName)}Tasks`, [], [data])));
111
+ return tasks.map((opts) => {
138
112
  return {
139
113
  ...opts,
140
114
  task: async (arg, task) => {
@@ -148,32 +122,45 @@ export class CliService {
148
122
  }
149
123
  async getPostInstallTasks(cmdName, data) {
150
124
  const provider = this.commands.get(cmdName);
151
- const instance = inject(provider.useClass);
125
+ const instance = inject(provider.token);
152
126
  data = this.mapData(cmdName, data, getContext());
153
127
  return [
154
128
  ...(instance.$postInstall ? await instance.$postInstall(data) : []),
155
- ...(await this.hooks.emit(CommandStoreKeys.POST_INSTALL_HOOKS, cmdName, data)),
156
129
  ...(await $asyncAlter(`$alter${pascalCase(cmdName)}PostInstallTasks`, [], [data])),
157
130
  ...(instance.$afterPostInstall ? await instance.$afterPostInstall(data) : [])
158
131
  ];
159
132
  }
160
133
  createCommand(metadata) {
161
- const { args, name, options, description, alias, allowUnknownOption } = metadata;
134
+ const { name, description, alias, inputSchema } = metadata;
162
135
  if (this.commands.has(name)) {
163
136
  return this.commands.get(name).command;
164
137
  }
165
- let cmd = this.program.command(name);
138
+ const { args, options, allowUnknownOption } = metadata.getOptions();
166
139
  const onAction = (commandName) => {
167
140
  const [, ...rawArgs] = cmd.args;
168
141
  const mappedArgs = mapCommanderArgs(args, this.program.args.filter((arg) => commandName === arg));
169
142
  const allOpts = mapCommanderOptions(commandName, this.program.commands);
170
- const data = {
143
+ let data = {
171
144
  ...allOpts,
172
145
  verbose: !!this.program.opts().verbose,
173
146
  ...mappedArgs,
174
147
  ...cmd.opts(),
175
148
  rawArgs
176
149
  };
150
+ if (inputSchema) {
151
+ const schema = isArrowFn(inputSchema) ? inputSchema() : inputSchema;
152
+ const { isValid, errors, value } = validate(data, schema);
153
+ if (isValid) {
154
+ data = value;
155
+ }
156
+ else {
157
+ logger().error({
158
+ event: "VALIDATION_ERROR",
159
+ errors
160
+ });
161
+ throw new Error("Validation error");
162
+ }
163
+ }
177
164
  const $ctx = new DIContext({
178
165
  id: v4(),
179
166
  injector: injector(),
@@ -187,6 +174,7 @@ export class CliService {
187
174
  configuration().set("command.metadata", metadata);
188
175
  return this.runLifecycle(name, data, $ctx);
189
176
  };
177
+ let cmd = this.program.command(name);
190
178
  if (alias) {
191
179
  cmd = cmd.alias(alias);
192
180
  }
@@ -205,7 +193,7 @@ export class CliService {
205
193
  }
206
194
  mapData(cmdName, data, $ctx) {
207
195
  const provider = this.commands.get(cmdName);
208
- const instance = inject(provider.useClass);
196
+ const instance = inject(provider.token);
209
197
  const verbose = data.verbose;
210
198
  data.commandName ||= cmdName;
211
199
  if (instance.$mapContext) {
@@ -227,7 +215,7 @@ export class CliService {
227
215
  * @param provider
228
216
  */
229
217
  build(provider) {
230
- const metadata = getCommandMetadata(provider.useClass);
218
+ const metadata = getCommandMetadata(provider.token);
231
219
  if (metadata.name) {
232
220
  if (this.commands.has(metadata.name)) {
233
221
  throw Error(`The ${metadata.name} command is already registered. Change your command name used by the class ${classOf(provider.useClass)}`);
@@ -45,12 +45,21 @@ export class ProjectPackageJson {
45
45
  get path() {
46
46
  return join(this.dir, "package.json");
47
47
  }
48
+ /**
49
+ * @deprecated
50
+ */
48
51
  get dir() {
49
- return String(constant("project.rootDir"));
52
+ return this.cwd;
50
53
  }
54
+ /**
55
+ * @deprecated
56
+ * @param dir
57
+ */
51
58
  set dir(dir) {
52
- configuration().set("project.rootDir", dir);
53
- this.read();
59
+ this.setCWD(dir);
60
+ }
61
+ get cwd() {
62
+ return String(constant("project.rootDir", ""));
54
63
  }
55
64
  get name() {
56
65
  return this.raw.name;
@@ -83,6 +92,10 @@ export class ProjectPackageJson {
83
92
  get preferences() {
84
93
  return this.raw[constant("name")];
85
94
  }
95
+ setCWD(dir) {
96
+ configuration().set("project.rootDir", dir);
97
+ this.read();
98
+ }
86
99
  fillWithPreferences(ctx) {
87
100
  return {
88
101
  ...ctx,
@@ -222,7 +235,7 @@ export class ProjectPackageJson {
222
235
  refresh() {
223
236
  this.reinstall = false;
224
237
  this.rewrite = false;
225
- const cwd = constant("project.rootDir");
238
+ const cwd = this.cwd;
226
239
  const pkgPath = join(String(cwd), "package.json");
227
240
  const pkg = this.fs.readJsonSync(pkgPath, { encoding: "utf8" });
228
241
  pkg.scripts = {