@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.
- package/lib/esm/CliCore.js +24 -38
- package/lib/esm/decorators/command.js +1 -1
- package/lib/esm/decorators/index.js +0 -4
- package/lib/esm/fn/command.js +31 -3
- package/lib/esm/index.js +1 -2
- package/lib/esm/interfaces/CommandProvider.js +1 -2
- package/lib/esm/interfaces/ProjectPreferences.js +1 -0
- package/lib/esm/interfaces/index.js +1 -2
- package/lib/esm/packageManagers/PackageManagersModule.js +11 -19
- package/lib/esm/packageManagers/index.js +1 -0
- package/lib/esm/packageManagers/supports/BaseManager.js +2 -6
- package/lib/esm/packageManagers/supports/YarnBerryManager.js +4 -1
- package/lib/esm/services/CliFs.js +3 -1
- package/lib/esm/services/CliHttpClient.js +3 -2
- package/lib/esm/services/CliHttpLogClient.js +1 -1
- package/lib/esm/services/CliLoadFile.js +5 -18
- package/lib/esm/services/CliPlugins.js +2 -3
- package/lib/esm/services/CliService.js +65 -76
- package/lib/esm/services/ProjectPackageJson.js +17 -4
- package/lib/esm/utils/createInjector.js +1 -1
- package/lib/esm/utils/getCommandMetadata.js +34 -7
- package/lib/esm/utils/index.js +1 -0
- package/lib/esm/utils/loadPlugins.js +5 -18
- package/lib/esm/utils/validate.js +23 -0
- package/lib/types/CliCore.d.ts +2 -9
- package/lib/types/decorators/command.d.ts +2 -2
- package/lib/types/decorators/index.d.ts +0 -4
- package/lib/types/fn/command.d.ts +58 -3
- package/lib/types/index.d.ts +1 -2
- package/lib/types/interfaces/CommandData.d.ts +1 -0
- package/lib/types/interfaces/CommandMetadata.d.ts +17 -15
- package/lib/types/interfaces/{CommandParameters.d.ts → CommandOptions.d.ts} +20 -3
- package/lib/types/interfaces/CommandProvider.d.ts +4 -22
- package/lib/types/interfaces/ProjectPreferences.d.ts +2 -1
- package/lib/types/interfaces/index.d.ts +3 -3
- package/lib/types/packageManagers/PackageManagersModule.d.ts +1 -2
- package/lib/types/packageManagers/index.d.ts +1 -0
- package/lib/types/services/CliHttpLogClient.d.ts +1 -1
- package/lib/types/services/CliLoadFile.d.ts +2 -4
- package/lib/types/services/CliPlugins.d.ts +0 -1
- package/lib/types/services/CliService.d.ts +6 -10
- package/lib/types/services/ProjectPackageJson.d.ts +9 -0
- package/lib/types/utils/index.d.ts +1 -0
- package/lib/types/utils/mapCommanderArgs.d.ts +1 -1
- package/lib/types/utils/resolveConfiguration.d.ts +1 -1
- package/lib/types/utils/validate.d.ts +14 -0
- package/package.json +4 -7
- package/readme.md +2 -2
- package/lib/esm/decorators/on.js +0 -8
- package/lib/esm/decorators/onAdd.js +0 -5
- package/lib/esm/decorators/onExec.js +0 -5
- package/lib/esm/decorators/onPostInstall.js +0 -5
- package/lib/esm/decorators/onPrompt.js +0 -5
- package/lib/esm/domains/CommandStoreKeys.js +0 -8
- package/lib/types/decorators/on.d.ts +0 -1
- package/lib/types/decorators/onAdd.d.ts +0 -1
- package/lib/types/decorators/onExec.d.ts +0 -1
- package/lib/types/decorators/onPostInstall.d.ts +0 -1
- package/lib/types/decorators/onPrompt.d.ts +0 -1
- package/lib/types/domains/CommandStoreKeys.d.ts +0 -7
- /package/lib/esm/interfaces/{CommandParameters.js → CommandOptions.js} +0 -0
package/lib/esm/CliCore.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import "@tsed/logger-std";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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]);
|
package/lib/esm/fn/command.js
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
import { injectable } from "@tsed/di";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
2
|
-
import AutocompletePrompt from "inquirer-autocomplete-prompt";
|
|
1
|
+
export {};
|
|
@@ -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 "./
|
|
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 {
|
|
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
|
-
|
|
22
|
-
constructor(
|
|
23
|
-
this.
|
|
24
|
-
this.packageManagers =
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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]);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
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) =>
|
|
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(
|
|
57
|
+
this.configureProxy(url, options);
|
|
57
58
|
return options;
|
|
58
59
|
}
|
|
59
60
|
configureProxy(endpoint, options) {
|
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
import { extname } from "node:path";
|
|
2
2
|
import { inject, injectable } from "@tsed/di";
|
|
3
|
-
import {
|
|
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
|
|
37
|
-
const isValid = validate(config);
|
|
27
|
+
const { isValid, errors, value } = validate(config, schema);
|
|
38
28
|
if (!isValid) {
|
|
39
|
-
const [error] =
|
|
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
|
|
35
|
+
return $asyncEmit("$onAddPlugin", [plugin, ctx]);
|
|
37
36
|
}
|
|
38
37
|
};
|
|
39
38
|
});
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
|
79
|
+
* @param data
|
|
80
|
+
* @param $ctx
|
|
102
81
|
*/
|
|
103
|
-
async prompt(cmdName,
|
|
82
|
+
async prompt(cmdName, data = {}, $ctx) {
|
|
104
83
|
const provider = this.commands.get(cmdName);
|
|
105
|
-
const instance = inject(provider.
|
|
84
|
+
const instance = inject(provider.token);
|
|
85
|
+
$ctx.set("data", data);
|
|
106
86
|
if (instance.$prompt) {
|
|
107
|
-
const questions =
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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.
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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)}`);
|