#!/usr/bin/env node import { Command } from 'commander'; import { execSync } from 'child_process'; import degit from 'degit'; import path from 'path'; import fs from 'fs'; import fetch from 'node-fetch'; import inquirer from 'inquirer'; const program = new Command(); const GITHUB_USERNAME = "Qortal"; const REPO_NAME = "qapp-templates"; // Single repo storing all templates /** * Function to fetch available templates from GitHub repo */ const fetchTemplates = async () => { const url = `https://api.github.com/repos/${GITHUB_USERNAME}/${REPO_NAME}/contents`; try { const response = await fetch(url); const contents = await response.json(); if (!Array.isArray(contents)) { throw new Error("Unexpected response format from GitHub."); } // Filter out only directories (folders inside the repo) return contents .filter(item => item.type === "dir") .map(item => item.name); } catch (error) { console.error("āŒ Error fetching templates:", error.message); return []; } }; /** * Function to sanitize project name for npm */ const sanitizePackageName = (name) => { return name .toLowerCase() .replace(/\s+/g, '-') // Replace spaces with dashes .replace(/[^a-z0-9-]/g, '') // Remove invalid characters .replace(/^-+|-+$/g, ''); // Remove leading/trailing dashes }; program .name('create-qapp') .description('Create a new Qortal App') .version('1.0.0') .action(async () => { // Ask for project name const { projectName } = await inquirer.prompt([ { type: 'input', name: 'projectName', message: 'Enter the name of your app:', validate: input => input.trim() !== '' ? true : 'Project name cannot be empty.', } ]); const sanitizedProjectName = sanitizePackageName(projectName); if (!sanitizedProjectName) { console.error('āŒ Error: Invalid project name. Use letters, numbers, and dashes only.'); process.exit(1); } console.log("\nšŸ” Fetching available templates...\n"); const templates = await fetchTemplates(); if (templates.length === 0) { console.error("āŒ No templates found. Check your GitHub repository structure."); process.exit(1); } // Show template selection menu const { selectedTemplate } = await inquirer.prompt([ { type: 'list', name: 'selectedTemplate', message: 'Select a template:', choices: templates, } ]); console.log(`\nšŸš€ Creating project "${sanitizedProjectName}" using template "${selectedTemplate}"...\n`); const projectPath = path.resolve(process.cwd(), sanitizedProjectName); if (fs.existsSync(projectPath)) { console.error(`āŒ Error: Directory "${sanitizedProjectName}" already exists.`); process.exit(1); } // Clone only the selected template (subdirectory) const repoPath = `${GITHUB_USERNAME}/${REPO_NAME}/${selectedTemplate}`; const emitter = degit(repoPath, { cache: false, force: true }); try { await emitter.clone(projectPath); console.log("āœ… Template cloned successfully!"); // Modify package.json const packageJsonPath = path.join(projectPath, 'package.json'); if (fs.existsSync(packageJsonPath)) { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); packageJson.name = sanitizedProjectName; fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); console.log("šŸ“¦ Updated package.json with project name."); } // Change directory before running npm install process.chdir(sanitizedProjectName); console.log("\nšŸ“¦ Installing dependencies..."); execSync(`npm install`, { stdio: 'inherit' }); // šŸ”¹ Check if "initialize" script exists in package.json if (fs.existsSync(packageJsonPath)) { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); if (packageJson.scripts && packageJson.scripts.initialize) { console.log("\nāš™ļø Running npm run initialize..."); execSync(`npm run initialize`, { stdio: 'inherit' }); } } console.log(`\nšŸŽ‰ Project "${sanitizedProjectName}" is ready!`); console.log(`\nNext steps:\n cd ${sanitizedProjectName}\n npm run dev`); // Check if VS Code is installed and open the project try { execSync('code --version', { stdio: 'ignore' }); // Check if 'code' command exists console.log("\nšŸ’» Opening project in VS Code..."); execSync(`code ${projectPath}`, { stdio: 'inherit' }); } catch (error) { console.log("\nāš ļø VS Code not found or not installed. Open the project manually."); } } catch (error) { console.error("āŒ Error:", error.message); process.exit(1); } }); program.parse();