task-script-support-cli 0.1.1 → 0.2.3

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 (192) hide show
  1. package/assets/yargs-template/task-runner/.prettierignore +3 -0
  2. package/assets/yargs-template/task-runner/.prettierrc +1 -0
  3. package/assets/yargs-template/task-runner/.vscode/launch.json +34 -0
  4. package/assets/yargs-template/task-runner/eslint.config.ts +11 -0
  5. package/assets/yargs-template/task-runner/install-link.sh +21 -0
  6. package/assets/yargs-template/task-runner/package-lock.json +3387 -0
  7. package/assets/yargs-template/task-runner/package.json +51 -0
  8. package/assets/yargs-template/task-runner/src/commands/verify.ts +10 -0
  9. package/assets/yargs-template/task-runner/src/index.ts +46 -0
  10. package/assets/yargs-template/task-runner/src/services/banner-service.ts +38 -0
  11. package/assets/yargs-template/task-runner/src/services/log-service.ts +48 -0
  12. package/assets/yargs-template/task-runner/src/services/spawn-service.ts +87 -0
  13. package/assets/yargs-template/task-runner/src/services/util-service.ts +28 -0
  14. package/assets/yargs-template/task-runner/src/tasks/check-env.ts +49 -0
  15. package/assets/yargs-template/task-runner/src/tasks/log-state.ts +22 -0
  16. package/assets/yargs-template/task-runner/src/tasks/print-banner.ts +73 -0
  17. package/assets/yargs-template/task-runner/src/types/process.ts +9 -0
  18. package/assets/yargs-template/task-runner/src/types/state.ts +31 -0
  19. package/assets/yargs-template/task-runner/src/wrappers/app-task.ts +42 -0
  20. package/assets/yargs-template/task-runner/src/wrappers/command.ts +20 -0
  21. package/assets/yargs-template/task-runner/src/wrappers/dependencies.ts +24 -0
  22. package/assets/yargs-template/task-runner/task-runner +3 -0
  23. package/assets/yargs-template/task-runner/templater.sh +57 -0
  24. package/assets/yargs-template/task-runner/tests/commands/verify.test.ts +42 -0
  25. package/assets/yargs-template/task-runner/tests/services/spawn-service.test.ts +132 -0
  26. package/assets/yargs-template/task-runner/tests/services/util-service.test.ts +36 -0
  27. package/assets/yargs-template/task-runner/tests/setup.ts +1 -0
  28. package/assets/yargs-template/task-runner/tests/tasks/check-env.test.ts +30 -0
  29. package/assets/yargs-template/task-runner/tsconfig.json +27 -0
  30. package/assets/yargs-template/task-runner/vitest.config.ts +14 -0
  31. package/dist/assets/yargs-template/task-runner/.prettierignore +3 -0
  32. package/dist/assets/yargs-template/task-runner/.prettierrc +1 -0
  33. package/dist/assets/yargs-template/task-runner/.vscode/launch.json +34 -0
  34. package/dist/assets/yargs-template/task-runner/eslint.config.ts +11 -0
  35. package/dist/assets/yargs-template/task-runner/install-link.sh +21 -0
  36. package/dist/assets/yargs-template/task-runner/package-lock.json +3387 -0
  37. package/dist/assets/yargs-template/task-runner/package.json +51 -0
  38. package/dist/assets/yargs-template/task-runner/src/commands/verify.ts +10 -0
  39. package/dist/assets/yargs-template/task-runner/src/index.ts +46 -0
  40. package/dist/assets/yargs-template/task-runner/src/services/banner-service.ts +38 -0
  41. package/dist/assets/yargs-template/task-runner/src/services/log-service.ts +48 -0
  42. package/dist/assets/yargs-template/task-runner/src/services/spawn-service.ts +87 -0
  43. package/dist/assets/yargs-template/task-runner/src/services/util-service.ts +28 -0
  44. package/dist/assets/yargs-template/task-runner/src/tasks/check-env.ts +49 -0
  45. package/dist/assets/yargs-template/task-runner/src/tasks/log-state.ts +22 -0
  46. package/dist/assets/yargs-template/task-runner/src/tasks/print-banner.ts +73 -0
  47. package/dist/assets/yargs-template/task-runner/src/types/process.ts +9 -0
  48. package/dist/assets/yargs-template/task-runner/src/types/state.ts +31 -0
  49. package/dist/assets/yargs-template/task-runner/src/wrappers/app-task.ts +42 -0
  50. package/dist/assets/yargs-template/task-runner/src/wrappers/command.ts +20 -0
  51. package/dist/assets/yargs-template/task-runner/src/wrappers/dependencies.ts +24 -0
  52. package/dist/assets/yargs-template/task-runner/task-runner +3 -0
  53. package/dist/assets/yargs-template/task-runner/templater.sh +57 -0
  54. package/dist/assets/yargs-template/task-runner/tests/commands/verify.test.ts +42 -0
  55. package/dist/assets/yargs-template/task-runner/tests/services/spawn-service.test.ts +132 -0
  56. package/dist/assets/yargs-template/task-runner/tests/services/util-service.test.ts +36 -0
  57. package/dist/assets/yargs-template/task-runner/tests/setup.ts +1 -0
  58. package/dist/assets/yargs-template/task-runner/tests/tasks/check-env.test.ts +30 -0
  59. package/dist/assets/yargs-template/task-runner/tsconfig.json +27 -0
  60. package/dist/assets/yargs-template/task-runner/vitest.config.ts +14 -0
  61. package/dist/package.json +8 -3
  62. package/dist/src/commands/gen.d.ts +3 -1
  63. package/dist/src/commands/gen.d.ts.map +1 -1
  64. package/dist/src/commands/gen.js +4 -0
  65. package/dist/src/commands/gen.js.map +1 -1
  66. package/dist/src/commands/new.d.ts +8 -0
  67. package/dist/src/commands/new.d.ts.map +1 -0
  68. package/dist/src/commands/new.js +25 -0
  69. package/dist/src/commands/new.js.map +1 -0
  70. package/dist/src/index.js +6 -0
  71. package/dist/src/index.js.map +1 -1
  72. package/dist/src/services/cache-service.d.ts +11 -0
  73. package/dist/src/services/cache-service.d.ts.map +1 -0
  74. package/dist/src/services/cache-service.js +64 -0
  75. package/dist/src/services/cache-service.js.map +1 -0
  76. package/dist/src/services/file-service.d.ts +5 -1
  77. package/dist/src/services/file-service.d.ts.map +1 -1
  78. package/dist/src/services/file-service.js +16 -2
  79. package/dist/src/services/file-service.js.map +1 -1
  80. package/dist/src/services/log-service.d.ts.map +1 -1
  81. package/dist/src/services/log-service.js +7 -1
  82. package/dist/src/services/log-service.js.map +1 -1
  83. package/dist/src/services/project-service.d.ts +2 -2
  84. package/dist/src/services/project-service.d.ts.map +1 -1
  85. package/dist/src/services/project-service.js +5 -7
  86. package/dist/src/services/project-service.js.map +1 -1
  87. package/dist/src/services/prompt-service.d.ts +27 -0
  88. package/dist/src/services/prompt-service.d.ts.map +1 -0
  89. package/dist/src/services/prompt-service.js +67 -0
  90. package/dist/src/services/prompt-service.js.map +1 -0
  91. package/dist/src/services/spawn-service.d.ts +2 -1
  92. package/dist/src/services/spawn-service.d.ts.map +1 -1
  93. package/dist/src/services/spawn-service.js +16 -1
  94. package/dist/src/services/spawn-service.js.map +1 -1
  95. package/dist/src/services/util-service.d.ts +2 -0
  96. package/dist/src/services/util-service.d.ts.map +1 -1
  97. package/dist/src/services/util-service.js +4 -0
  98. package/dist/src/services/util-service.js.map +1 -1
  99. package/dist/src/tasks/check-env.d.ts +3 -2
  100. package/dist/src/tasks/check-env.d.ts.map +1 -1
  101. package/dist/src/tasks/check-env.js +11 -3
  102. package/dist/src/tasks/check-env.js.map +1 -1
  103. package/dist/src/tasks/create-new-project.d.ts +26 -0
  104. package/dist/src/tasks/create-new-project.d.ts.map +1 -0
  105. package/dist/src/tasks/create-new-project.js +76 -0
  106. package/dist/src/tasks/create-new-project.js.map +1 -0
  107. package/dist/src/tasks/generate-command.d.ts +18 -6
  108. package/dist/src/tasks/generate-command.d.ts.map +1 -1
  109. package/dist/src/tasks/generate-command.js +66 -29
  110. package/dist/src/tasks/generate-command.js.map +1 -1
  111. package/dist/src/tasks/generate-service.d.ts +10 -6
  112. package/dist/src/tasks/generate-service.d.ts.map +1 -1
  113. package/dist/src/tasks/generate-service.js +27 -26
  114. package/dist/src/tasks/generate-service.js.map +1 -1
  115. package/dist/src/tasks/generate-task.d.ts +10 -6
  116. package/dist/src/tasks/generate-task.d.ts.map +1 -1
  117. package/dist/src/tasks/generate-task.js +27 -26
  118. package/dist/src/tasks/generate-task.js.map +1 -1
  119. package/dist/src/tasks/print-banner.d.ts +3 -3
  120. package/dist/src/tasks/print-banner.d.ts.map +1 -1
  121. package/dist/src/tasks/print-banner.js +12 -8
  122. package/dist/src/tasks/print-banner.js.map +1 -1
  123. package/dist/src/tasks/print-generated-results.d.ts.map +1 -1
  124. package/dist/src/tasks/print-generated-results.js +13 -10
  125. package/dist/src/tasks/print-generated-results.js.map +1 -1
  126. package/dist/src/tasks/select-gen-target-name.d.ts +15 -0
  127. package/dist/src/tasks/select-gen-target-name.d.ts.map +1 -0
  128. package/dist/src/tasks/select-gen-target-name.js +58 -0
  129. package/dist/src/tasks/select-gen-target-name.js.map +1 -0
  130. package/dist/src/tasks/select-gen-target.d.ts +5 -0
  131. package/dist/src/tasks/select-gen-target.d.ts.map +1 -1
  132. package/dist/src/tasks/select-gen-target.js +16 -2
  133. package/dist/src/tasks/select-gen-target.js.map +1 -1
  134. package/dist/src/tasks/sync-configuration.d.ts +34 -0
  135. package/dist/src/tasks/sync-configuration.d.ts.map +1 -0
  136. package/dist/src/tasks/sync-configuration.js +116 -0
  137. package/dist/src/tasks/sync-configuration.js.map +1 -0
  138. package/dist/src/tasks/test-task.d.ts +10 -0
  139. package/dist/src/tasks/test-task.d.ts.map +1 -0
  140. package/dist/src/tasks/test-task.js +36 -0
  141. package/dist/src/tasks/test-task.js.map +1 -0
  142. package/dist/src/templates/command.js +2 -2
  143. package/dist/src/templates/command.js.map +1 -1
  144. package/dist/src/templates/service.js +1 -1
  145. package/dist/src/templates/service.js.map +1 -1
  146. package/dist/src/types/process.d.ts +2 -1
  147. package/dist/src/types/process.d.ts.map +1 -1
  148. package/dist/src/types/process.js +1 -0
  149. package/dist/src/types/process.js.map +1 -1
  150. package/dist/src/types/project.d.ts +12 -0
  151. package/dist/src/types/project.d.ts.map +1 -0
  152. package/dist/src/types/project.js +3 -0
  153. package/dist/src/types/project.js.map +1 -0
  154. package/dist/src/types/state.d.ts +5 -1
  155. package/dist/src/types/state.d.ts.map +1 -1
  156. package/dist/src/types/state.js +1 -0
  157. package/dist/src/types/state.js.map +1 -1
  158. package/dist/vitest.config.d.ts +3 -0
  159. package/dist/vitest.config.d.ts.map +1 -0
  160. package/dist/vitest.config.js +16 -0
  161. package/dist/vitest.config.js.map +1 -0
  162. package/package.json +8 -3
  163. package/src/commands/gen.ts +4 -0
  164. package/src/commands/new.ts +10 -0
  165. package/src/index.ts +7 -0
  166. package/src/services/cache-service.ts +50 -0
  167. package/src/services/file-service.ts +18 -1
  168. package/src/services/log-service.ts +6 -1
  169. package/src/services/project-service.ts +2 -2
  170. package/src/services/prompt-service.ts +60 -0
  171. package/src/services/spawn-service.ts +15 -1
  172. package/src/services/util-service.ts +5 -0
  173. package/src/tasks/check-env.ts +10 -2
  174. package/src/tasks/create-new-project.ts +66 -0
  175. package/src/tasks/generate-command.ts +84 -31
  176. package/src/tasks/generate-service.ts +24 -28
  177. package/src/tasks/generate-task.ts +24 -28
  178. package/src/tasks/print-banner.ts +10 -6
  179. package/src/tasks/print-generated-results.ts +15 -15
  180. package/src/tasks/select-gen-target-name.ts +44 -0
  181. package/src/tasks/select-gen-target.ts +16 -1
  182. package/src/tasks/sync-configuration.ts +121 -0
  183. package/src/tasks/test-task.ts +20 -0
  184. package/src/templates/command.ts +2 -2
  185. package/src/templates/service.ts +1 -1
  186. package/src/types/process.ts +1 -0
  187. package/src/types/project.ts +12 -0
  188. package/src/types/state.ts +7 -1
  189. package/tests/index.spec.ts +8 -0
  190. package/tests/setup.ts +1 -0
  191. package/tsconfig.json +1 -1
  192. package/vitest.config.ts +14 -0
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ UtilService.initializeDependencies();
13
13
 
