UNPKG

hardhat-deploy

Version:

Hardhat plugin for replicable smart contract deployments and easy testing across multiple EVM chains, with support for proxies, diamonds, named accounts, and deployment fixtures

194 lines 7.37 kB
#!/usr/bin/env node import { Command } from 'commander'; import { readFileSync, readdirSync, mkdirSync, copyFileSync, existsSync, writeFileSync, statSync } from 'fs'; import { join, dirname, basename } from 'path'; import { fileURLToPath } from 'url'; import * as readline from 'readline'; import pkg from '../package.json' with { type: 'json' }; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const program = new Command(); // Get the current version of hardhat-deploy const getHardhatDeployVersion = () => { return pkg.version; }; const askFolder = async () => { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); return new Promise((resolve) => { rl.question('Enter folder path (default: ./): ', (answer) => { rl.close(); resolve(answer.trim() || './'); }); }); }; const askAutoInstall = async () => { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); return new Promise((resolve) => { rl.question('Auto-install dependencies with pnpm? (Y/n): ', (answer) => { rl.close(); const trimmed = answer.trim().toLowerCase(); resolve(trimmed === '' || trimmed === 'y' || trimmed === 'yes'); }); }); }; const isFolderEmpty = (folderPath) => { if (!existsSync(folderPath)) { return true; } try { const files = readdirSync(folderPath); return files.length === 0; } catch (error) { // If we can't read the directory, treat it as not empty return false; } }; const copyFile = (source, target, replacements = {}, gitignorePatterns = []) => { const fileName = basename(source); // Check if file should be skipped based on gitignore patterns for (const pattern of gitignorePatterns) { if (fileName === pattern || fileName.endsWith(pattern.replace('*', ''))) { return; // Skip this file } } let content = readFileSync(source, 'utf-8'); // Apply replacements for (const [search, replace] of Object.entries(replacements)) { content = content.replaceAll(search, replace); } mkdirSync(dirname(target), { recursive: true }); // For binary files, just copy as-is if (source.endsWith('.lock') || source.endsWith('.so') || source.endsWith('.wasm')) { copyFileSync(source, target); } else { writeFileSync(target, content, 'utf-8'); } }; const parseGitignore = (gitignorePath) => { if (!existsSync(gitignorePath)) { return []; } const content = readFileSync(gitignorePath, 'utf-8'); return content .split('\n') .map((line) => line.trim()) .filter((line) => line && !line.startsWith('#')); }; const copyFolder = (source, target, replacements = {}, gitignorePatterns = []) => { if (!existsSync(target)) { mkdirSync(target, { recursive: true }); } const files = readdirSync(source); files.forEach((file) => { const sourcePath = join(source, file); const targetPath = join(target, file); const stat = statSync(sourcePath); if (stat.isDirectory()) { // Check if directory should be skipped based on gitignore patterns const shouldSkip = gitignorePatterns.some((pattern) => file === pattern.replace('/', '') || (pattern.startsWith('/') && file === pattern.slice(1))); if (!shouldSkip) { copyFolder(sourcePath, targetPath, replacements, gitignorePatterns); } } else { copyFile(sourcePath, targetPath, replacements, gitignorePatterns); } }); }; const generateProject = (targetFolder, projectName) => { // find template in published package const templatePath = join(__dirname, '../templates/basic'); const gitignorePath = join(templatePath, '.gitignore'); // Parse gitignore patterns const gitignorePatterns = parseGitignore(gitignorePath); // Determine project name from folder or use placeholder const folderName = projectName || basename(targetFolder === './' ? process.cwd() : targetFolder); // Get the current version of hardhat-deploy const hardhatDeployVersion = getHardhatDeployVersion(); const replacements = { 'template-hardhat-node-test-runner': `${folderName}`, 'workspace:*': hardhatDeployVersion, }; console.log(`Generating project in: ${targetFolder}`); copyFolder(templatePath, targetFolder, replacements, gitignorePatterns); console.log('✓ Project initialized successfully!'); }; const runPnpmInstall = async (folderPath) => { console.log(`Installing dependencies...`); const { spawn } = await import('child_process'); return new Promise((resolve, reject) => { // Use --ignore-workspace to ensure dependencies are installed locally // This prevents pnpm from treating the target folder as part of a parent workspace const pnpm = spawn('pnpm', ['install', '--ignore-workspace', `--no-frozen-lockfile`], { cwd: folderPath, stdio: 'inherit', }); pnpm.on('close', (code) => { if (code === 0) { console.log('✓ Dependencies installed successfully!'); resolve(); } else { reject(new Error(`pnpm install failed with exit code ${code}`)); } }); pnpm.on('error', (error) => { reject(error); }); }); }; program.name('hardhat-deploy').description('CLI for hardhat-deploy').version(pkg.version); program .command('init') .argument('[folder]', 'folder to initialize the project in') .option('--install', 'auto-install dependencies with pnpm') .description('Initialize a new hardhat-deploy project') .action(async (folder, options) => { let targetFolder = folder; let autoInstall = options?.install ?? false; // If no folder specified, ask user if (!targetFolder) { targetFolder = await askFolder(); // If we prompted for folder, also prompt for auto-install autoInstall = await askAutoInstall(); } // Normalize path targetFolder = targetFolder.trim(); // Check if folder is empty if (!isFolderEmpty(targetFolder)) { console.error(`Error: Folder "${targetFolder}" is not empty. Please specify an empty folder or a new folder path.`); process.exit(1); } // Generate project generateProject(targetFolder); // Auto-install if requested if (autoInstall) { try { await runPnpmInstall(targetFolder); } catch (error) { console.error('Failed to install dependencies:', error); console.log('\nYou can install dependencies manually:'); console.log(` cd ${targetFolder === './' ? '.' : targetFolder}`); console.log(' pnpm install'); process.exit(1); } } // Show next steps console.log(`\nNext steps:`); console.log(` cd ${targetFolder === './' ? '.' : targetFolder}`); if (!autoInstall) { console.log(` pnpm install`); } console.log(` pnpm hardhat test`); }); program.parse(); //# sourceMappingURL=cli.js.map