Loris Leiva hace 1 año
padre
commit
bea8ea6e38

+ 5 - 0
.prettierignore

@@ -6,6 +6,11 @@
 **/tsconfig.*.json
 **/tsconfig.json
 
+template/**/*.njk
+template/**/*.json
+template/**/*.yaml
+template/**/.gitignore
+
 .prettierrc
 package.json
 README.md

+ 1 - 9
.prettierrc

@@ -5,13 +5,5 @@
   "useTabs": false,
   "tabWidth": 2,
   "arrowParens": "always",
-  "printWidth": 80,
-  "overrides": [
-    {
-      "files": ["*.mts", "*.cts", "*.ts"],
-      "options": {
-        "parser": "typescript"
-      }
-    }
-  ]
+  "printWidth": 80
 }

+ 16 - 16
index.ts

@@ -1,14 +1,14 @@
 #!/usr/bin/env node
 
-import * as path from "node:path";
+import * as path from 'node:path';
 
-import { createOrEmptyTargetDirectory } from "./utils/fsHelpers";
-import { getInputs } from "./utils/getInputs";
-import { getLanguage } from "./utils/getLanguage";
-import { logBanner, logDone, logStep } from "./utils/getLogs";
-import { RenderContext, getRenderContext } from "./utils/getRenderContext";
-import { renderTemplate } from "./utils/renderTemplates";
-import { detectSolanaVersion, generateKeypair } from "./utils/solanaCli";
+import { createOrEmptyTargetDirectory } from './utils/fsHelpers';
+import { getInputs } from './utils/getInputs';
+import { getLanguage } from './utils/getLanguage';
+import { logBanner, logDone, logStep } from './utils/getLogs';
+import { RenderContext, getRenderContext } from './utils/getRenderContext';
+import { renderTemplate } from './utils/renderTemplates';
+import { detectSolanaVersion, generateKeypair } from './utils/solanaCli';
 
 (async function init() {
   logBanner();
@@ -37,8 +37,8 @@ import { detectSolanaVersion, generateKeypair } from "./utils/solanaCli";
       const outfile = path.join(
         process.cwd(),
         inputs.targetDirectoryName,
-        "program",
-        "keypair.json"
+        'program',
+        'keypair.json'
       );
       return generateKeypair(language, outfile);
     }));