14
14
  import { AboutCommand } from "./commands/about";
15
15
  import { GenCommand } from "./commands/gen";
16
+ import { NewCommand } from "./commands/new";
16
17
 
17
18
  const { program } = commander;
18
19
  const name = UtilService.getAppName();
@@ -35,4 +36,10 @@ program
35
36
  .option("-t, --task", "Generate a task")
36
37
  .action(container.resolve(GenCommand).handler);
37
38
 
39
+ program
40
+ .command("new")
41
+ .option("-n, --name <name>", "Name of the New Project")
42
+ // .option("-d, --dest", "target destination path")
43
+ .action(container.resolve(NewCommand).handler);
44
+
38
45
  program.parse(process.argv);
@@ -0,0 +1,50 @@
1
+ import nodePersist from "node-persist";
2
+ import { singleton } from "tsyringe";
3
+ import { ProjectConfig } from "../types/project";
4
+ import _ from "lodash";
5
+ import chalk from "chalk";
6
+ import { LogService } from "./log-service";
7
+ import path from "path";
8
+
9
+ @singleton()
10
+ export class CacheService {
11
+ private storageInitialized = false;
12
+ constructor(private logger: LogService) {
13
+ logger.setPrefix(`${this.constructor.name} :: `);
14
+ }
15
+
16
+ async initialize() {
17
+ if (this.storageInitialized) {
18
+ return;
19
+ }
20
+ const appDataFolder =
21
+ process.env.APPDATA ||
22
+ (process.platform === "darwin"
23
+ ? `${process.env.HOME}/Library/Preferences`
24
+ : `${process.env.HOME}/.config`);
25
+
26
+ const location = path.join(appDataFolder, "tssc");
27
+ this.logger.debug(chalk.dim(`Using cache: ${location}`));
28
+
29
+ await nodePersist.init({ dir: location });
30
+ this.storageInitialized = true;
31
+ }
32
+
33
+ async getProject(rootDir: string): Promise<ProjectConfig | null> {
34
+ await this.initialize();
35
+ const project = await nodePersist.getItem(rootDir);
36
+ return project;
37
+ }
38
+
39
+ async writeProject(project: ProjectConfig): Promise<void> {
40
+ await this.initialize();
41
+ const projectKeys: (keyof ProjectConfig)[] = [
42
+ "rootDir",
43
+ "taskDestination",
44
+ "serviceDestination",
45
+ "commandDestination",
46
+ ];
47
+ const cleanedProject: ProjectConfig = _.pick(project, projectKeys);
48
+ await nodePersist.setItem(project.rootDir, cleanedProject);
49
+ }
50
+ }
@@ -6,11 +6,28 @@ import { singleton } from "tsyringe";
6
6
  const ignoredDirectories = new Set(["node_modules", "dist"]);
