index.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. #!/usr/bin/env node
  2. import * as fs from "node:fs";
  3. import * as path from "node:path";
  4. import { getInputs } from "./utils/getInputs";
  5. import { Language, getLanguage } from "./utils/getLanguage";
  6. import { logBanner, logDone, logErrorAndExit, logStep } from "./utils/getLogs";
  7. import { RenderContext, getRenderContext } from "./utils/getRenderContext";
  8. import { renderTemplate } from "./utils/renderTemplates";
  9. import { detectSolanaVersion, generateKeypair } from "./utils/solanaCli";
  10. (async function init() {
  11. logBanner();
  12. // Get arguments from CLI and prompt.
  13. const language = getLanguage();
  14. const inputs = await getInputs(language);
  15. // Create or empty the target directory.
  16. createOrEmptyTargetDirectory(
  17. language,
  18. inputs.targetDirectoryName,
  19. inputs.shouldOverride
  20. );
  21. // Detect the solana version.
  22. const solanaVersionDetected = await logStep(
  23. language.infos.detectSolanaVersion,
  24. () => detectSolanaVersion(language)
  25. );
  26. // Generate a keypair if needed.
  27. const programAddress =
  28. inputs.programAddress ??
  29. (await logStep(language.infos.generateKeypair, () => {
  30. const outfile = path.join(
  31. process.cwd(),
  32. inputs.targetDirectoryName,
  33. "program",
  34. "keypair.json"
  35. );
  36. return generateKeypair(language, outfile);
  37. }));
  38. // Get the args inputs, prompt inputs and computed values.
  39. const ctx = getRenderContext({
  40. language,
  41. inputs,
  42. programAddress,
  43. solanaVersionDetected,
  44. });
  45. // Render the templates.
  46. await logStep(
  47. language.infos.scaffold.replace(
  48. "$targetDirectory",
  49. inputs.targetDirectoryName
  50. ),
  51. () => renderTemplates(ctx)
  52. );
  53. // Done.
  54. logDone(ctx);
  55. })().catch((e) => console.error(e));
  56. function renderTemplates(ctx: RenderContext) {
  57. const render = (templateName: string) => {
  58. const directory = path.resolve(ctx.templateDirectory, templateName);
  59. renderTemplate(ctx, directory, ctx.targetDirectory);
  60. };
  61. render("base");
  62. if (ctx.programFramework === "anchor") {
  63. render("programs/counter-anchor");
  64. } else {
  65. render("programs/counter-shank");
  66. }
  67. if (ctx.clients.length > 0) {
  68. render("clients/base");
  69. }
  70. ctx.clients.forEach((client) => {
  71. render(`clients/${client}`);
  72. });
  73. }
  74. function createOrEmptyTargetDirectory(
  75. language: Language,
  76. targetDirectoryName: string,
  77. shouldOverride: boolean
  78. ) {
  79. const targetDirectory = path.join(process.cwd(), targetDirectoryName);
  80. if (!fs.existsSync(targetDirectory)) {
  81. fs.mkdirSync(targetDirectory, { recursive: true });
  82. } else if (shouldOverride) {
  83. emptyDirectory(targetDirectory);
  84. } else {
  85. logErrorAndExit(
  86. language.errors.cannotOverrideDirectory.replace(
  87. "$targetDirectory",
  88. targetDirectoryName
  89. )
  90. );
  91. }
  92. }
  93. function emptyDirectory(directory: string) {
  94. for (const filename of fs.readdirSync(directory)) {
  95. if (filename === ".git") continue;
  96. const fullpath = path.resolve(directory, filename);
  97. fs.rmSync(fullpath, { recursive: true });
  98. }
  99. }