task-script-support-cli 0.1.1 → 0.2.4
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/assets/yargs-template/task-runner/.prettierignore +3 -0
- package/assets/yargs-template/task-runner/.prettierrc +1 -0
- package/assets/yargs-template/task-runner/.vscode/launch.json +34 -0
- package/assets/yargs-template/task-runner/eslint.config.ts +11 -0
- package/assets/yargs-template/task-runner/install-link.sh +21 -0
- package/assets/yargs-template/task-runner/package-lock.json +3387 -0
- package/assets/yargs-template/task-runner/package.json +51 -0
- package/assets/yargs-template/task-runner/src/commands/verify.ts +10 -0
- package/assets/yargs-template/task-runner/src/index.ts +46 -0
- package/assets/yargs-template/task-runner/src/services/banner-service.ts +38 -0
- package/assets/yargs-template/task-runner/src/services/log-service.ts +48 -0
- package/assets/yargs-template/task-runner/src/services/spawn-service.ts +87 -0
- package/assets/yargs-template/task-runner/src/services/util-service.ts +28 -0
- package/assets/yargs-template/task-runner/src/tasks/check-env.ts +49 -0
- package/assets/yargs-template/task-runner/src/tasks/log-state.ts +22 -0
- package/assets/yargs-template/task-runner/src/tasks/print-banner.ts +73 -0
- package/assets/yargs-template/task-runner/src/types/process.ts +9 -0
- package/assets/yargs-template/task-runner/src/types/state.ts +31 -0
- package/assets/yargs-template/task-runner/src/wrappers/app-task.ts +42 -0
- package/assets/yargs-template/task-runner/src/wrappers/command.ts +20 -0
- package/assets/yargs-template/task-runner/src/wrappers/dependencies.ts +24 -0
- package/assets/yargs-template/task-runner/task-runner +3 -0
- package/assets/yargs-template/task-runner/templater.sh +57 -0
- package/assets/yargs-template/task-runner/tests/commands/verify.test.ts +42 -0
- package/assets/yargs-template/task-runner/tests/services/spawn-service.test.ts +132 -0
- package/assets/yargs-template/task-runner/tests/services/util-service.test.ts +36 -0
- package/assets/yargs-template/task-runner/tests/setup.ts +1 -0
- package/assets/yargs-template/task-runner/tests/tasks/check-env.test.ts +30 -0
- package/assets/yargs-template/task-runner/tsconfig.json +27 -0
- package/assets/yargs-template/task-runner/vitest.config.ts +14 -0
- package/dist/assets/yargs-template/task-runner/.prettierignore +3 -0
- package/dist/assets/yargs-template/task-runner/.prettierrc +1 -0
- package/dist/assets/yargs-template/task-runner/.vscode/launch.json +34 -0
- package/dist/assets/yargs-template/task-runner/eslint.config.ts +11 -0
- package/dist/assets/yargs-template/task-runner/install-link.sh +21 -0
- package/dist/assets/yargs-template/task-runner/package-lock.json +3387 -0
- package/dist/assets/yargs-template/task-runner/package.json +51 -0
- package/dist/assets/yargs-template/task-runner/src/commands/verify.ts +10 -0
- package/dist/assets/yargs-template/task-runner/src/index.ts +46 -0
- package/dist/assets/yargs-template/task-runner/src/services/banner-service.ts +38 -0
- package/dist/assets/yargs-template/task-runner/src/services/log-service.ts +48 -0
- package/dist/assets/yargs-template/task-runner/src/services/spawn-service.ts +87 -0
- package/dist/assets/yargs-template/task-runner/src/services/util-service.ts +28 -0
- package/dist/assets/yargs-template/task-runner/src/tasks/check-env.ts +49 -0
- package/dist/assets/yargs-template/task-runner/src/tasks/log-state.ts +22 -0
- package/dist/assets/yargs-template/task-runner/src/tasks/print-banner.ts +73 -0
- package/dist/assets/yargs-template/task-runner/src/types/process.ts +9 -0
- package/dist/assets/yargs-template/task-runner/src/types/state.ts +31 -0
- package/dist/assets/yargs-template/task-runner/src/wrappers/app-task.ts +42 -0
- package/dist/assets/yargs-template/task-runner/src/wrappers/command.ts +20 -0
- package/dist/assets/yargs-template/task-runner/src/wrappers/dependencies.ts +24 -0
- package/dist/assets/yargs-template/task-runner/task-runner +3 -0
- package/dist/assets/yargs-template/task-runner/templater.sh +57 -0
- package/dist/assets/yargs-template/task-runner/tests/commands/verify.test.ts +42 -0
- package/dist/assets/yargs-template/task-runner/tests/services/spawn-service.test.ts +132 -0
- package/dist/assets/yargs-template/task-runner/tests/services/util-service.test.ts +36 -0
- package/dist/assets/yargs-template/task-runner/tests/setup.ts +1 -0
- package/dist/assets/yargs-template/task-runner/tests/tasks/check-env.test.ts +30 -0
- package/dist/assets/yargs-template/task-runner/tsconfig.json +27 -0
- package/dist/assets/yargs-template/task-runner/vitest.config.ts +14 -0
- package/dist/package.json +9 -3
- package/dist/src/commands/about.d.ts +2 -2
- package/dist/src/commands/about.d.ts.map +1 -1
- package/dist/src/commands/about.js +2 -2
- package/dist/src/commands/about.js.map +1 -1
- package/dist/src/commands/configure.d.ts +8 -0
- package/dist/src/commands/configure.d.ts.map +1 -0
- package/dist/src/commands/configure.js +25 -0
- package/dist/src/commands/configure.js.map +1 -0
- package/dist/src/commands/gen.d.ts +9 -7
- package/dist/src/commands/gen.d.ts.map +1 -1
- package/dist/src/commands/gen.js +10 -6
- package/dist/src/commands/gen.js.map +1 -1
- package/dist/src/commands/new.d.ts +8 -0
- package/dist/src/commands/new.d.ts.map +1 -0
- package/dist/src/commands/new.js +25 -0
- package/dist/src/commands/new.js.map +1 -0
- package/dist/src/index.js +10 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/services/cache-service.d.ts +11 -0
- package/dist/src/services/cache-service.d.ts.map +1 -0
- package/dist/src/services/cache-service.js +64 -0
- package/dist/src/services/cache-service.js.map +1 -0
- package/dist/src/services/file-service.d.ts +8 -2
- package/dist/src/services/file-service.d.ts.map +1 -1
- package/dist/src/services/file-service.js +38 -6
- package/dist/src/services/file-service.js.map +1 -1
- package/dist/src/services/log-service.d.ts +1 -0
- package/dist/src/services/log-service.d.ts.map +1 -1
- package/dist/src/services/log-service.js +14 -1
- package/dist/src/services/log-service.js.map +1 -1
- package/dist/src/services/project-service.d.ts +3 -3
- package/dist/src/services/project-service.d.ts.map +1 -1
- package/dist/src/services/project-service.js +10 -10
- package/dist/src/services/project-service.js.map +1 -1
- package/dist/src/services/prompt-service.d.ts +34 -0
- package/dist/src/services/prompt-service.d.ts.map +1 -0
- package/dist/src/services/prompt-service.js +80 -0
- package/dist/src/services/prompt-service.js.map +1 -0
- package/dist/src/services/spawn-service.d.ts +2 -1
- package/dist/src/services/spawn-service.d.ts.map +1 -1
- package/dist/src/services/spawn-service.js +16 -1
- package/dist/src/services/spawn-service.js.map +1 -1
- package/dist/src/services/util-service.d.ts +2 -0
- package/dist/src/services/util-service.d.ts.map +1 -1
- package/dist/src/services/util-service.js +4 -0
- package/dist/src/services/util-service.js.map +1 -1
- package/dist/src/tasks/check-env.d.ts +3 -2
- package/dist/src/tasks/check-env.d.ts.map +1 -1
- package/dist/src/tasks/check-env.js +11 -3
- package/dist/src/tasks/check-env.js.map +1 -1
- package/dist/src/tasks/configure.d.ts +44 -0
- package/dist/src/tasks/configure.d.ts.map +1 -0
- package/dist/src/tasks/configure.js +102 -0
- package/dist/src/tasks/configure.js.map +1 -0
- package/dist/src/tasks/create-new-project.d.ts +26 -0
- package/dist/src/tasks/create-new-project.d.ts.map +1 -0
- package/dist/src/tasks/create-new-project.js +76 -0
- package/dist/src/tasks/create-new-project.js.map +1 -0
- package/dist/src/tasks/generate/generate-command.d.ts +35 -0
- package/dist/src/tasks/generate/generate-command.d.ts.map +1 -0
- package/dist/src/tasks/generate/generate-command.js +128 -0
- package/dist/src/tasks/generate/generate-command.js.map +1 -0
- package/dist/src/tasks/generate/generate-service.d.ts +30 -0
- package/dist/src/tasks/generate/generate-service.d.ts.map +1 -0
- package/dist/src/tasks/{generate-service.js → generate/generate-service.js} +32 -31
- package/dist/src/tasks/generate/generate-service.js.map +1 -0
- package/dist/src/tasks/generate/generate-task.d.ts +30 -0
- package/dist/src/tasks/generate/generate-task.d.ts.map +1 -0
- package/dist/src/tasks/{generate-task.js → generate/generate-task.js} +32 -31
- package/dist/src/tasks/generate/generate-task.js.map +1 -0
- package/dist/src/tasks/generate/select-gen-target-name.d.ts +15 -0
- package/dist/src/tasks/generate/select-gen-target-name.d.ts.map +1 -0
- package/dist/src/tasks/generate/select-gen-target-name.js +59 -0
- package/dist/src/tasks/generate/select-gen-target-name.js.map +1 -0
- package/dist/src/tasks/generate/select-gen-target.d.ts +14 -0
- package/dist/src/tasks/generate/select-gen-target.d.ts.map +1 -0
- package/dist/src/tasks/generate/select-gen-target.js +60 -0
- package/dist/src/tasks/generate/select-gen-target.js.map +1 -0
- package/dist/src/tasks/{print-about-information.d.ts → stdout/print-about-information.d.ts} +1 -1
- package/dist/src/tasks/stdout/print-about-information.d.ts.map +1 -0
- package/dist/src/tasks/{print-about-information.js → stdout/print-about-information.js} +2 -2
- package/dist/src/tasks/stdout/print-about-information.js.map +1 -0
- package/dist/src/tasks/stdout/print-banner.d.ts +12 -0
- package/dist/src/tasks/stdout/print-banner.d.ts.map +1 -0
- package/dist/src/tasks/{print-banner.js → stdout/print-banner.js} +15 -11
- package/dist/src/tasks/stdout/print-banner.js.map +1 -0
- package/dist/src/tasks/{print-generated-results.d.ts → stdout/print-generated-results.d.ts} +2 -2
- package/dist/src/tasks/stdout/print-generated-results.d.ts.map +1 -0
- package/dist/src/tasks/{print-generated-results.js → stdout/print-generated-results.js} +16 -12
- package/dist/src/tasks/stdout/print-generated-results.js.map +1 -0
- package/dist/src/tasks/sync-configuration.d.ts +38 -0
- package/dist/src/tasks/sync-configuration.d.ts.map +1 -0
- package/dist/src/tasks/sync-configuration.js +121 -0
- package/dist/src/tasks/sync-configuration.js.map +1 -0
- package/dist/src/tasks/test-task.d.ts +10 -0
- package/dist/src/tasks/test-task.d.ts.map +1 -0
- package/dist/src/tasks/test-task.js +36 -0
- package/dist/src/tasks/test-task.js.map +1 -0
- package/dist/src/templates/command.js +2 -2
- package/dist/src/templates/command.js.map +1 -1
- package/dist/src/templates/service.js +1 -1
- package/dist/src/templates/service.js.map +1 -1
- package/dist/src/types/process.d.ts +2 -1
- package/dist/src/types/process.d.ts.map +1 -1
- package/dist/src/types/process.js +1 -0
- package/dist/src/types/process.js.map +1 -1
- package/dist/src/types/project.d.ts +12 -0
- package/dist/src/types/project.d.ts.map +1 -0
- package/dist/src/types/project.js +3 -0
- package/dist/src/types/project.js.map +1 -0
- package/dist/src/types/state.d.ts +5 -1
- package/dist/src/types/state.d.ts.map +1 -1
- package/dist/src/types/state.js +1 -0
- package/dist/src/types/state.js.map +1 -1
- package/dist/src/wrappers/app-task.d.ts +1 -0
- package/dist/src/wrappers/app-task.d.ts.map +1 -1
- package/dist/src/wrappers/app-task.js +4 -0
- package/dist/src/wrappers/app-task.js.map +1 -1
- package/dist/vitest.config.d.ts +3 -0
- package/dist/vitest.config.d.ts.map +1 -0
- package/dist/vitest.config.js +16 -0
- package/dist/vitest.config.js.map +1 -0
- package/package.json +9 -3
- package/src/commands/about.ts +2 -2
- package/src/commands/configure.ts +10 -0
- package/src/commands/gen.ts +10 -6
- package/src/commands/new.ts +10 -0
- package/src/index.ts +12 -0
- package/src/services/cache-service.ts +50 -0
- package/src/services/file-service.ts +46 -5
- package/src/services/log-service.ts +14 -1
- package/src/services/project-service.ts +8 -5
- package/src/services/prompt-service.ts +71 -0
- package/src/services/spawn-service.ts +15 -1
- package/src/services/util-service.ts +5 -0
- package/src/tasks/check-env.ts +10 -2
- package/src/tasks/configure.ts +105 -0
- package/src/tasks/create-new-project.ts +66 -0
- package/src/tasks/generate/generate-command.ts +136 -0
- package/src/tasks/generate/generate-service.ts +78 -0
- package/src/tasks/generate/generate-task.ts +79 -0
- package/src/tasks/generate/select-gen-target-name.ts +44 -0
- package/src/tasks/generate/select-gen-target.ts +46 -0
- package/src/tasks/{print-about-information.ts → stdout/print-about-information.ts} +2 -2
- package/src/tasks/stdout/print-banner.ts +41 -0
- package/src/tasks/stdout/print-generated-results.ts +37 -0
- package/src/tasks/sync-configuration.ts +129 -0
- package/src/tasks/test-task.ts +20 -0
- package/src/templates/command.ts +2 -2
- package/src/templates/service.ts +1 -1
- package/src/types/process.ts +1 -0
- package/src/types/project.ts +12 -0
- package/src/types/state.ts +7 -1
- package/src/wrappers/app-task.ts +5 -0
- package/tests/index.spec.ts +8 -0
- package/tests/setup.ts +1 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +14 -0
- package/dist/src/tasks/generate-command.d.ts +0 -23
- package/dist/src/tasks/generate-command.d.ts.map +0 -1
- package/dist/src/tasks/generate-command.js +0 -91
- package/dist/src/tasks/generate-command.js.map +0 -1
- package/dist/src/tasks/generate-service.d.ts +0 -26
- package/dist/src/tasks/generate-service.d.ts.map +0 -1
- package/dist/src/tasks/generate-service.js.map +0 -1
- package/dist/src/tasks/generate-task.d.ts +0 -26
- package/dist/src/tasks/generate-task.d.ts.map +0 -1
- package/dist/src/tasks/generate-task.js.map +0 -1
- package/dist/src/tasks/print-about-information.d.ts.map +0 -1
- package/dist/src/tasks/print-about-information.js.map +0 -1
- package/dist/src/tasks/print-banner.d.ts +0 -12
- package/dist/src/tasks/print-banner.d.ts.map +0 -1
- package/dist/src/tasks/print-banner.js.map +0 -1
- package/dist/src/tasks/print-generated-results.d.ts.map +0 -1
- package/dist/src/tasks/print-generated-results.js.map +0 -1
- package/dist/src/tasks/select-gen-target.d.ts +0 -9
- package/dist/src/tasks/select-gen-target.d.ts.map +0 -1
- package/dist/src/tasks/select-gen-target.js +0 -44
- package/dist/src/tasks/select-gen-target.js.map +0 -1
- package/src/tasks/generate-command.ts +0 -87
- package/src/tasks/generate-service.ts +0 -82
- package/src/tasks/generate-task.ts +0 -83
- package/src/tasks/print-banner.ts +0 -37
- package/src/tasks/print-generated-results.ts +0 -36
- package/src/tasks/select-gen-target.ts +0 -29
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { autoInjectable } from "tsyringe";
|
|
2
|
+
import { UtilService } from "./util-service";
|
|
3
|
+
|
|
4
|
+
import sortableCheckbox from "inquirer-sortable-checkbox";
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
7
|
+
const { search, input } = require("@inquirer/prompts");
|
|
8
|
+
|
|
9
|
+
@autoInjectable()
|
|
10
|
+
export class PromptService {
|
|
11
|
+
constructor(private utilService: UtilService) {}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Select an option from a list of choices. Allows search filtering.
|
|
15
|
+
* See: https://github.com/SBoudrias/Inquirer.js/tree/main/packages/search
|
|
16
|
+
*
|
|
17
|
+
* @param message the message to prompt (question to ask).
|
|
18
|
+
* @param options the selectable choices to search / choose from.
|
|
19
|
+
* @returns the selected choice value
|
|
20
|
+
*/
|
|
21
|
+
async select(message: string, options: string[]) {
|
|
22
|
+
// wait for logging to catch up before prompting
|
|
23
|
+
await this.utilService.sleep(800);
|
|
24
|
+
|
|
25
|
+
const choices = options.map((opt) => ({ value: opt }));
|
|
26
|
+
|
|
27
|
+
const answer = await search({
|
|
28
|
+
message,
|
|
29
|
+
source: async (input: string | void) => {
|
|
30
|
+
if (!input) {
|
|
31
|
+
return choices;
|
|
32
|
+
}
|
|
33
|
+
return choices.filter((c) =>
|
|
34
|
+
c.value.toLowerCase().includes(input.toLowerCase()),
|
|
35
|
+
);
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return answer;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get a simple input string from the user.
|
|
44
|
+
*
|
|
45
|
+
* @param message the message to prompt the user with
|
|
46
|
+
* @param required whether or not to throw an error on empty input.
|
|
47
|
+
* @returns the read input string or undefined if not required
|
|
48
|
+
*/
|
|
49
|
+
async getInput(message: string, required: boolean = true) {
|
|
50
|
+
await this.utilService.sleep(800);
|
|
51
|
+
return await input({ message, required });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Prompt with multi-select options returning the array of selections.
|
|
56
|
+
*
|
|
57
|
+
* @param message the message string to prompt with.
|
|
58
|
+
* @param options the options to choose from.
|
|
59
|
+
* @returns the selected optoins array
|
|
60
|
+
*/
|
|
61
|
+
async pickMultiple<T>(
|
|
62
|
+
message: string,
|
|
63
|
+
options: { name: string; value: T }[],
|
|
64
|
+
): Promise<T[]> {
|
|
65
|
+
await this.utilService.sleep(800);
|
|
66
|
+
return await sortableCheckbox({
|
|
67
|
+
message,
|
|
68
|
+
choices: options,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -4,6 +4,15 @@ import { singleton } from "tsyringe";
|
|
|
4
4
|
|
|
5
5
|
@singleton()
|
|
6
6
|
export class SpawnService {
|
|
7
|
+
execSyncFromDir(command: string, dir: string) {
|
|
8
|
+
try {
|
|
9
|
+
return childProcess.execSync(command, { cwd: dir }).toString();
|
|
10
|
+
} catch (error) {
|
|
11
|
+
console.error(`Error executing command: ${command}`);
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
7
16
|
execSync(command: string) {
|
|
8
17
|
try {
|
|
9
18
|
return childProcess.execSync(command).toString();
|
|
@@ -15,7 +24,12 @@ export class SpawnService {
|
|
|
15
24
|
|
|
16
25
|
exec(command: string) {
|
|
17
26
|
try {
|
|
18
|
-
return
|
|
27
|
+
return new Promise((res, rej) => {
|
|
28
|
+
return childProcess.exec(command, (err, stdout, stderr) => {
|
|
29
|
+
if (err) return rej(err);
|
|
30
|
+
return res({ stdout, stderr });
|
|
31
|
+
});
|
|
32
|
+
});
|
|
19
33
|
} catch (error) {
|
|
20
34
|
console.error(`Error executing command: ${command}`, error);
|
|
21
35
|
throw error;
|
package/src/tasks/check-env.ts
CHANGED
|
@@ -2,7 +2,7 @@ import chalk from "chalk";
|
|
|
2
2
|
import { AppTask } from "../wrappers/app-task";
|
|
3
3
|
import { autoInjectable } from "tsyringe";
|
|
4
4
|
import { EnvironmentConfigKeys } from "../types/state";
|
|
5
|
-
import FileService from "../services/file-service";
|
|
5
|
+
import { FileService } from "../services/file-service";
|
|
6
6
|
import path from "path";
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -20,6 +20,7 @@ export default class CheckEnvironment extends AppTask {
|
|
|
20
20
|
EnvironmentConfigKeys.PINO_LOG_DIR_PATH,
|
|
21
21
|
EnvironmentConfigKeys.PINO_LOG_FILENAME,
|
|
22
22
|
EnvironmentConfigKeys.PINO_LOG_LEVEL,
|
|
23
|
+
EnvironmentConfigKeys.TSSC_SILENCE_BANNER,
|
|
23
24
|
];
|
|
24
25
|
|
|
25
26
|
constructor(private fileService: FileService) {
|
|
@@ -48,7 +49,8 @@ export default class CheckEnvironment extends AppTask {
|
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
51
|
* Check for the folders we need to generate files in. Add errors
|
|
51
|
-
* when no target directories can be found.
|
|
52
|
+
* when no target directories can be found. Saves project data to
|
|
53
|
+
* state so we don't have to refetch it later.
|
|
52
54
|
*
|
|
53
55
|
* @param projectDir the target project root directory path
|
|
54
56
|
* @param errors string array error messages are added to
|
|
@@ -65,6 +67,12 @@ export default class CheckEnvironment extends AppTask {
|
|
|
65
67
|
errors.push(`Unable to find any ${k} in project`);
|
|
66
68
|
}
|
|
67
69
|
});
|
|
70
|
+
this.setData({
|
|
71
|
+
project: {
|
|
72
|
+
rootDir: projectDir,
|
|
73
|
+
...directories,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
/**
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { AppTask } from "../wrappers/app-task";
|
|
3
|
+
import { autoInjectable } from "tsyringe";
|
|
4
|
+
import { GenTargetType } from "../types/state";
|
|
5
|
+
import { ProjectConfig, ProjectData } from "../types/project";
|
|
6
|
+
import { PromptService } from "../services/prompt-service";
|
|
7
|
+
import { CacheService } from "../services/cache-service";
|
|
8
|
+
import { UtilService } from "../services/util-service";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Configures the Project Settings Cache in a Proactive way.
|
|
12
|
+
* Prompts for each resource type destination if not
|
|
13
|
+
* yet configured and multiple directories are found.
|
|
14
|
+
*/
|
|
15
|
+
@autoInjectable()
|
|
16
|
+
export default class ConfigureCache extends AppTask {
|
|
17
|
+
loggerName = "Configure";
|
|
18
|
+
|
|
19
|
+
constructor(
|
|
20
|
+
private promptService: PromptService,
|
|
21
|
+
private cacheService: CacheService,
|
|
22
|
+
private utilService: UtilService,
|
|
23
|
+
) {
|
|
24
|
+
super();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Configure each resource type's output destination directory. Allows
|
|
29
|
+
* the user to reset these values if their project has changed since
|
|
30
|
+
* last cached.
|
|
31
|
+
*/
|
|
32
|
+
async run() {
|
|
33
|
+
this.logger.info(chalk.blueBright("Checking Configuration"));
|
|
34
|
+
const project: ProjectConfig & ProjectData = this.state.data.project!;
|
|
35
|
+
|
|
36
|
+
project.commandDestination = await this.getDestination(
|
|
37
|
+
GenTargetType.Command,
|
|
38
|
+
);
|
|
39
|
+
project.serviceDestination = await this.getDestination(
|
|
40
|
+
GenTargetType.Service,
|
|
41
|
+
);
|
|
42
|
+
project.taskDestination = await this.getDestination(GenTargetType.Task);
|
|
43
|
+
|
|
44
|
+
this.logger.debug(chalk.blackBright(`Saving configuration`));
|
|
45
|
+
await this.cacheService.writeProject(project);
|
|
46
|
+
this.setData({ project });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Prints the target destination path string
|
|
51
|
+
*
|
|
52
|
+
* @param dest the destination path to log
|
|
53
|
+
* @returns
|
|
54
|
+
*/
|
|
55
|
+
logDestination = (dest: string = "") =>
|
|
56
|
+
this.logger.debug(`Destination: ${chalk.magentaBright(dest)}`);
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Gets the destination path for generating resources by resource type.
|
|
60
|
+
* If only one resource destination folder was found it is returned
|
|
61
|
+
* otherwise the user is prompted to select one from the found folders.
|
|
62
|
+
*
|
|
63
|
+
* @param keyType the type of destination to resolve
|
|
64
|
+
* @returns the path of the resolved destination
|
|
65
|
+
*/
|
|
66
|
+
async getDestination(keyType: "command" | "task" | "service") {
|
|
67
|
+
const foldersKey: keyof typeof this.state.data.project =
|
|
68
|
+
`${keyType}Folders` as keyof typeof this.state.data.project;
|
|
69
|
+
|
|
70
|
+
const nonTestFolders: string[] = (
|
|
71
|
+
this.state.data.project![foldersKey] as string[]
|
|
72
|
+
).filter((folder: string) => !(folder || "").includes("/tests/"));
|
|
73
|
+
|
|
74
|
+
// if we only have one folder found, use this as the destination
|
|
75
|
+
const titleizedKey = chalk.blueBright(this.utilService.titleize(keyType));
|
|
76
|
+
if (nonTestFolders.length === 1) {
|
|
77
|
+
this.logger.debug(`Configuring single found ${titleizedKey} folder`);
|
|
78
|
+
return nonTestFolders[0] || "";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// otherwise prompt to pick one
|
|
82
|
+
await this.logger.flush();
|
|
83
|
+
const userSelection: string = await this.promptService.select(
|
|
84
|
+
`Multiple folders found. Select correct ${titleizedKey} folder`,
|
|
85
|
+
nonTestFolders,
|
|
86
|
+
);
|
|
87
|
+
return userSelection;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//@override
|
|
91
|
+
async preRun() {
|
|
92
|
+
this.verifyState();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Check we have all the required state set
|
|
97
|
+
*/
|
|
98
|
+
verifyState() {
|
|
99
|
+
if (!this.state.data.project || !this.state.data.project.rootDir) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Missing project rootDir! ${this.constructor.name} requires the CheckEnvironment task to be ran first.`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { SpawnService } from "../services/spawn-service";
|
|
2
|
+
import { FileService } from "../services/file-service";
|
|
3
|
+
import { PromptService } from "../services/prompt-service";
|
|
4
|
+
import { CLIOptions } from "../types/process";
|
|
5
|
+
import { AppTask } from "../wrappers/app-task";
|
|
6
|
+
import { autoInjectable } from "tsyringe";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a New Task Script Support Project
|
|
10
|
+
*/
|
|
11
|
+
@autoInjectable()
|
|
12
|
+
export default class CreateNewProject extends AppTask {
|
|
13
|
+
loggerName = "Create New Project";
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
private fileService: FileService,
|
|
17
|
+
private spawnService: SpawnService,
|
|
18
|
+
private promptService: PromptService,
|
|
19
|
+
) {
|
|
20
|
+
super();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Generates a new project. Updates state with outputDestination of the
|
|
25
|
+
* newly generated project path.
|
|
26
|
+
*/
|
|
27
|
+
async run() {
|
|
28
|
+
// get path of the templater
|
|
29
|
+
const templaterFullPath = this.fileService.join(
|
|
30
|
+
__dirname,
|
|
31
|
+
"../../assets/yargs-template/task-runner/templater.sh",
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// get execution directory
|
|
35
|
+
const runnerDir = this.fileService.getRunnerDir();
|
|
36
|
+
|
|
37
|
+
// get project name user input
|
|
38
|
+
const projectName: string = await this.getProjectName();
|
|
39
|
+
|
|
40
|
+
// execute the templater from the runner directory and provide given name
|
|
41
|
+
const shCmd = `bash ${templaterFullPath} ${projectName}`;
|
|
42
|
+
this.spawnService.execSyncFromDir(shCmd, runnerDir);
|
|
43
|
+
|
|
44
|
+
this.setData({
|
|
45
|
+
outputDestination: this.fileService.join(runnerDir, projectName),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Resolves the project name input from args or prompt.
|
|
51
|
+
*
|
|
52
|
+
* @returns the project name string
|
|
53
|
+
*/
|
|
54
|
+
async getProjectName(): Promise<string> {
|
|
55
|
+
let projectName: string;
|
|
56
|
+
if (this.argService.hasFlag(CLIOptions.name)) {
|
|
57
|
+
projectName = this.argService.getOption<string>(CLIOptions.name);
|
|
58
|
+
} else {
|
|
59
|
+
projectName = await this.promptService.getInput("Enter Project Name");
|
|
60
|
+
}
|
|
61
|
+
if (!projectName) {
|
|
62
|
+
throw new Error("Unable to resolve projectName");
|
|
63
|
+
}
|
|
64
|
+
return projectName;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { AppTask } from "../../wrappers/app-task";
|
|
3
|
+
import { autoInjectable } from "tsyringe";
|
|
4
|
+
import { AppState, GenTargetType } from "../../types/state";
|
|
5
|
+
import { FileService } from "../../services/file-service";
|
|
6
|
+
import { UtilService } from "../../services/util-service";
|
|
7
|
+
import { CaseType } from "../../types/format";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { getCommandTemplate } from "../../templates/command";
|
|
10
|
+
import { ProjectService } from "../../services/project-service";
|
|
11
|
+
import { PromptService } from "../../services/prompt-service";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Generates a new command class
|
|
15
|
+
*/
|
|
16
|
+
@autoInjectable()
|
|
17
|
+
export default class GenerateCommand extends AppTask {
|
|
18
|
+
loggerName = "Generate Command";
|
|
19
|
+
|
|
20
|
+
constructor(
|
|
21
|
+
private fileService: FileService,
|
|
22
|
+
private utilService: UtilService,
|
|
23
|
+
private projectService: ProjectService,
|
|
24
|
+
private promptService: PromptService,
|
|
25
|
+
) {
|
|
26
|
+
super();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Generates a new command class file.
|
|
31
|
+
*
|
|
32
|
+
* @returns updated state with outputDestination set to generated result path.
|
|
33
|
+
*/
|
|
34
|
+
async run(): Promise<void | Partial<AppState>> {
|
|
35
|
+
const { utilService, fileService, projectService } = this;
|
|
36
|
+
|
|
37
|
+
if (this.state.data.genTargetType !== GenTargetType.Command) {
|
|
38
|
+
return; // no-op
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.logger.info(chalk.blueBright("Generating Command"));
|
|
42
|
+
const targetName = this.state.data.genTargetName!;
|
|
43
|
+
const targetDirectory = this.state.data.project!.commandDestination!;
|
|
44
|
+
|
|
45
|
+
// detect case between commands and task files
|
|
46
|
+
const convention = projectService.getConvention();
|
|
47
|
+
this.logger.debug(`Using convention: ${chalk.magentaBright(convention)}`);
|
|
48
|
+
|
|
49
|
+
const className = utilService.titleizedToCase(
|
|
50
|
+
targetName,
|
|
51
|
+
CaseType.PASCAL_CASE,
|
|
52
|
+
);
|
|
53
|
+
const filename = `${utilService.titleizedToCase(targetName, convention)}${ProjectService.defaults.extention}`;
|
|
54
|
+
|
|
55
|
+
const taskMappings = new Map();
|
|
56
|
+
await this.addTasksToMap(taskMappings);
|
|
57
|
+
const rendered = getCommandTemplate(className, taskMappings);
|
|
58
|
+
const destination = path.join(targetDirectory, filename);
|
|
59
|
+
|
|
60
|
+
// write contents
|
|
61
|
+
fileService.writeFile(destination, rendered);
|
|
62
|
+
return { data: { outputDestination: destination } };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Checks state for required fields. Executes prior to run method.
|
|
67
|
+
* Skips validation if genTargetType is not set to command.
|
|
68
|
+
*/
|
|
69
|
+
async preRun() {
|
|
70
|
+
if (this.state.data.genTargetType !== GenTargetType.Command) {
|
|
71
|
+
return; // no validation on no-op flow
|
|
72
|
+
}
|
|
73
|
+
if (!this.state.data.genTargetName) {
|
|
74
|
+
throw new Error("Unable to determine targetName");
|
|
75
|
+
}
|
|
76
|
+
if (!this.state.data.project) {
|
|
77
|
+
throw new Error("Unable to determine project");
|
|
78
|
+
}
|
|
79
|
+
if (!this.state.data.project.commandDestination) {
|
|
80
|
+
throw new Error("Unable to determine targetDirectory");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Prompt to select tasks to use in the command being generated.
|
|
86
|
+
*
|
|
87
|
+
* @param taskMappings the map to add picked tasks to import for the command
|
|
88
|
+
*/
|
|
89
|
+
async addTasksToMap(taskMappings: Map<string, string>) {
|
|
90
|
+
if (!this.state.data.project?.taskDestination) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const taskFiles = this.fileService.getFilesInDir(
|
|
95
|
+
this.state.data.project.taskDestination,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
this.logger.debug(`Found ${taskFiles.length} task files`);
|
|
99
|
+
await this.logger.flush();
|
|
100
|
+
|
|
101
|
+
// prompt for tasks to use in the new command
|
|
102
|
+
const pickedTasks = await this.promptService.pickMultiple(
|
|
103
|
+
"Include Tasks",
|
|
104
|
+
taskFiles.map((tf) => ({
|
|
105
|
+
name: this.utilService.titleizedToCase(
|
|
106
|
+
this.utilService.titleizeAll(this.fileService.getFilenameNoExt(tf)),
|
|
107
|
+
CaseType.PASCAL_CASE,
|
|
108
|
+
),
|
|
109
|
+
value: tf,
|
|
110
|
+
})),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
this.logger.debug(`Selected ${pickedTasks.length} tasks`);
|
|
114
|
+
if (!pickedTasks.length) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// add them to the map
|
|
119
|
+
for (const picked of pickedTasks) {
|
|
120
|
+
const filename = this.fileService.getFilenameNoExt(picked);
|
|
121
|
+
const taskClassName = this.utilService.titleizedToCase(
|
|
122
|
+
this.utilService.titleizeAll(filename),
|
|
123
|
+
CaseType.PASCAL_CASE,
|
|
124
|
+
);
|
|
125
|
+
const relativeSource = this.state.data.project!.commandDestination!;
|
|
126
|
+
const relativeDest = picked;
|
|
127
|
+
taskMappings.set(
|
|
128
|
+
taskClassName,
|
|
129
|
+
this.fileService
|
|
130
|
+
.toRelativePath(relativeSource, relativeDest)
|
|
131
|
+
// remove extension for typescript import
|
|
132
|
+
.replace(ProjectService.defaults.extention, ""),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { AppTask } from "../../wrappers/app-task";
|
|
3
|
+
import { autoInjectable } from "tsyringe";
|
|
4
|
+
import { GenTargetType } from "../../types/state";
|
|
5
|
+
import { ProjectService } from "../../services/project-service";
|
|
6
|
+
import { UtilService } from "../../services/util-service";
|
|
7
|
+
import { FileService } from "../../services/file-service";
|
|
8
|
+
import { CaseType } from "../../types/format";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { getServiceTemplate } from "../../templates/service";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Generates a new service class
|
|
14
|
+
*/
|
|
15
|
+
@autoInjectable()
|
|
16
|
+
export default class GenerateService extends AppTask {
|
|
17
|
+
loggerName = "Generate Service";
|
|
18
|
+
|
|
19
|
+
constructor(
|
|
20
|
+
private projectService: ProjectService,
|
|
21
|
+
private utilService: UtilService,
|
|
22
|
+
private fileService: FileService,
|
|
23
|
+
) {
|
|
24
|
+
super();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Generates a new service class file.
|
|
29
|
+
*
|
|
30
|
+
* @returns updated state with outputDestination set to generated result path.
|
|
31
|
+
*/
|
|
32
|
+
async run() {
|
|
33
|
+
if (this.state.data.genTargetType !== GenTargetType.Service) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
this.logger.info(chalk.blueBright("Generating Service"));
|
|
38
|
+
const { utilService, projectService } = this;
|
|
39
|
+
|
|
40
|
+
const targetName = this.state.data.genTargetName!;
|
|
41
|
+
const targetDirectory = this.state.data.project!.serviceDestination!;
|
|
42
|
+
|
|
43
|
+
// detect case
|
|
44
|
+
const convention = projectService.getConvention();
|
|
45
|
+
this.logger.debug(`Using convention: ${chalk.magentaBright(convention)}`);
|
|
46
|
+
const className = utilService.titleizedToCase(
|
|
47
|
+
targetName,
|
|
48
|
+
CaseType.PASCAL_CASE,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const rendered = getServiceTemplate(className);
|
|
52
|
+
const filename = `${utilService.titleizedToCase(targetName, convention)}${ProjectService.defaults.extention}`;
|
|
53
|
+
const destination = path.join(targetDirectory, filename);
|
|
54
|
+
|
|
55
|
+
// write contents
|
|
56
|
+
this.fileService.writeFile(destination, rendered);
|
|
57
|
+
return { data: { outputDestination: destination } };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Checks state for required fields. Executes prior to run method.
|
|
62
|
+
* Skips validation if genTargetType is not set to service.
|
|
63
|
+
*/
|
|
64
|
+
async preRun() {
|
|
65
|
+
if (this.state.data.genTargetType !== GenTargetType.Service) {
|
|
66
|
+
return; // no validation on no-op flow
|
|
67
|
+
}
|
|
68
|
+
if (!this.state.data.genTargetName) {
|
|
69
|
+
throw new Error("Unable to determine targetName");
|
|
70
|
+
}
|
|
71
|
+
if (!this.state.data.project) {
|
|
72
|
+
throw new Error("Unable to determine project");
|
|
73
|
+
}
|
|
74
|
+
if (!this.state.data.project.serviceDestination) {
|
|
75
|
+
throw new Error("Unable to determine targetDirectory");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { AppTask } from "../../wrappers/app-task";
|
|
3
|
+
import { autoInjectable } from "tsyringe";
|
|
4
|
+
import { GenTargetType } from "../../types/state";
|
|
5
|
+
import { getTaskTemplate } from "../../templates/task";
|
|
6
|
+
import { CaseType } from "../../types/format";
|
|
7
|
+
import { UtilService } from "../../services/util-service";
|
|
8
|
+
import { FileService } from "../../services/file-service";
|
|
9
|
+
import { ProjectService } from "../../services/project-service";
|
|
10
|
+
import path from "path";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Generates a new task class
|
|
14
|
+
*/
|
|
15
|
+
@autoInjectable()
|
|
16
|
+
export default class GenerateTask extends AppTask {
|
|
17
|
+
loggerName = "Generate Task";
|
|
18
|
+
|
|
19
|
+
constructor(
|
|
20
|
+
private utilService: UtilService,
|
|
21
|
+
private fileService: FileService,
|
|
22
|
+
private projectService: ProjectService,
|
|
23
|
+
) {
|
|
24
|
+
super();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Generates a new task class file.
|
|
29
|
+
*
|
|
30
|
+
* @returns updated state with outputDestination set to generated result path.
|
|
31
|
+
*/
|
|
32
|
+
async run() {
|
|
33
|
+
if (this.state.data.genTargetType !== GenTargetType.Task) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const { utilService } = this;
|
|
38
|
+
|
|
39
|
+
this.logger.info(chalk.blueBright("Generating Task"));
|
|
40
|
+
|
|
41
|
+
const targetName = this.state.data.genTargetName!;
|
|
42
|
+
const targetDirectory = this.state.data.project!.taskDestination!;
|
|
43
|
+
|
|
44
|
+
// detect case
|
|
45
|
+
const convention = this.projectService.getConvention();
|
|
46
|
+
this.logger.debug(`Using convention: ${chalk.magentaBright(convention)}`);
|
|
47
|
+
const className = utilService.titleizedToCase(
|
|
48
|
+
targetName,
|
|
49
|
+
CaseType.PASCAL_CASE,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const rendered = getTaskTemplate(className);
|
|
53
|
+
const filename = `${utilService.titleizedToCase(targetName, convention)}${ProjectService.defaults.extention}`;
|
|
54
|
+
const destination = path.join(targetDirectory, filename);
|
|
55
|
+
|
|
56
|
+
// write contents
|
|
57
|
+
this.fileService.writeFile(destination, rendered);
|
|
58
|
+
return { data: { outputDestination: destination } };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Checks state for required fields. Executes prior to run method.
|
|
63
|
+
* Skips validation if genTargetType is not set to task.
|
|
64
|
+
*/
|
|
65
|
+
async preRun() {
|
|
66
|
+
if (this.state.data.genTargetType !== GenTargetType.Task) {
|
|
67
|
+
return; // no validation on no-op flow
|
|
68
|
+
}
|
|
69
|
+
if (!this.state.data.genTargetName) {
|
|
70
|
+
throw new Error("Unable to determine targetName");
|
|
71
|
+
}
|
|
72
|
+
if (!this.state.data.project) {
|
|
73
|
+
throw new Error("Unable to determine project");
|
|
74
|
+
}
|
|
75
|
+
if (!this.state.data.project.taskDestination) {
|
|
76
|
+
throw new Error("Unable to determine targetDirectory");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { AppTask } from "../../wrappers/app-task";
|
|
3
|
+
import { autoInjectable } from "tsyringe";
|
|
4
|
+
import { PromptService } from "../../services/prompt-service";
|
|
5
|
+
import { ProjectService } from "../../services/project-service";
|
|
6
|
+
import { UtilService } from "../../services/util-service";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* SelectGenTargetName
|
|
10
|
+
*/
|
|
11
|
+
@autoInjectable()
|
|
12
|
+
export default class SelectGenTargetName extends AppTask {
|
|
13
|
+
loggerName = "SelectGenTargetName";
|
|
14
|
+
constructor(
|
|
15
|
+
private promptService: PromptService,
|
|
16
|
+
private utilService: UtilService,
|
|
17
|
+
) {
|
|
18
|
+
super();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async run() {
|
|
22
|
+
this.logger.info(chalk.blueBright("Checking Target Name"));
|
|
23
|
+
|
|
24
|
+
let targetName = this.argService.getTargetName();
|
|
25
|
+
if (!targetName) {
|
|
26
|
+
targetName =
|
|
27
|
+
(await this.promptService.getInput(
|
|
28
|
+
`Enter Name for ${chalk.blueBright(this.utilService.titleize(this.state.data.genTargetType!))}`,
|
|
29
|
+
)) || ProjectService.defaults.targetName;
|
|
30
|
+
}
|
|
31
|
+
if (!targetName) {
|
|
32
|
+
throw new Error("Unable to resolve targetName");
|
|
33
|
+
}
|
|
34
|
+
this.logger.debug(`Name selected: ${chalk.magentaBright(targetName)}`);
|
|
35
|
+
targetName = this.utilService.titleizeAll(targetName);
|
|
36
|
+
this.setData({ genTargetName: targetName });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async preRun() {
|
|
40
|
+
if (!this.state.data.genTargetType) {
|
|
41
|
+
throw new Error("Unable to determine genTargetType");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|