UNPKG

zksync-cli

Version:

CLI tool that simplifies the process of developing applications and interacting with the ZKsync network

221 lines 9.59 kB
import chalk from "chalk"; import fs from "fs"; import inquirer from "inquirer"; import ora from "ora"; import path from "path"; import { copyRecursiveSync, fileOrDirExists } from "../../utils/files.js"; import { cloneRepo } from "../../utils/git.js"; import { executeCommand } from "../../utils/helpers.js"; import Logger from "../../utils/logger.js"; import { packageManagers } from "../../utils/packageManager.js"; export const getUniqueValues = (arr) => { return Array.from(new Set(arr)); }; export const askForTemplate = async (templates) => { const { name: templateValue } = await inquirer.prompt([ { message: "Template", name: "name", type: "list", choices: templates, required: true, }, ]); const template = templates.find((t) => t.value === templateValue); return template; }; export const askForPackageManager = async () => { const { packageManager } = await inquirer.prompt([ { message: "Package manager", name: "packageManager", type: "list", choices: ["npm", "pnpm", "yarn", "bun"], required: true, }, ]); return packageManager; }; const setupEnv = (folderLocation, env) => { const envExamplePath = path.join(folderLocation, ".env.example"); const envPath = path.join(folderLocation, ".env"); // Initialize finalEnvContent with the content from .env if it exists or an empty string let finalEnvContent = fs.existsSync(envPath) ? fs.readFileSync(envPath, "utf8") : ""; // Keep track of what keys we've seen in the final .env content const seenKeys = {}; // Extract and remember the keys that are already in the final .env content finalEnvContent.split("\n").forEach((line) => { const match = line.match(/^\s*([\w.-]+)\s*=/); if (match) { seenKeys[match[1]] = true; } }); if (fs.existsSync(envExamplePath)) { // .env.example exists, so iterate over its content const envExampleContent = fs.readFileSync(envExamplePath, "utf8"); const lines = envExampleContent.split("\n"); lines.forEach((line) => { const match = line.match(/^\s*([\w.-]+)\s*=(.*)$/); if (match) { const key = match[1]; // Replace only if the key from .env.example is in the env object and hasn't been seen in the .env if (!seenKeys[key] && Object.prototype.hasOwnProperty.call(env, key)) { line = `${key}=${env[key]}`; seenKeys[key] = true; // Mark as seen } } finalEnvContent += line + "\n"; }); } // Append new keys from the env object that weren't in .env.example for (const [key, value] of Object.entries(env)) { if (!seenKeys[key]) { finalEnvContent += `${key}=${value}\n`; } } // Write the final .env content fs.writeFileSync(envPath, finalEnvContent.trim() + "\n"); }; export const setupTemplate = async (template, folderLocation, env, packageManager) => { Logger.info(`\nSetting up template in ${chalk.magentaBright(folderLocation)}...`); const typedTemplate = template; if (typedTemplate.framework === "Foundry") { await setupFoundryProject(template, folderLocation); return; } await setupHardhatProject(template, folderLocation); if (Object.keys(env).length > 0) { await setupEnvironmentVariables(folderLocation, env); } if (packageManager) { await installDependencies(packageManager, folderLocation); } else { throw new Error("Package manager is required to install dependencies."); } }; // Sets up a foundry project by initializing it with the specified template. // Primarily only used for foundry quickstart templates. const setupFoundryProject = async (template, folderLocation) => { const spinner = ora("Initializing foundry project...").start(); try { const isInstalled = await executeCommand("forge --version", { silent: true }); // TODO: https://github.com/matter-labs/zksync-cli/issues/137 if (!isInstalled) { spinner.fail("Forge is not installed from the `foundry-zksync` repository. Please visit the official installation guide at https://github.com/matter-labs/foundry-zksync and follow the instructions to install it. Once installed, try running the command again."); return; } const cloneTempPath = path.join(folderLocation, "___temp"); await executeCommand(`forge init --template ${template.git} ${cloneTempPath}`, { silent: true }); const templatePath = path.join(cloneTempPath, template.path || ""); if (!fileOrDirExists(templatePath)) { throw new Error(`The specified template path does not exist: ${templatePath}`); } copyRecursiveSync(templatePath, folderLocation); fs.rmSync(cloneTempPath, { recursive: true, force: true }); spinner.succeed("Foundry project initialized successfully."); } catch (error) { spinner.fail("Failed to initialize foundry project"); throw error; } }; // Sets up a Hardhat project by cloning the specified template and copying it to the specified folder location. const setupHardhatProject = async (template, folderLocation) => { if (!template.path) { const spinner = ora("Cloning template...").start(); try { await cloneRepo(template.git, folderLocation, { silent: true }); try { fs.rmSync(path.join(folderLocation, ".git"), { recursive: true }); } catch { Logger.warn("Failed to remove .git folder. Make sure to remove it manually before pushing to a new repo."); } try { const githubFolderLocation = path.join(folderLocation, ".github"); if (fileOrDirExists(githubFolderLocation)) { fs.rmSync(githubFolderLocation, { recursive: true }); } } catch { Logger.warn("Failed to remove .github folder. Make sure to remove it manually before pushing to a new repo."); } spinner.succeed("Cloned template"); } catch (error) { spinner.fail("Failed to clone template"); throw error; } } else { // We need to firstly clone the repo to a temp folder // then copy required folder to the main folder // then remove the temp folder const cloneTempPath = path.join(folderLocation, "___temp"); const spinner = ora("Cloning template...").start(); try { await cloneRepo(template.git, cloneTempPath, { silent: true }); const templatePath = path.join(cloneTempPath, template.path); if (fileOrDirExists(templatePath)) { try { copyRecursiveSync(templatePath, folderLocation); fs.rmSync(cloneTempPath, { recursive: true, force: true }); } catch { throw new Error("An error occurred while copying the template"); } } else { throw new Error(`The specified template path does not exist: ${templatePath}`); } spinner.succeed("Cloned template"); } catch (error) { spinner.fail("Failed to clone template"); throw error; } } }; // Sets up environment variables in the specified folder location. const setupEnvironmentVariables = async (folderLocation, env) => { const spinner = ora("Setting up environment variables...").start(); try { setupEnv(folderLocation, env); } catch (error) { spinner.fail("Failed to set up environment variables"); throw error; } spinner.succeed("Environment variables set up"); }; // Install dependencies with the specified package manager in the specified folder location. const installDependencies = async (packageManager, folderLocation) => { const spinner = ora(`Installing dependencies with ${chalk.bold(packageManager)}... This may take a couple minutes.`).start(); if (await packageManagers[packageManager].isInstalled()) { try { await executeCommand(packageManagers[packageManager].install(), { cwd: folderLocation, silent: true }); } catch (error) { spinner.fail("Failed to install dependencies"); throw error; } spinner.succeed("Dependencies installed"); } else { spinner.fail(`${chalk.bold(packageManager)} is not installed. After installing it, run \`${chalk.blueBright(packageManagers[packageManager].install())}\` in the project folder.`); } }; export const successfulMessage = { start: (folderName) => { Logger.info(`\n${chalk.green("🎉 All set up! 🎉")}\n`); Logger.info("--------------------------\n", { noFormat: true }); Logger.info(`${chalk.magentaBright("Navigate to your project:")} ${chalk.blueBright(`cd ${folderName}`)}\n`); }, end: (folderName) => { Logger.info(`${chalk.magentaBright("\nFurther Reading:")} - Check out the README file in the project location for more details: ${path.join(folderName, "README.md")}\n`); Logger.info("--------------------------", { noFormat: true }); }, }; //# sourceMappingURL=utils.js.map