7
7
 
8
8
  @singleton()
9
- export default class FileService {
9
+ export class FileService {
10
10
  getRunnerDir(): string {
11
11
  return process.cwd();
12
12
  }
13
13
 
14
+ join(path1: string, path2: string) {
15
+ return path.join(path1, path2);
16
+ }
17
+
18
+ toRelativePath(fromPath: string, toPath: string) {
19
+ return path.relative(fromPath, toPath);
20
+ }
21
+
22
+ toFullPath(relativePath: string) {
23
+ return path.resolve(relativePath);
24
+ }
25
+
26
+ getFilenameNoExt(somePath: string) {
27
+ const filename = path.basename(somePath);
28
+ return filename.includes(".") ? filename.split(".")[0] || "" : filename;
29
+ }
30
+
14
31
  getFilesInDir(dirPath: string): string[] {
15
32
  if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory()) {
16
33
  throw new Error(`Directory not found: ${dirPath}`);
@@ -18,7 +18,12 @@ export class LogService {
18
18
  }
19
19
 
20
20
  setPrefix(prefix: string) {
21
- this.logger = this.pinoLogger.getLogger(prefix);
21
+ if (!LogService.ParentInstance) {
22
+ this.logger = this.pinoLogger.getLogger(prefix);
23
+ LogService.ParentInstance = this.logger;
24
+ } else {
25
+ this.logger = LogService.ParentInstance.child({}, { msgPrefix: prefix });
26
+ }
22
27
  }
23
28
 
24
29
  public info(msg: string, obj?: unknown): void {
@@ -1,10 +1,10 @@
1
1
  import { autoInjectable } from "tsyringe";
2
- import FileService from "./file-service";
2
+ import { FileService } from "./file-service";
3
3
  import { CaseType } from "../types/format";
4
4
  import { UtilService } from "./util-service";
5
5
 
6
6
  @autoInjectable()
7
- export default class ProjectService {
7
+ export class ProjectService {
8
8
  static defaults = {
9
9
  targetName: "Foo-Bar-Baz",
10
10
  sampleFile: "test-file.ts",
@@ -0,0 +1,60 @@
1
+ import { autoInjectable } from "tsyringe";
2
+ import { UtilService } from "./util-service";
3
+
4
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
5
+ const { search, input, checkbox } = require("@inquirer/prompts");
6
+
7
+ @autoInjectable()
8
+ export class PromptService {
9
+ constructor(private utilService: UtilService) {}
10
+
11
+ /**
12
+ * Select an option from a list of choices. Allows search filtering.
13
+ * See: https://github.com/SBoudrias/Inquirer.js/tree/main/packages/search
14
+ *
15
+ * @param message the message to prompt (question to ask).
16
+ * @param options the selectable choices to search / choose from.
17
+ * @returns the selected choice value
18
+ */
19
+ async select(message: string, options: string[]) {
20
+ // wait for logging to catch up before prompting
21
+ await this.utilService.sleep(200);
22
+
23
+ const choices = options.map((opt) => ({ value: opt }));
24
+
25
+ const answer = await search({
26
+ message,
27
+ source: async (input: string | void) => {
28
+ if (!input) {
29
+ return choices;
30
+ }
31
+ return choices.filter((c) =>
32
+ c.value.toLowerCase().includes(input.toLowerCase()),
33
+ );
34
+ },
35
+ });
36
+
37
+ return answer;
38
+ }
39
+
40
+ async getInput(message: string, required: boolean = true) {
41
+ return await input({ message, required });
42
+ }
43
+
44
+ /**
45
+ * Prompt with multi-select options returning the array of selections.
46
+ *
47
+ * @param message the message string to prompt with.
48
+ * @param options the options to choose from.
49
+ * @returns the selected optoins array
50
+ */
51
+ async pickMultiple<T>(
52
+ message: string,
53
+ options: { name: string; value: T }[],
54
+ ): Promise<T[]> {
55
+ return await checkbox({
56
+ message,
57
+ choices: options,
58
+ });
59
+ }
60
+ }
@@ -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 childProcess.exec(command).toString();
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;
@@ -84,4 +84,9 @@ export class UtilService {
84
84
 
85
85
  return null;
86
86
  }
87
+
88
+ sleep = (ms: number) => UtilService.sleep(ms);
89
+ static async sleep(ms: number) {
90
+ await new Promise((res) => setTimeout(res, ms));
91
+ }
87
92
  }
@@ -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,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
+ * CreateNewProject
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
+ }
@@ -2,12 +2,13 @@ import chalk from "chalk";
2
2
  import { AppTask } from "../wrappers/app-task";
3
3
  import { autoInjectable } from "tsyringe";
4
4
  import { AppState, GenTargetType } from "../types/state";
5
- import FileService from "../services/file-service";
5
+ import { FileService } from "../services/file-service";
6
6
  import { UtilService } from "../services/util-service";
7
7
  import { CaseType } from "../types/format";
8
8
  import path from "path";
9
9
  import { getCommandTemplate } from "../templates/command";
10
- import ProjectService from "../services/project-service";
10
+ import { ProjectService } from "../services/project-service";
11
+ import { PromptService } from "../services/prompt-service";
11
12
 
12
13
  /**
13
14
  * Generates a new command class
@@ -20,47 +21,40 @@ export default class GenerateCommand extends AppTask {
20
21
  private fileService: FileService,
21
22
  private utilService: UtilService,
22
23
  private projectService: ProjectService,
24
+ private promptService: PromptService,
23
25
  ) {
24
26
  super();
25
27
  }
26
28
 
29
+ /**
30
+ * Generates a new command class file.
31
+ *
32
+ * @returns updated state with outputDestination set to generated result path.
33
+ */
27
34
  async run(): Promise<void | Partial<AppState>> {
28
- const { utilService, fileService, argService, projectService } = this;
35
+ const { utilService, fileService, projectService } = this;
29
36
 
30
37
  if (this.state.data.genTargetType !== GenTargetType.Command) {
31
- return;
38
+ return; // no-op
32
39
  }
33
40
 
34
41
  this.logger.info(chalk.blueBright("Generating Command"));
35
-
36
- let targetName = argService.getTargetName();
37
- if (!targetName) {
38
- // TODO: Should we prompt for target name here?
39
- targetName = argService.cleanTargetName(
40
- ProjectService.defaults.targetName,
41
- );
42
- }
43
-
44
- const targetDirectory = this.getTargetDirectory();
45
- if (!targetDirectory) {
46
- throw new Error("Unable to determine target Directory");
47
- }
42
+ const targetName = this.state.data.genTargetName!;
43
+ const targetDirectory = this.state.data.project!.commandDestination!;
48
44
 
49
45
  // detect case between commands and task files
50
- const taskFiles = projectService.getTaskFiles();
51
46
  const convention = projectService.getConvention();
52
47
  this.logger.debug(`Using convention: ${chalk.magentaBright(convention)}`);
53
48
 
54
- this.logger.debug(`Found ${taskFiles.length} task files`);
55
49
  const className = utilService.titleizedToCase(
56
50
  targetName,
57
51
  CaseType.PASCAL_CASE,
58
52
  );
59
- // TODO: prompt for tasks to use in the new command?
60
- const taskMappings = new Map();
53
+ const filename = `${utilService.titleizedToCase(targetName, convention)}${ProjectService.defaults.extention}`;
61
54
 
55
+ const taskMappings = new Map();
56
+ await this.addTasksToMap(taskMappings);
62
57
  const rendered = getCommandTemplate(className, taskMappings);
63
- const filename = `${utilService.titleizedToCase(targetName, convention)}${ProjectService.defaults.extention}`;
64
58
  const destination = path.join(targetDirectory, filename);
65
59
 
66
60
  // write contents
@@ -69,19 +63,78 @@ export default class GenerateCommand extends AppTask {
69
63
  }
70
64
 
71
65
  /**
72
- * Get the target directory to save the generated file.
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.
73
86
  *
74
- * @returns the destination path of the parent folder to save the new file in.
87
+ * @param taskMappings the map to add picked tasks to import for the command
75
88
  */
76
- getTargetDirectory() {
77
- const runnerRoot = this.fileService.getRunnerRootDir();
78
- const commandFolders = this.fileService.getCommandDirs(runnerRoot);
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
+
100
+ // prompt for tasks to use in the new command
101
+ const pickedTasks = await this.promptService.pickMultiple(
102
+ "Include Tasks",
103
+ taskFiles.map((tf) => ({
104
+ name: this.utilService.titleizedToCase(
105
+ this.utilService.titleizeAll(this.fileService.getFilenameNoExt(tf)),
106
+ CaseType.PASCAL_CASE,
107
+ ),
108
+ value: tf,
109
+ })),
110
+ );
79
111
 
80
- if (commandFolders.length === 1) {
81
- return commandFolders[0];
112
+ this.logger.debug(`Selected ${pickedTasks.length} tasks`);
113
+ if (!pickedTasks.length) {
114
+ return;
82
115
  }
83
116
 
84
- // TODO: prompt to select destination from available commandFolders
85
- return commandFolders[0];
117
+ console.log(JSON.stringify(pickedTasks));
118
+
119
+ // add them to the map
120
+ for (const picked of pickedTasks) {
121
+ const filename = this.fileService.getFilenameNoExt(picked);
122
+ const taskClassName = this.utilService.titleizedToCase(
123
+ this.utilService.titleizeAll(filename),
124
+ CaseType.PASCAL_CASE,
125
+ );
126
+ const relativeSource = this.state.data.project!.commandDestination!;
127
+ const relativeDest = this.fileService.join(
128
+ this.state.data.project.taskDestination,
129
+ picked,
130
+ );
131
+ taskMappings.set(
132
+ taskClassName,
133
+ this.fileService
134
+ .toRelativePath(relativeSource, relativeDest)
135
+ // remove extension for typescript import
136
+ .replace(ProjectService.defaults.extention, ""),
137
+ );
138
+ }
86
139
  }
87
140
  }
@@ -2,9 +2,9 @@ import chalk from "chalk";
2
2
  import { AppTask } from "../wrappers/app-task";
3
3
  import { autoInjectable } from "tsyringe";
4
4
  import { GenTargetType } from "../types/state";
5
- import ProjectService from "../services/project-service";
5
+ import { ProjectService } from "../services/project-service";
6
6
  import { UtilService } from "../services/util-service";
7
- import FileService from "../services/file-service";
7
+ import { FileService } from "../services/file-service";
8
8
  import { CaseType } from "../types/format";
9
9
  import path from "path";
10
10
  import { getServiceTemplate } from "../templates/service";
@@ -24,26 +24,21 @@ export default class GenerateService extends AppTask {
24
24
  super();
25
25
  }
26
26
 
27
+ /**
28
+ * Generates a new service class file.
29
+ *
30
+ * @returns updated state with outputDestination set to generated result path.
31
+ */
27
32
  async run() {
28
33
  if (this.state.data.genTargetType !== GenTargetType.Service) {
29
34
  return;
30
35
  }
31
36
 
32
37
  this.logger.info(chalk.blueBright("Generating Service"));
33
- const { argService, utilService, projectService } = this;
38
+ const { utilService, projectService } = this;
34
39
 
35
- let targetName = argService.getTargetName();
36
- if (!targetName) {
37
- // TODO: Prompt for target name?
38
- targetName = argService.cleanTargetName(
39
- ProjectService.defaults.targetName,
40
- );
41
- }
42
-
43
- const targetDirectory = this.getTargetDirectory();
44
- if (!targetDirectory) {
45
- throw new Error("Unable to determine target Directory");
46
- }
40
+ const targetName = this.state.data.genTargetName!;
41
+ const targetDirectory = this.state.data.project!.serviceDestination!;
47
42
 
48
43
  // detect case
49
44
  const convention = projectService.getConvention();
@@ -63,20 +58,21 @@ export default class GenerateService extends AppTask {
63
58
  }
64
59
 
65
60
  /**
66
- * Get the target directory to save the generated file.
67
- *
68
- * @returns the destination path of the parent folder to save the new file in.
61
+ * Checks state for required fields. Executes prior to run method.
62
+ * Skips validation if genTargetType is not set to service.
69
63
  */
70
- getTargetDirectory() {
71
- const runnerRoot = this.fileService.getRunnerRootDir();
72
- const servicesFolders = this.fileService.getServiceDirs(runnerRoot);
73
-
74
- if (servicesFolders.length === 1) {
75
- return servicesFolders[0];
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
76
  }
77
-
78
- // TODO: prompt to select destination from available servicesFolders
79
- this.logger.warn(`Multiple service folders detected. Using first found.`);
80
- return servicesFolders[0];
81
77
  }
82
78
  }
@@ -5,8 +5,8 @@ import { GenTargetType } from "../types/state";
5
5
  import { getTaskTemplate } from "../templates/task";
6
6
  import { CaseType } from "../types/format";
7
7
  import { UtilService } from "../services/util-service";
8
- import FileService from "../services/file-service";
9
- import ProjectService from "../services/project-service";
8
+ import { FileService } from "../services/file-service";
9
+ import { ProjectService } from "../services/project-service";
10
10
  import path from "path";
11
11
 
12
12
  /**
@@ -24,27 +24,22 @@ export default class GenerateTask extends AppTask {
24
24
  super();
25
25
  }
26
26
 
27
+ /**
28
+ * Generates a new task class file.
29
+ *
30
+ * @returns updated state with outputDestination set to generated result path.
31
+ */
27
32
  async run() {
28
33
  if (this.state.data.genTargetType !== GenTargetType.Task) {
29
34
  return;
30
35
  }
31
36
 
32
- const { utilService, argService } = this;
37
+ const { utilService } = this;
33
38
 
34
39
  this.logger.info(chalk.blueBright("Generating Task"));
35
40
 
36
- let targetName = argService.getTargetName();
37
- if (!targetName) {
38
- // TODO: Prompt for target name?
39
- targetName = argService.cleanTargetName(
40
- ProjectService.defaults.targetName,
41
- );
42
- }
43
-
44
- const targetDirectory = this.getTargetDirectory();
45
- if (!targetDirectory) {
46
- throw new Error("Unable to determine target Directory");
47
- }
41
+ const targetName = this.state.data.genTargetName!;
42
+ const targetDirectory = this.state.data.project!.taskDestination!;
48
43
 
49
44
  // detect case
50
45
  const convention = this.projectService.getConvention();
@@ -64,20 +59,21 @@ export default class GenerateTask extends AppTask {
64
59
  }
65
60
 
66
61
  /**
67
- * Get the target directory to save the generated file.
68
- *
69
- * @returns the destination path of the parent folder to save the new file in.
62
+ * Checks state for required fields. Executes prior to run method.
63
+ * Skips validation if genTargetType is not set to task.
70
64
  */
71
- getTargetDirectory() {
72
- const runnerRoot = this.fileService.getRunnerRootDir();
73
- const tasksFolders = this.fileService.getTaskDirs(runnerRoot);
74
-
75
- if (tasksFolders.length === 1) {
76
- return tasksFolders[0];
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
77
  }
78
-
79
- // TODO: prompt to select destination from available tasksFolders
80
- this.logger.warn(`Multiple task folders detected. Using first found.`);
81
- return tasksFolders[0];
82
78
  }
83
79
  }