inputPrompts.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import chalk from 'chalk';
  2. import * as fs from 'node:fs';
  3. import prompts from 'prompts';
  4. import { Language } from './localization';
  5. import { allClients, getDefaultInputs, Inputs } from './inputCore';
  6. type PromptInputs = {
  7. programName?: string;
  8. shouldOverride?: boolean;
  9. organizationName?: string;
  10. programCrateName?: string;
  11. programFramework?: 'shank' | 'anchor';
  12. clients?: Array<'js' | 'rust'>;
  13. jsClientPackageName?: string;
  14. rustClientCrateName?: string;
  15. };
  16. export async function getInputsFromPrompts(
  17. language: Language,
  18. argInputs: Partial<Inputs>
  19. ): Promise<Inputs> {
  20. let defaultInputs = getDefaultInputs(argInputs);
  21. try {
  22. const promptInputs: PromptInputs = await prompts(
  23. [
  24. {
  25. name: 'programName',
  26. type: argInputs.programName ? null : 'text',
  27. message: language.programName.message,
  28. initial: () => defaultInputs.programName,
  29. },
  30. {
  31. name: 'shouldOverride',
  32. type: (_, values) => {
  33. if (argInputs.shouldOverride) return null;
  34. defaultInputs = parsePromptInputs(values, argInputs);
  35. return canSkipEmptying(defaultInputs.targetDirectoryName)
  36. ? null
  37. : 'toggle';
  38. },
  39. message: () => {
  40. const dirForPrompt =
  41. defaultInputs.targetDirectoryName === '.'
  42. ? language.shouldOverride.dirForPrompts!.current
  43. : `${language.shouldOverride.dirForPrompts!.target} "${defaultInputs.targetDirectoryName}"`;
  44. return `${dirForPrompt} ${language.shouldOverride.message}`;
  45. },
  46. initial: false,
  47. active: language.defaultToggleOptions.active,
  48. inactive: language.defaultToggleOptions.inactive,
  49. },
  50. {
  51. name: 'overwriteChecker',
  52. type: (_, values) => {
  53. if (values.shouldOverride === false) {
  54. throw new Error(
  55. chalk.red('✖') + ` ${language.errors.operationCancelled}`
  56. );
  57. }
  58. return null;
  59. },
  60. },
  61. {
  62. name: 'organizationName',
  63. type: argInputs.organizationName ? null : 'text',
  64. message: language.organizationName.message,
  65. initial: () => defaultInputs.organizationName,
  66. },
  67. {
  68. name: 'programCrateName',
  69. type: argInputs.programCrateName ? null : 'text',
  70. message: language.programCrateName.message,
  71. initial: (_, values) => {
  72. defaultInputs = parsePromptInputs(values, argInputs);
  73. return defaultInputs.programCrateName;
  74. },
  75. },
  76. {
  77. name: 'programFramework',
  78. type: argInputs.programFramework ? null : 'select',
  79. message: language.programFramework.message,
  80. hint: language.instructions.select,
  81. initial: 0,
  82. choices: [
  83. {
  84. title: language.programFramework.selectOptions!.shank.title,
  85. description: language.programFramework.selectOptions!.shank.desc,
  86. value: 'shank',
  87. },
  88. {
  89. title: language.programFramework.selectOptions!.anchor.title,
  90. description: language.programFramework.selectOptions!.anchor.desc,
  91. value: 'anchor',
  92. },
  93. ],
  94. },
  95. {
  96. name: 'clients',
  97. type: () => {
  98. const hasSelectedClients = [
  99. argInputs.jsClient,
  100. argInputs.rustClient,
  101. ].every((client) => typeof client === 'boolean');
  102. return hasSelectedClients ? null : 'multiselect';
  103. },
  104. message: language.clients.message,
  105. hint: language.clients.hint,
  106. instructions: language.instructions.multiselect,
  107. choices: allClients.map((client) => ({
  108. title: language.clients.selectOptions![client].title,
  109. description: language.clients.selectOptions![client].desc,
  110. value: client,
  111. selected: true,
  112. })),
  113. },
  114. {
  115. name: 'jsClientPackageName',
  116. type: (_, values) => {
  117. if (argInputs.jsClientPackageName) return null;
  118. defaultInputs = parsePromptInputs(values, argInputs);
  119. return defaultInputs.jsClient ? 'text' : null;
  120. },
  121. message: language.jsClientPackageName.message,
  122. initial: () => defaultInputs.jsClientPackageName,
  123. },
  124. {
  125. name: 'rustClientCrateName',
  126. type: (_, values) => {
  127. if (argInputs.rustClientCrateName) return null;
  128. defaultInputs = parsePromptInputs(values, argInputs);
  129. return defaultInputs.rustClient ? 'text' : null;
  130. },
  131. message: language.rustClientCrateName.message,
  132. initial: () => defaultInputs.rustClientCrateName,
  133. },
  134. ],
  135. {
  136. onCancel: () => {
  137. throw new Error(
  138. chalk.red('✖') + ` ${language.errors.operationCancelled}`
  139. );
  140. },
  141. }
  142. );
  143. // Add a line break after the prompts
  144. console.log('');
  145. return parsePromptInputs(promptInputs, argInputs);
  146. } catch (cancelled) {
  147. console.log((cancelled as Error).message);
  148. process.exit(1);
  149. }
  150. }
  151. function parsePromptInputs(
  152. promptInputs: PromptInputs,
  153. argInputs: Partial<Inputs>
  154. ): Inputs {
  155. const inputs = {} as Partial<Inputs>;
  156. if (promptInputs.programName) inputs.programName = promptInputs.programName;
  157. if (promptInputs.shouldOverride !== undefined)
  158. inputs.shouldOverride = promptInputs.shouldOverride;
  159. if (promptInputs.organizationName)
  160. inputs.organizationName = promptInputs.organizationName;
  161. if (promptInputs.programCrateName)
  162. inputs.programCrateName = promptInputs.programCrateName;
  163. if (promptInputs.programFramework)
  164. inputs.programFramework = promptInputs.programFramework;
  165. if (promptInputs.clients !== undefined) {
  166. inputs.jsClient = promptInputs.clients.includes('js');
  167. inputs.rustClient = promptInputs.clients.includes('rust');
  168. }
  169. if (promptInputs.jsClientPackageName)
  170. inputs.jsClientPackageName = promptInputs.jsClientPackageName;
  171. if (promptInputs.rustClientCrateName)
  172. inputs.rustClientCrateName = promptInputs.rustClientCrateName;
  173. return getDefaultInputs({ ...argInputs, ...inputs });
  174. }
  175. function canSkipEmptying(dir: fs.PathLike) {
  176. if (!fs.existsSync(dir)) {
  177. return true;
  178. }
  179. const files = fs.readdirSync(dir);
  180. if (files.length === 0) {
  181. return true;
  182. }
  183. if (files.length === 1 && files[0] === '.git') {
  184. return true;
  185. }
  186. return false;
  187. }