@@ -54,7 +54,7 @@ import { detectSolanaVersion, generateKeypair } from "./utils/solanaCli";
   // Render the templates.
   await logStep(
     language.infos.scaffold.replace(
-      "$targetDirectory",
+      '$targetDirectory',
       inputs.targetDirectoryName
     ),
     () => renderTemplates(ctx)
@@ -70,16 +70,16 @@ function renderTemplates(ctx: RenderContext) {
     renderTemplate(ctx, directory, ctx.targetDirectory);
   };
 
-  render("base");
+  render('base');
 
-  if (ctx.programFramework === "anchor") {
-    render("programs/counter-anchor");
+  if (ctx.programFramework === 'anchor') {
+    render('programs/counter-anchor');
   } else {
-    render("programs/counter-shank");
+    render('programs/counter-shank');
   }
 
   if (ctx.clients.length > 0) {
-    render("clients/base");
+    render('clients/base');
   }
 
   ctx.clients.forEach((client) => {

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 393 - 133
pnpm-lock.yaml


+ 11 - 11
scripts/build.mjs

@@ -1,27 +1,27 @@
 #!/usr/bin/env zx
-import * as esbuild from "esbuild";
+import * as esbuild from 'esbuild';
 
 await esbuild.build({
   bundle: true,
-  entryPoints: ["index.ts"],
-  external: ["locales/*"],
-  outfile: "outfile.cjs",
-  format: "cjs",
-  platform: "node",
-  target: "node14",
+  entryPoints: ['index.ts'],
+  external: ['locales/*'],
+  outfile: 'outfile.cjs',
+  format: 'cjs',
+  platform: 'node',
+  target: 'node14',
 
   plugins: [
     {
-      name: "alias",
+      name: 'alias',
       setup({ onResolve, resolve }) {
         onResolve(
-          { filter: /^prompts$/, namespace: "file" },
+          { filter: /^prompts$/, namespace: 'file' },
           async ({ importer, resolveDir }) => {
             // we can always use non-transpiled code since we support 14.16.0+
-            const result = await resolve("prompts/lib/index.js", {
+            const result = await resolve('prompts/lib/index.js', {
               importer,
               resolveDir,
-              kind: "import-statement",
+              kind: 'import-statement',
             });
             return result;
           }

+ 6 - 6
scripts/prepublish.mjs

@@ -1,14 +1,14 @@
 #!/usr/bin/env zx
-import "zx/globals";
-import { PROJECTS } from "./utils.mjs";
+import 'zx/globals';
+import { PROJECTS } from './utils.mjs';
 
 await $`pnpm snapshot`;
 
-const { version } = await fs.readJSON("./package.json");
+const { version } = await fs.readJSON('./package.json');
 const projects = Object.keys(PROJECTS);
 
-const rootDirectory = path.resolve(__dirname, "..");
-const projectsDirectory = path.resolve(rootDirectory, "projects");
+const rootDirectory = path.resolve(__dirname, '..');
+const projectsDirectory = path.resolve(rootDirectory, 'projects');
 
 for (const projectName of projects) {
   const projectDirectory = path.resolve(projectsDirectory, projectName);
@@ -19,7 +19,7 @@ for (const projectName of projects) {
   try {
     await $`git commit -m "version ${version} snapshot"`;
   } catch (e) {
-    if (!e.stdout.includes("nothing to commit")) {
+    if (!e.stdout.includes('nothing to commit')) {
       throw e;
     }
   }

+ 16 - 16
scripts/snapshot.mjs

@@ -1,6 +1,6 @@
 #!/usr/bin/env zx
-import "zx/globals";
-import { CLIENTS, PROJECTS, executeStep } from "./utils.mjs";
+import 'zx/globals';
+import { CLIENTS, PROJECTS, executeStep } from './utils.mjs';
 
 $.verbose = false;
 
@@ -15,8 +15,8 @@ const projects =
 const runTests = !!argv.test;
 
 // Resolve paths.
-const bin = path.resolve(__dirname, "../outfile.cjs");
-const projectsDirectory = path.resolve(__dirname, "../projects/");
+const bin = path.resolve(__dirname, '../outfile.cjs');
+const projectsDirectory = path.resolve(__dirname, '../projects/');
 
 if (!fs.existsSync(projectsDirectory)) {
   fs.mkdirSync(projectsDirectory);
@@ -32,38 +32,38 @@ for (const projectName of projects) {
   // Scaffold the project.
   const args = [projectName, ...PROJECTS[projectName]];
   await executeStep(
-    "scaffold the project",
-    () => $`node ${[bin, ...args, "--force", "--default"]}`
+    'scaffold the project',
+    () => $`node ${[bin, ...args, '--force', '--default']}`
   );
 
   // Go inside the created project.
   const projectDirectory = path.resolve(projectsDirectory, projectName);
   cd(projectDirectory);
-  const pkg = await fs.readJSON(path.resolve(projectDirectory, "package.json"));
+  const pkg = await fs.readJSON(path.resolve(projectDirectory, 'package.json'));
 
   // Install project's dependencies.
-  await executeStep("install NPM dependencies", async () => {
+  await executeStep('install NPM dependencies', async () => {
     await $`pnpm install`;
   });
 
   // Generate IDLs.
-  if ("generate:idls" in pkg.scripts) {
-    await executeStep("generate IDLs", async () => {
+  if ('generate:idls' in pkg.scripts) {
+    await executeStep('generate IDLs', async () => {
       await $`pnpm generate:idls`;
     });
   }
 
   // Generate clients.
-  if ("generate:clients" in pkg.scripts) {
-    await executeStep("generate clients", async () => {
+  if ('generate:clients' in pkg.scripts) {
+    await executeStep('generate clients', async () => {
       await $`pnpm generate:clients`;
     });
   }
 
   if (runTests) {
     // Test programs.
-    if ("programs:test" in pkg.scripts) {
-      await executeStep("test programs", async () => {
+    if ('programs:test' in pkg.scripts) {
+      await executeStep('test programs', async () => {
         await $`pnpm programs:test`;
       });
     }
@@ -79,7 +79,7 @@ for (const projectName of projects) {
   }
 
   // Add line break between projects.
-  echo("");
+  echo('');
 }
 
-echo(chalk.green("All projects were created successfully!"));
+echo(chalk.green('All projects were created successfully!'));

+ 4 - 4
scripts/utils.mjs

@@ -1,15 +1,15 @@
-export const CLIENTS = ["js", "rust"];
+export const CLIENTS = ['js', 'rust'];
 export const PROJECTS = {
-  "counter-shank": ["counter", "--shank"],
+  'counter-shank': ['counter', '--shank'],
 };
 
 export async function executeStep(title, fn) {
   try {
     const capitalizedTitle = title.charAt(0).toUpperCase() + title.slice(1);
     await spinner(`${capitalizedTitle}...`, fn);
-    echo(chalk.green("✔︎") + ` ${capitalizedTitle}.`);
+    echo(chalk.green('✔︎') + ` ${capitalizedTitle}.`);
   } catch (e) {
-    echo(chalk.red("✘") + ` Failed to ${title}.\n`);
+    echo(chalk.red('✘') + ` Failed to ${title}.\n`);
     echo(e);
     process.exit(1);
   }

+ 10 - 10
template/base/scripts/generate-idls.mjs

@@ -1,21 +1,21 @@
 #!/usr/bin/env zx
-import "zx/globals";
-import { generateIdl } from "@metaplex-foundation/shank-js";
-import { getCargo, getProgramFolders } from "./utils.mjs";
+import 'zx/globals';
+import { generateIdl } from '@metaplex-foundation/shank-js';
+import { getCargo, getProgramFolders } from './utils.mjs';
 
-const binaryInstallDir = path.join(__dirname, "..", ".cargo");
+const binaryInstallDir = path.join(__dirname, '..', '.cargo');
 
 getProgramFolders().forEach((folder) => {
   const cargo = getCargo(folder);
-  const isShank = Object.keys(cargo.dependencies).includes("shank");
-  const programDir = path.join(__dirname, "..", folder);
+  const isShank = Object.keys(cargo.dependencies).includes('shank');
+  const programDir = path.join(__dirname, '..', folder);
 
   generateIdl({
-    generator: isShank ? "shank" : "anchor",
-    programName: cargo.package.name.replace(/-/g, "_"),
-    programId: cargo.package.metadata.solana["program-id"],
+    generator: isShank ? 'shank' : 'anchor',
+    programName: cargo.package.name.replace(/-/g, '_'),
+    programId: cargo.package.metadata.solana['program-id'],
     idlDir: programDir,
-    idlName: "idl",
+    idlName: 'idl',
     programDir,
     binaryInstallDir,
   });

+ 3 - 3
template/base/scripts/program/build.mjs

@@ -1,9 +1,9 @@
 #!/usr/bin/env zx
-import "zx/globals";
-import { workingDirectory, getProgramFolders } from "../utils.mjs";
+import 'zx/globals';
+import { workingDirectory, getProgramFolders } from '../utils.mjs';
 
 // Save external programs binaries to the output directory.
-import "./dump.mjs";
+import './dump.mjs';
 
 // Build the programs.
 await Promise.all(

+ 2 - 2
template/base/scripts/program/clean.mjs

@@ -1,6 +1,6 @@
 #!/usr/bin/env zx
-import "zx/globals";
-import { getExternalProgramOutputDir } from "../utils.mjs";
+import 'zx/globals';
+import { getExternalProgramOutputDir } from '../utils.mjs';
 
 // Remove the programs output directories.
 const externalProgramOutput = getExternalProgramOutputDir();

+ 8 - 8
template/base/scripts/program/dump.mjs

@@ -1,12 +1,12 @@
 #!/usr/bin/env zx
-import "zx/globals";
+import 'zx/globals';
 import {
   getExternalProgramAddresses,
   getExternalProgramOutputDir,
-} from "../utils.mjs";
+} from '../utils.mjs';
 
 // Get input from environment variables.
-const rpc = process.env.RPC ?? "https://api.mainnet-beta.solana.com";
+const rpc = process.env.RPC ?? 'https://api.mainnet-beta.solana.com';
 const outputDir = getExternalProgramOutputDir();
 await dump();
 
@@ -40,12 +40,12 @@ async function dump() {
 
       if (onChainHash.toString() !== localHash.toString()) {
         echo(
-          chalk.yellow("[ WARNING ]"),
+          chalk.yellow('[ WARNING ]'),
           `on-chain and local binaries are different for '${binary}'`
         );
       } else {
         echo(
-          chalk.green("[ SKIPPED ]"),
+          chalk.green('[ SKIPPED ]'),
           `on-chain and local binaries are the same for '${binary}'`
         );
       }
@@ -57,10 +57,10 @@ async function dump() {
 
 /** Helper function to copy external programs or accounts binaries from the chain. */
 async function copyFromChain(address, binary) {
-  switch (binary.split(".").pop()) {
-    case "bin":
+  switch (binary.split('.').pop()) {
+    case 'bin':
       return $`solana account -u ${rpc} ${address} -o ${outputDir}/${binary} >/dev/null`.quiet();
-    case "so":
+    case 'so':
       return $`solana program dump -u ${rpc} ${address} ${outputDir}/${binary} >/dev/null`.quiet();
     default:
       echo(chalk.red(`[  ERROR  ] unknown account type for '${binary}'`));

+ 2 - 2
template/base/scripts/program/format.mjs

@@ -1,6 +1,6 @@
 #!/usr/bin/env zx
-import "zx/globals";
-import { workingDirectory, getProgramFolders } from "../utils.mjs";
+import 'zx/globals';
+import { workingDirectory, getProgramFolders } from '../utils.mjs';
 
 // Format the programs.
 await Promise.all(

+ 2 - 2
template/base/scripts/program/lint.mjs

@@ -1,6 +1,6 @@
 #!/usr/bin/env zx
-import "zx/globals";
-import { workingDirectory, getProgramFolders } from "../utils.mjs";
+import 'zx/globals';
+import { workingDirectory, getProgramFolders } from '../utils.mjs';
 
 // Lint the programs using clippy.
 await Promise.all(

+ 4 - 4
template/base/scripts/program/test.mjs

@@ -1,15 +1,15 @@
 #!/usr/bin/env zx
-import "zx/globals";
-import { workingDirectory, getProgramFolders } from "../utils.mjs";
+import 'zx/globals';
+import { workingDirectory, getProgramFolders } from '../utils.mjs';
 
 // Save external programs binaries to the output directory.
-import "./dump.mjs";
+import './dump.mjs';
 
 // Test the programs.
 await Promise.all(
   getProgramFolders().map(async (folder) => {
     await $`cd ${path.join(workingDirectory, folder)}`.quiet();
-    const hasSolfmt = await which("solfmt", { nothrow: true });
+    const hasSolfmt = await which('solfmt', { nothrow: true });
 
     if (hasSolfmt) {
       await $`RUST_LOG=error cargo test-sbf ${argv._} 2>&1 | solfmt`;

+ 10 - 10
template/base/scripts/utils.mjs

@@ -1,27 +1,27 @@
-import "zx/globals";
-import { parse as parseToml } from "@iarna/toml";
+import 'zx/globals';
+import { parse as parseToml } from '@iarna/toml';
 
 process.env.FORCE_COLOR = 3;
-process.env.CARGO_TERM_COLOR = "always";
+process.env.CARGO_TERM_COLOR = 'always';
 
 export const workingDirectory = (await $`pwd`.quiet()).toString().trim();
 
 export function getAllProgramIdls() {
   return getAllProgramFolders().map((folder) =>
-    path.join(workingDirectory, folder, "idl.json")
+    path.join(workingDirectory, folder, 'idl.json')
   );
 }
 
 export function getExternalProgramOutputDir() {
   const config =
-    getCargo().workspace?.metadata?.solana?.["external-programs-output"];
-  return path.join(workingDirectory, config ?? "target/deploy");
+    getCargo().workspace?.metadata?.solana?.['external-programs-output'];
+  return path.join(workingDirectory, config ?? 'target/deploy');
 }
 
 export function getExternalProgramAddresses() {
   const addresses = getProgramFolders().flatMap(
     (folder) =>
-      getCargo(folder).package?.metadata?.solana?.["program-dependencies"] ?? []
+      getCargo(folder).package?.metadata?.solana?.['program-dependencies'] ?? []
   );
   return Array.from(new Set(addresses));
 }
@@ -52,15 +52,15 @@ export function getProgramFolders() {
 
 export function getAllProgramFolders() {
   return getCargo().workspace.members.filter((member) =>
-    (getCargo(member).lib?.["crate-type"] ?? []).includes("cdylib")
+    (getCargo(member).lib?.['crate-type'] ?? []).includes('cdylib')
   );
 }
 
 export function getCargo(folder) {
   return parseToml(
     fs.readFileSync(
-      path.join(workingDirectory, folder ? folder : ".", "Cargo.toml"),
-      "utf8"
+      path.join(workingDirectory, folder ? folder : '.', 'Cargo.toml'),
+      'utf8'
     )
   );
 }

+ 24 - 24
template/clients/base/scripts/start-validator.mjs

@@ -1,28 +1,28 @@
 #!/usr/bin/env zx
-import { spawn } from "node:child_process";
-import fs from "node:fs";
-import "zx/globals";
+import { spawn } from 'node:child_process';
+import fs from 'node:fs';
+import 'zx/globals';
 import {
   getCargo,
   getExternalProgramAddresses,
   getExternalProgramOutputDir,
   getProgramFolders,
-} from "./utils.mjs";
+} from './utils.mjs';
 
 // Options and arguments.
-const force = argv["force"];
+const force = argv['force'];
 
 // Keep the validator running when not using the force flag.
 const isValidatorRunning = (await $`lsof -t -i:8899`.quiet().exitCode) === 0;
 if (!force && isValidatorRunning) {
-  echo(chalk.yellow("Local validator is already running."));
+  echo(chalk.yellow('Local validator is already running.'));
   process.exit();
 }
 
 // Initial message.
-const verb = isValidatorRunning ? "Restarting" : "Starting";
+const verb = isValidatorRunning ? 'Restarting' : 'Starting';
 const programs = [...getPrograms(), ...getExternalPrograms()];
-const programPluralized = programs.length === 1 ? "program" : "programs";
+const programPluralized = programs.length === 1 ? 'program' : 'programs';
 echo(
   `${verb} local validator with ${programs.length} custom ${programPluralized}...`
 );
@@ -34,34 +34,34 @@ if (isValidatorRunning) {
 }
 
 // Global validator arguments.
-const args = [/* Reset ledger */ "-r"];
+const args = [/* Reset ledger */ '-r'];
 
 // Load programs.
 programs.forEach(({ programId, deployPath }) => {
-  args.push(/* Load BPF program */ "--bpf-program", programId, deployPath);
+  args.push(/* Load BPF program */ '--bpf-program', programId, deployPath);
 });
 
 // Start the validator in detached mode.
-const cliLogs = path.join(os.tmpdir(), "validator-cli.log");
-fs.writeFileSync(cliLogs, "", () => {});
-const out = fs.openSync(cliLogs, "a");
-const err = fs.openSync(cliLogs, "a");
-const validator = spawn("solana-test-validator", args, {
+const cliLogs = path.join(os.tmpdir(), 'validator-cli.log');
+fs.writeFileSync(cliLogs, '', () => {});
+const out = fs.openSync(cliLogs, 'a');
+const err = fs.openSync(cliLogs, 'a');
+const validator = spawn('solana-test-validator', args, {
   detached: true,
-  stdio: ["ignore", out, err],
+  stdio: ['ignore', out, err],
 });
 validator.unref();
 
 // Wait for the validator to stabilize.
 const waitForValidator = spinner(
-  "Waiting for local validator to stabilize...",
+  'Waiting for local validator to stabilize...',
   () =>
     new Promise((resolve, reject) => {
       setInterval(() => {
-        const logs = fs.readFileSync(cliLogs, "utf8");
+        const logs = fs.readFileSync(cliLogs, 'utf8');
         if (validator.exitCode !== null) {
           reject(logs);
-        } else if (logs.includes("Confirmed Slot: 1")) {
+        } else if (logs.includes('Confirmed Slot: 1')) {
           resolve();
         }
       }, 1000);
@@ -70,22 +70,22 @@ const waitForValidator = spinner(
 
 try {
   await waitForValidator;
-  echo(chalk.green("Local validator is up and running!"));
+  echo(chalk.green('Local validator is up and running!'));
 } catch (error) {
   echo(error);
-  echo(chalk.red("Could not start local validator."));
+  echo(chalk.red('Could not start local validator.'));
 } finally {
   fs.rmSync(cliLogs);
   process.exit();
 }
 
 function getPrograms() {
-  const binaryDir = path.join(__dirname, "..", "target", "deploy");
+  const binaryDir = path.join(__dirname, '..', 'target', 'deploy');
   return getProgramFolders().map((folder) => {
     const cargo = getCargo(folder);
-    const name = cargo.package.name.replace(/-/g, "_");
+    const name = cargo.package.name.replace(/-/g, '_');
     return {
-      programId: cargo.package.metadata.solana["program-id"],
+      programId: cargo.package.metadata.solana['program-id'],
       deployPath: path.join(binaryDir, `${name}.so`),
     };
   });

+ 3 - 3
template/clients/base/scripts/stop-validator.mjs

@@ -1,5 +1,5 @@
 #!/usr/bin/env zx
-import "zx/globals";
+import 'zx/globals';
 
 const isValidatorRunning = (await $`lsof -t -i:8899`.quiet().exitCode) === 0;
 
@@ -7,7 +7,7 @@ if (isValidatorRunning) {
   // Kill the validator if it's already running.
   await $`pkill -f solana-test-validator`.quiet();
   await sleep(1000);
-  echo(chalk.green("Local validator terminated!"));
+  echo(chalk.green('Local validator terminated!'));
 } else {
-  echo(chalk.yellow("Local validator is not running."));
+  echo(chalk.yellow('Local validator is not running.'));
 }

+ 3 - 3
template/clients/js/scripts/client/test-js.mjs

@@ -1,12 +1,12 @@
 #!/usr/bin/env zx
-import "zx/globals";
-import { workingDirectory } from "../utils.mjs";
+import 'zx/globals';
+import { workingDirectory } from '../utils.mjs';
 
 // Start the local validator if it's not already running.
 await $`pnpm validator --force`;
 
 // Build the client and run the tests.
-cd(path.join(workingDirectory, "clients", "js"));
+cd(path.join(workingDirectory, 'clients', 'js'));
 await $`pnpm install`;
 await $`pnpm build`;
 await $`pnpm test ${argv._}`;

+ 4 - 4
template/clients/rust/scripts/client/test-rust.mjs

@@ -1,13 +1,13 @@
 #!/usr/bin/env zx
-import "zx/globals";
-import { workingDirectory } from "../utils.mjs";
+import 'zx/globals';
+import { workingDirectory } from '../utils.mjs';
 
 // Start the local validator if it's not already running.
 await $`pnpm validator --force`;
 
 // Run the tests.
-cd(path.join(workingDirectory, "clients", "rust"));
-const hasSolfmt = await which("solfmt", { nothrow: true });
+cd(path.join(workingDirectory, 'clients', 'rust'));
+const hasSolfmt = await which('solfmt', { nothrow: true });
 if (hasSolfmt) {
   await $`cargo test-sbf ${argv._} 2>&1 | solfmt`;
 } else {

+ 6 - 6
utils/fsHelpers.ts

@@ -1,8 +1,8 @@
-import * as fs from "node:fs";
-import * as path from "node:path";
+import * as fs from 'node:fs';
+import * as path from 'node:path';
 
-import { Language } from "./getLanguage";
-import { logErrorAndExit } from "./getLogs";
+import { Language } from './getLanguage';
+import { logErrorAndExit } from './getLogs';
 
 export function createOrEmptyTargetDirectory(
   language: Language,
@@ -17,7 +17,7 @@ export function createOrEmptyTargetDirectory(
   } else {
     logErrorAndExit(
       language.errors.cannotOverrideDirectory.replace(
-        "$targetDirectory",
+        '$targetDirectory',
         targetDirectoryName
       )
     );
@@ -35,7 +35,7 @@ export function traverseGitDirectory(
   callback: (dir: string) => void
 ) {
   for (const filename of fs.readdirSync(directory)) {
-    if (filename === ".git") continue;
+    if (filename === '.git') continue;
     const fullpath = path.resolve(directory, filename);
     if (fs.lstatSync(fullpath).isDirectory()) {
       traverseGitDirectory(fullpath, callback);

+ 58 - 58
utils/getInputs.ts

@@ -1,13 +1,13 @@
-import chalk from "chalk";
-import * as fs from "node:fs";
-import { parseArgs } from "node:util";
-import prompts from "prompts";
+import chalk from 'chalk';
+import * as fs from 'node:fs';
+import { parseArgs } from 'node:util';
+import prompts from 'prompts';
 
-import { Language } from "./getLanguage";
-import { kebabCase } from "./stringHelpers";
-import { toMinorSolanaVersion } from "./solanaCli";
+import { Language } from './getLanguage';
+import { kebabCase } from './stringHelpers';
+import { toMinorSolanaVersion } from './solanaCli';
 
-export const allClients = ["js", "rust"] as const;
+export const allClients = ['js', 'rust'] as const;
 export type Client = (typeof allClients)[number];
 
 export type Inputs = {
@@ -16,7 +16,7 @@ export type Inputs = {
   organizationName: string;
   programAddress?: string;
   programCrateName: string;
-  programFramework: "shank" | "anchor";
+  programFramework: 'shank' | 'anchor';
   programName: string;
   rustClient: boolean;
   rustClientCrateName: string;
@@ -46,8 +46,8 @@ async function getInputsFromPrompts(
     shouldOverride?: boolean;
     organizationName?: string;
     programCrateName?: string;
-    programFramework?: "shank" | "anchor";
-    clients?: Array<"js" | "rust">;
+    programFramework?: 'shank' | 'anchor';
+    clients?: Array<'js' | 'rust'>;
     jsClientPackageName?: string;
     rustClientCrateName?: string;
   };
@@ -67,8 +67,8 @@ async function getInputsFromPrompts(
     if (promptInputs.programFramework)
       inputs.programFramework = promptInputs.programFramework;
     if (promptInputs.clients !== undefined) {
-      inputs.jsClient = promptInputs.clients.includes("js");
-      inputs.rustClient = promptInputs.clients.includes("rust");
+      inputs.jsClient = promptInputs.clients.includes('js');
+      inputs.rustClient = promptInputs.clients.includes('rust');
     }
     if (promptInputs.jsClientPackageName)
       inputs.jsClientPackageName = promptInputs.jsClientPackageName;
@@ -82,23 +82,23 @@ async function getInputsFromPrompts(
     const promptInputs: PromptInputs = await prompts(
       [
         {
-          name: "programName",
-          type: argInputs.programName ? null : "text",
+          name: 'programName',
+          type: argInputs.programName ? null : 'text',
           message: language.programName.message,
           initial: () => defaultInputs.programName,
         },
         {
-          name: "shouldOverride",
+          name: 'shouldOverride',
           type: (_, values) => {
             if (argInputs.shouldOverride) return null;
             defaultInputs = parsePromptInputs(values);
             return canSkipEmptying(defaultInputs.targetDirectoryName)
               ? null
-              : "toggle";
+              : 'toggle';
           },
           message: () => {
             const dirForPrompt =
-              defaultInputs.targetDirectoryName === "."
+              defaultInputs.targetDirectoryName === '.'
                 ? language.shouldOverride.dirForPrompts!.current
                 : `${language.shouldOverride.dirForPrompts!.target} "${defaultInputs.targetDirectoryName}"`;
 
@@ -109,25 +109,25 @@ async function getInputsFromPrompts(
           inactive: language.defaultToggleOptions.inactive,
         },
         {
-          name: "overwriteChecker",
+          name: 'overwriteChecker',
           type: (_, values) => {
             if (values.shouldOverride === false) {
               throw new Error(
-                chalk.red("✖") + ` ${language.errors.operationCancelled}`
+                chalk.red('✖') + ` ${language.errors.operationCancelled}`
               );
             }
             return null;
           },
         },
         {
-          name: "organizationName",
-          type: argInputs.organizationName ? null : "text",
+          name: 'organizationName',
+          type: argInputs.organizationName ? null : 'text',
           message: language.organizationName.message,
           initial: () => defaultInputs.organizationName,
         },
         {
-          name: "programCrateName",
-          type: argInputs.programCrateName ? null : "text",
+          name: 'programCrateName',
+          type: argInputs.programCrateName ? null : 'text',
           message: language.programCrateName.message,
           initial: (_, values) => {
             defaultInputs = parsePromptInputs(values);
@@ -135,8 +135,8 @@ async function getInputsFromPrompts(
           },
         },
         {
-          name: "programFramework",
-          type: argInputs.programFramework ? null : "select",
+          name: 'programFramework',
+          type: argInputs.programFramework ? null : 'select',
           message: language.programFramework.message,
           hint: language.instructions.select,
           initial: 0,
@@ -144,24 +144,24 @@ async function getInputsFromPrompts(
             {
               title: language.programFramework.selectOptions!.shank.title,
               description: language.programFramework.selectOptions!.shank.desc,
-              value: "shank",
+              value: 'shank',
             },
             {
               title: language.programFramework.selectOptions!.anchor.title,
               description: language.programFramework.selectOptions!.anchor.desc,
-              value: "anchor",
+              value: 'anchor',
               disabled: true,
             },
           ],
         },
         {
-          name: "clients",
+          name: 'clients',
           type: () => {
             const hasSelectedClients = [
               argInputs.jsClient,
               argInputs.rustClient,
-            ].every((client) => typeof client === "boolean");
-            return hasSelectedClients ? null : "multiselect";
+            ].every((client) => typeof client === 'boolean');
+            return hasSelectedClients ? null : 'multiselect';
           },
           message: language.clients.message,
           hint: language.clients.hint,
@@ -174,21 +174,21 @@ async function getInputsFromPrompts(
           })),
         },
         {
-          name: "jsClientPackageName",
+          name: 'jsClientPackageName',
           type: (_, values) => {
             if (argInputs.jsClientPackageName) return null;
             defaultInputs = parsePromptInputs(values);
-            return defaultInputs.jsClient ? "text" : null;
+            return defaultInputs.jsClient ? 'text' : null;
           },
           message: language.jsClientPackageName.message,
           initial: () => defaultInputs.jsClientPackageName,
         },
         {
-          name: "rustClientCrateName",
+          name: 'rustClientCrateName',
           type: (_, values) => {
             if (argInputs.rustClientCrateName) return null;
             defaultInputs = parsePromptInputs(values);
-            return defaultInputs.rustClient ? "text" : null;
+            return defaultInputs.rustClient ? 'text' : null;
           },
           message: language.rustClientCrateName.message,
           initial: () => defaultInputs.rustClientCrateName,
@@ -197,14 +197,14 @@ async function getInputsFromPrompts(
       {
         onCancel: () => {
           throw new Error(
-            chalk.red("✖") + ` ${language.errors.operationCancelled}`
+            chalk.red('✖') + ` ${language.errors.operationCancelled}`
           );
         },
       }
     );
 
     // Add a line break after the prompts
-    console.log("");
+    console.log('');
 
     return parsePromptInputs(promptInputs);
   } catch (cancelled) {
@@ -217,7 +217,7 @@ function getInputsFromArgs(language: Language): Partial<Inputs> {
   type ArgInputs = {
     address?: string;
     anchorProgram: boolean;
-    clients: Array<"js" | "rust">;
+    clients: Array<'js' | 'rust'>;
     force: boolean;
     noClients: boolean;
     organizationName?: string;
@@ -247,17 +247,17 @@ function getInputsFromArgs(language: Language): Partial<Inputs> {
     if (argInputs.useDefaults) inputs.useDefaults = true;
 
     if (argInputs.anchorProgram) {
-      inputs.programFramework = "anchor";
+      inputs.programFramework = 'anchor';
     } else if (argInputs.shankProgram) {
-      inputs.programFramework = "shank";
+      inputs.programFramework = 'shank';
     }
 
     if (argInputs.noClients) {
       inputs.jsClient = false;
       inputs.rustClient = false;
     } else if (argInputs.clients) {
-      inputs.jsClient = argInputs.clients.includes("js");
-      inputs.rustClient = argInputs.clients.includes("rust");
+      inputs.jsClient = argInputs.clients.includes('js');
+      inputs.rustClient = argInputs.clients.includes('rust');
     }
 
     return inputs;
@@ -267,15 +267,15 @@ function getInputsFromArgs(language: Language): Partial<Inputs> {
   const { values: options, positionals } = parseArgs({
     args,
     options: {
-      address: { type: "string" },
-      anchor: { type: "boolean" },
-      client: { type: "string", multiple: true },
-      default: { type: "boolean", short: "d" },
-      force: { type: "boolean" },
-      "no-clients": { type: "boolean" },
-      org: { type: "string" },
-      shank: { type: "boolean" },
-      solana: { type: "string" },
+      address: { type: 'string' },
+      anchor: { type: 'boolean' },
+      client: { type: 'string', multiple: true },
+      default: { type: 'boolean', short: 'd' },
+      force: { type: 'boolean' },
+      'no-clients': { type: 'boolean' },
+      org: { type: 'string' },
+      shank: { type: 'boolean' },
+      solana: { type: 'string' },
     },
     strict: false,
   });
@@ -285,7 +285,7 @@ function getInputsFromArgs(language: Language): Partial<Inputs> {
     anchorProgram: options.anchor ?? false,
     clients: options.client,
     force: options.force ?? false,
-    noClients: options["no-clients"] ?? false,
+    noClients: options['no-clients'] ?? false,
     organizationName: options.org,
     programName: positionals[1],
     shankProgram: options.shank ?? false,
@@ -297,13 +297,13 @@ function getInputsFromArgs(language: Language): Partial<Inputs> {
 
 export function getDefaultInputs(partialInputs: Partial<Inputs>): Inputs {
   const organizationName = kebabCase(
-    partialInputs.organizationName ?? "solana-program"
+    partialInputs.organizationName ?? 'solana-program'
   );
   const parsedTargetDirectoryName = partialInputs.targetDirectoryName
-    ? partialInputs.targetDirectoryName.split("/").pop()
-    : "";
+    ? partialInputs.targetDirectoryName.split('/').pop()
+    : '';
   const programName = kebabCase(
-    partialInputs.programName ?? (parsedTargetDirectoryName || "my-program")
+    partialInputs.programName ?? (parsedTargetDirectoryName || 'my-program')
   );
   const programCrateName =
     partialInputs.programCrateName ?? `${organizationName}-${programName}`;
@@ -313,7 +313,7 @@ export function getDefaultInputs(partialInputs: Partial<Inputs>): Inputs {
     jsClientPackageName: `@${organizationName}/${programName}`,
     organizationName,
     programCrateName,
-    programFramework: "shank",
+    programFramework: 'shank',
     programName,
     rustClient: true,
     rustClientCrateName: `${programCrateName}-client`,
@@ -333,7 +333,7 @@ function canSkipEmptying(dir: fs.PathLike) {
   if (files.length === 0) {
     return true;
   }
-  if (files.length === 1 && files[0] === ".git") {
+  if (files.length === 1 && files[0] === '.git') {
     return true;
   }
 

+ 13 - 13
utils/getLanguage.ts

@@ -1,5 +1,5 @@
-import * as fs from "node:fs";
-import * as path from "node:path";
+import * as fs from 'node:fs';
+import * as path from 'node:path';
 
 interface LanguageItem {
   hint?: string;
@@ -62,14 +62,14 @@ function linkLocale(locale: string) {
   try {
     // @ts-ignore
     switch (Intl.getCanonicalLocales(locale)[0]) {
-      case "zh-TW":
-      case "zh-HK":
-      case "zh-MO":
-        return "zh-Hant";
+      case 'zh-TW':
+      case 'zh-HK':
+      case 'zh-MO':
+        return 'zh-Hant';
         break;
-      case "zh-CN":
-      case "zh-SG":
-        return "zh-Hans";
+      case 'zh-CN':
+      case 'zh-SG':
+        return 'zh-Hans';
         break;
       default:
         return locale;
@@ -86,9 +86,9 @@ function getLocale() {
     process.env.LC_MESSAGES ||
     process.env.LANG ||
     Intl.DateTimeFormat().resolvedOptions().locale || // Built-in ECMA-402 support
-    "en-US"; // Default fallback
+    'en-US'; // Default fallback
 
-  return linkLocale(shellLocale.split(".")[0].replace("_", "-"));
+  return linkLocale(shellLocale.split('.')[0].replace('_', '-'));
 }
 
 export function getLanguage() {
@@ -96,13 +96,13 @@ export function getLanguage() {
 
   // Note here __dirname would not be transpiled,
   // so it refers to the __dirname of the file `<repositoryRoot>/outfile.cjs`
-  const localesRoot = path.resolve(__dirname, "locales");
+  const localesRoot = path.resolve(__dirname, 'locales');
   const languageFilePath = path.resolve(localesRoot, `${locale}.json`);
   const doesLanguageExist = fs.existsSync(languageFilePath);
 
   const lang: Language = doesLanguageExist
     ? require(languageFilePath)
-    : require(path.resolve(localesRoot, "en-US.json"));
+    : require(path.resolve(localesRoot, 'en-US.json'));
 
   return lang;
 }

+ 17 - 17
utils/getLogs.ts

@@ -1,20 +1,20 @@
-import chalk from "chalk";
+import chalk from 'chalk';
 
 // @ts-ignore
-import gradient from "gradient-string";
+import gradient from 'gradient-string';
 
-import type { RenderContext } from "./getRenderContext";
+import type { RenderContext } from './getRenderContext';
 
 export function logBanner() {
   console.log(`\n${getBanner()}\n`);
 }
 
 export function logSuccess(message: string) {
-  console.log(chalk.green("✔︎") + ` ${message}`);
+  console.log(chalk.green('✔︎') + ` ${message}`);
 }
 
 export function logError(message: string) {
-  console.log(chalk.red("✖") + ` ${message}`);
+  console.log(chalk.red('✖') + ` ${message}`);
 }
 
 export function logErrorAndExit(message: string) {
@@ -35,20 +35,20 @@ export async function logStep<T>(title: string, callback: () => T): Promise<T> {
 }
 
 export function logDone(ctx: RenderContext) {
-  console.log(chalk.green("✔︎") + ` ${ctx.language.infos.done}\n`);
+  console.log(chalk.green('✔︎') + ` ${ctx.language.infos.done}\n`);
 
   // Log next steps: Cd into the target directory.
   if (ctx.targetDirectory !== ctx.currentDirectory) {
-    const cdCommand = `cd ${ctx.targetDirectoryName.includes(" ") ? `"${ctx.targetDirectoryName}"` : ctx.targetDirectoryName}`;
+    const cdCommand = `cd ${ctx.targetDirectoryName.includes(' ') ? `"${ctx.targetDirectoryName}"` : ctx.targetDirectoryName}`;
     console.log(`  ${chalk.bold(chalk.green(cdCommand))}`);
   }
 
   // Log next steps: Install dependencies.
-  const installCommand = ctx.getNpmCommand("install");
+  const installCommand = ctx.getNpmCommand('install');
   console.log(`  ${chalk.bold(chalk.green(installCommand))}`);
 
   // Log next steps: Generate Idls and clients.
-  const generateCommand = ctx.getNpmCommand("generate");
+  const generateCommand = ctx.getNpmCommand('generate');
   console.log(`  ${chalk.bold(chalk.green(generateCommand))}`);
 
   // Final line break.
@@ -61,30 +61,30 @@ export async function spinner<T>(
   title: string | (() => T),
   callback?: () => T
 ): Promise<T> {
-  if (typeof title == "function") {
+  if (typeof title == 'function') {
     callback = title;
-    title = "";
+    title = '';
   }
   let i = 0;
   const spin = () =>
-    process.stderr.write(`  ${"⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"[i++ % 10]} ${title}\r`);
+    process.stderr.write(`  ${'⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'[i++ % 10]} ${title}\r`);
   const id = setInterval(spin, 100);
   let result: T;
   try {
     result = await callback!();
   } finally {
     clearInterval(id as NodeJS.Timeout);
-    process.stderr.write(" ".repeat(process.stdout.columns - 1) + "\r");
+    process.stderr.write(' '.repeat(process.stdout.columns - 1) + '\r');
   }
   return result;
 }
 
 function getBanner() {
-  const textBanner = "Create Solana Program";
+  const textBanner = 'Create Solana Program';
   const gradientBanner = chalk.bold(
-    gradient(["#89d7c8", "#dc7a8b"])(textBanner, {
-      interpolation: "hsv",
-      hsvSpin: "long",
+    gradient(['#89d7c8', '#dc7a8b'])(textBanner, {
+      interpolation: 'hsv',
+      hsvSpin: 'long',
     })
   );
 

+ 6 - 6
utils/getPackageManager.ts

@@ -1,8 +1,8 @@
-export type PackageManager = "npm" | "yarn" | "pnpm";
+export type PackageManager = 'npm' | 'yarn' | 'pnpm';
 
 export function getPackageManager(): PackageManager {
   // For now, we only support pnpm.
-  return "pnpm";
+  return 'pnpm';
 }
 
 export function getPackageManagerCommand(
@@ -10,16 +10,16 @@ export function getPackageManagerCommand(
   scriptName: string,
   args?: string
 ) {
-  if (scriptName === "install") {
-    return packageManager === "yarn" ? "yarn" : `${packageManager} install`;
+  if (scriptName === 'install') {
+    return packageManager === 'yarn' ? 'yarn' : `${packageManager} install`;
   }
 
   if (args) {
-    return packageManager === "npm"
+    return packageManager === 'npm'
       ? `npm run ${scriptName} -- ${args}`
       : `${packageManager} ${scriptName} ${args}`;
   } else {
-    return packageManager === "npm"
+    return packageManager === 'npm'
       ? `npm run ${scriptName}`
       : `${packageManager} ${scriptName}`;
   }

+ 10 - 10
utils/getRenderContext.ts

@@ -1,15 +1,15 @@
-import * as path from "node:path";
+import * as path from 'node:path';
 
-import { Client, Inputs, allClients } from "./getInputs";
-import { Language } from "./getLanguage";
+import { Client, Inputs, allClients } from './getInputs';
+import { Language } from './getLanguage';
 import {
   PackageManager,
   getPackageManager,
   getPackageManagerCommand,
-} from "./getPackageManager";
-import { toMinorSolanaVersion } from "./solanaCli";
+} from './getPackageManager';
+import { toMinorSolanaVersion } from './solanaCli';
 
-export type RenderContext = Omit<Inputs, "programAddress" | "solanaVersion"> & {
+export type RenderContext = Omit<Inputs, 'programAddress' | 'solanaVersion'> & {
   clientDirectory: string;
   clients: Client[];
   currentDirectory: string;
@@ -39,18 +39,18 @@ export function getRenderContext({
   const clients = allClients.flatMap((client) =>
     inputs[`${client}Client`] ? [client] : []
   );
-  const getNpmCommand: RenderContext["getNpmCommand"] = (...args) =>
+  const getNpmCommand: RenderContext['getNpmCommand'] = (...args) =>
     getPackageManagerCommand(packageManager, ...args);
 
   // Directories.
-  const templateDirectory = path.resolve(__dirname, "template");
+  const templateDirectory = path.resolve(__dirname, 'template');
   const currentDirectory = process.cwd();
   const targetDirectory = path.join(
     currentDirectory,
     inputs.targetDirectoryName
   );
-  const programDirectory = path.join(targetDirectory, "program");
-  const clientDirectory = path.join(targetDirectory, "client");
+  const programDirectory = path.join(targetDirectory, 'program');
+  const clientDirectory = path.join(targetDirectory, 'client');
 
   return {
     ...inputs,

+ 5 - 5
utils/objectHelpers.ts

@@ -1,5 +1,5 @@
 const isObject = (val: unknown): val is object =>
-  !!val && typeof val === "object";
+  !!val && typeof val === 'object';
 
 const mergeArrayWithDedupe = (a: any[], b: any[]) =>
   Array.from(new Set([...a, ...b]));
@@ -31,10 +31,10 @@ export function sortDependencies<T extends object = object>(packageJson: T): T {
   const sorted = {};
 
   const depTypes = [
-    "dependencies",
-    "devDependencies",
-    "peerDependencies",
-    "optionalDependencies",
+    'dependencies',
+    'devDependencies',
+    'peerDependencies',
+    'optionalDependencies',
   ];
 
   for (const depType of depTypes) {

+ 22 - 22
utils/renderTemplates.ts

@@ -1,16 +1,16 @@
-import * as fs from "node:fs";
-import * as path from "node:path";
-import nunjucks, { ConfigureOptions } from "nunjucks";
+import * as fs from 'node:fs';
+import * as path from 'node:path';
+import nunjucks, { ConfigureOptions } from 'nunjucks';
 
-import { RenderContext } from "./getRenderContext";
-import { deepMerge, sortDependencies } from "./objectHelpers";
+import { RenderContext } from './getRenderContext';
+import { deepMerge, sortDependencies } from './objectHelpers';
 import {
   camelCase,
   kebabCase,
   pascalCase,
   snakeCase,
   titleCase,
-} from "./stringHelpers";
+} from './stringHelpers';
 
 /**
  * Renders a template folder/file to the provided destination,
@@ -28,7 +28,7 @@ export function renderTemplate(ctx: RenderContext, src: string, dest: string) {
   // Recursively render directories.
   if (stats.isDirectory()) {
     // Skip node_module.
-    if (path.basename(src) === "node_modules") return;
+    if (path.basename(src) === 'node_modules') return;
 
     // Render its subdirectories and files recursively.
     fs.mkdirSync(dest, { recursive: true });
@@ -41,25 +41,25 @@ export function renderTemplate(ctx: RenderContext, src: string, dest: string) {
   const filename = path.basename(src);
 
   // Merge package.json files.
-  if (filename === "package.json" && fs.existsSync(dest)) {
-    const existing = JSON.parse(fs.readFileSync(dest, "utf8"));
-    const newPackage = JSON.parse(fs.readFileSync(src, "utf8"));
+  if (filename === 'package.json' && fs.existsSync(dest)) {
+    const existing = JSON.parse(fs.readFileSync(dest, 'utf8'));
+    const newPackage = JSON.parse(fs.readFileSync(src, 'utf8'));
     const pkg = sortDependencies(deepMerge(existing, newPackage));
-    fs.writeFileSync(dest, JSON.stringify(pkg, null, 2) + "\n");
+    fs.writeFileSync(dest, JSON.stringify(pkg, null, 2) + '\n');
     return;
   }
 
   // Append to existing .gitignore.
-  if (filename === ".gitignore" && fs.existsSync(dest)) {
-    const existing = fs.readFileSync(dest, "utf8");
-    const newGitignore = fs.readFileSync(src, "utf8");
-    fs.writeFileSync(dest, existing + "\n" + newGitignore);
+  if (filename === '.gitignore' && fs.existsSync(dest)) {
+    const existing = fs.readFileSync(dest, 'utf8');
+    const newGitignore = fs.readFileSync(src, 'utf8');
+    fs.writeFileSync(dest, existing + '\n' + newGitignore);
     return;
   }
 
   // Render nunjucks templates.
-  if (filename.endsWith(".njk")) {
-    dest = dest.replace(/\.njk$/, "");
+  if (filename.endsWith('.njk')) {
+    dest = dest.replace(/\.njk$/, '');
     fs.writeFileSync(dest, resolveNunjunksTemplate(src, ctx));
     fs.chmodSync(dest, stats.mode);
     return;
@@ -80,10 +80,10 @@ function resolveNunjunksTemplate(
     autoescape: false,
     ...options,
   });
-  env.addFilter("pascalCase", pascalCase);
-  env.addFilter("camelCase", camelCase);
-  env.addFilter("snakeCase", snakeCase);
-  env.addFilter("kebabCase", kebabCase);
-  env.addFilter("titleCase", titleCase);
+  env.addFilter('pascalCase', pascalCase);
+  env.addFilter('camelCase', camelCase);
+  env.addFilter('snakeCase', snakeCase);
+  env.addFilter('kebabCase', kebabCase);
+  env.addFilter('titleCase', titleCase);
   return env.render(filename, context);
 }

+ 5 - 5
utils/runCommands.ts

@@ -1,4 +1,4 @@
-import { spawn, ChildProcess, SpawnOptions } from "node:child_process";
+import { spawn, ChildProcess, SpawnOptions } from 'node:child_process';
 
 export function spawnCommand(
   command: string,
@@ -10,7 +10,7 @@ export function spawnCommand(
 
 export async function hasCommand(command: string): Promise<boolean> {
   try {
-    await waitForCommand(spawnCommand("which", [command], { stdio: "ignore" }));
+    await waitForCommand(spawnCommand('which', [command], { stdio: 'ignore' }));
     return true;
   } catch {
     return false;
@@ -19,7 +19,7 @@ export async function hasCommand(command: string): Promise<boolean> {
 
 export async function waitForCommand(child: ChildProcess): Promise<number> {
   return new Promise((resolve, reject) => {
-    child.on("close", (code) => {
+    child.on('close', (code) => {
       if (code !== 0) {
         const message = `$(${child}) exited with code ${code}`;
         reject(new Error(message));
@@ -33,9 +33,9 @@ export async function waitForCommand(child: ChildProcess): Promise<number> {
 export async function readStdout(child: ChildProcess): Promise<string[]> {
   const stdout: string[] = [];
   return new Promise((resolve) => {
-    child.stdout?.on("data", (data) => {
+    child.stdout?.on('data', (data) => {
       stdout.push(data.toString());
     });
-    child.on("close", () => resolve(stdout));
+    child.on('close', () => resolve(stdout));
   });
 }

+ 14 - 14
utils/solanaCli.ts

@@ -1,26 +1,26 @@
-import { Language } from "./getLanguage";
+import { Language } from './getLanguage';
 import {
   hasCommand,
   readStdout,
   spawnCommand,
   waitForCommand,
-} from "./runCommands";
+} from './runCommands';
 
 export async function detectSolanaVersion(language: Language): Promise<string> {
-  const hasSolanaCli = await hasCommand("solana");
+  const hasSolanaCli = await hasCommand('solana');
   if (!hasSolanaCli) {
     throw new Error(
-      language.errors.solanaCliNotFound.replace("$command", "solana")
+      language.errors.solanaCliNotFound.replace('$command', 'solana')
     );
   }
 
-  const child = spawnCommand("solana", ["--version"]);
+  const child = spawnCommand('solana', ['--version']);
   const [stdout] = await Promise.all([
     readStdout(child),
     waitForCommand(child),
   ]);
 
-  const version = stdout.join("").match(/(\d+\.\d+\.\d+)/)?.[1];
+  const version = stdout.join('').match(/(\d+\.\d+\.\d+)/)?.[1];
   return version!;
 }
 
@@ -31,7 +31,7 @@ export function toMinorSolanaVersion(
   const validVersion = version.match(/^(\d+\.\d+)/);
   if (!validVersion) {
     throw new Error(
-      language.errors.invalidSolanaVersion.replace("$version", version)
+      language.errors.invalidSolanaVersion.replace('$version', version)
     );
   }
   return validVersion[0];
@@ -41,18 +41,18 @@ export async function generateKeypair(
   language: Language,
   outfile: string
 ): Promise<string> {
-  const hasSolanaKeygen = await hasCommand("solana-keygen");
+  const hasSolanaKeygen = await hasCommand('solana-keygen');
   if (!hasSolanaKeygen) {
     throw new Error(
-      language.errors.solanaCliNotFound.replace("$command", "solana-keygen")
+      language.errors.solanaCliNotFound.replace('$command', 'solana-keygen')
     );
   }
 
   // Run the solana-keygen command to generate a new keypair.
-  const child = spawnCommand("solana-keygen", [
-    "new",
-    "--no-bip39-passphrase",
-    "--outfile",
+  const child = spawnCommand('solana-keygen', [
+    'new',
+    '--no-bip39-passphrase',
+    '--outfile',
     outfile,
   ]);
 
@@ -63,7 +63,7 @@ export async function generateKeypair(
   ]);
 
   // Update the render context with the generated address.
-  const address = stdout.join("").match(/pubkey: (\w+)/)?.[1];
+  const address = stdout.join('').match(/pubkey: (\w+)/)?.[1];
   if (!address) {
     throw new Error(language.errors.solanaKeygenFailed);
   }

+ 5 - 5
utils/stringHelpers.ts

@@ -5,15 +5,15 @@ export function capitalize(str: string): string {
 
 export function titleCase(str: string): string {
   return str
-    .replace(/([A-Z])/g, " $1")
+    .replace(/([A-Z])/g, ' $1')
     .split(/[-_\s+.]/)
     .filter((word) => word.length > 0)
     .map(capitalize)
-    .join(" ");
+    .join(' ');
 }
 
 export function pascalCase(str: string): string {
-  return titleCase(str).split(" ").join("");
+  return titleCase(str).split(' ').join('');
 }
 
 export function camelCase(str: string): string {
@@ -23,9 +23,9 @@ export function camelCase(str: string): string {
 }
 
 export function kebabCase(str: string): string {
-  return titleCase(str).split(" ").join("-").toLowerCase();
+  return titleCase(str).split(' ').join('-').toLowerCase();
 }
 
 export function snakeCase(str: string): string {
-  return titleCase(str).split(" ").join("_").toLowerCase();
+  return titleCase(str).split(' ').join('_').toLowerCase();
 }

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio