UNPKG

@sodacore/cli

Version:

Sodacore CLI is a plugin that offers CLI functionality within the framework.

239 lines (238 loc) 8.72 kB
import { cancel, confirm, group, intro, isCancel, log, multiselect, outro, select, tasks, text } from '@clack/prompts'; import { addConnection, getConfig, removeConnection } from './config'; import { chdir, cwd, exit } from 'node:process'; import { mkdir, rm } from 'node:fs/promises'; import { $, file, write } from 'bun'; import { resolve } from 'node:path'; import { getFiles } from './files'; export async function main() { intro('Hello, World!'); const meh = await text({ message: 'Meh' }); const results = await group({ name: () => text({ message: 'Field: A' }), age: () => text({ message: 'Field: B' }), colors: ({ results }) => multiselect({ message: `Field: C (${results.name} / ${results.age})`, options: [{ value: 'red', label: 'Red' }, { value: 'green', label: 'Green' }, { value: 'blue', label: 'Blue' }] }), }, { onCancel: () => { cancel('Operation cancelled'); exit(0); }, }); const foo = await text({ message: 'Foo', defaultValue: 'bar' }); outro('Done!'); console.log(meh, results, foo); exit(0); // Get the config. const config = await getConfig(); // What would you like to do? const intention = await select({ message: 'What would you like to do?', options: [ { value: 'create', label: 'Create a new project' }, { value: 'connection:add', label: 'Add a new connection' }, { value: 'connection:del', label: 'Remove a connection' }, ...config.connections.map(config => ({ value: `${config.host}:${config.port}:${config.pass}`, label: `Access: ${config.host}:${config.port}`, })), ], }); if (isCancel(intention)) { cancel('Operation cancelled'); exit(0); } // Validate the intention. if (intention === 'create') { await createProjectMenu(); } else if (intention === 'connection:add') { await addConnectionMenu(); } else if (intention === 'connection:del') { await removeConnectionMenu(); } else { const [host, port, pass] = intention.split(':'); const connection = config.connections.find(c => c.host === host && c.port === Number.parseInt(port) && c.pass === pass); if (!connection) { cancel('Invalid connection'); exit(1); } await accessConnectionMenu(connection); } } export async function createProjectMenu() { // Ask for the project name. const projectName = await text({ message: 'What is the project name?', defaultValue: 'my-sodacore-project', placeholder: 'my-sodacore-project', }); if (isCancel(projectName)) { cancel('Operation cancelled'); exit(0); } // Ask for the project path. const projectBasePath = await text({ message: 'What is the project path?', defaultValue: cwd(), placeholder: cwd(), }); if (isCancel(projectBasePath)) { cancel('Operation cancelled'); exit(0); } // Confirm the path. const isPathConfirmed = await confirm({ message: `Is the project path ${projectBasePath}/${projectName} correct?`, }); if (isCancel(isPathConfirmed)) { cancel('Operation cancelled'); exit(0); } // Define the project. const projectPath = `${projectBasePath}/${projectName}`; // What plugins should be installed? const plugins = await multiselect({ message: 'What plugins would you like to install? (Use space to select, enter to confirm)', options: [ { value: '@sodacore/http@alpha', label: 'HTTP' }, { value: '@sodacore/ws@alpha', label: 'WebSockets' }, { value: '@sodacore/prisma@alpha', label: 'Prisma' }, { value: '@sodacore/discord@alpha', label: 'Discord' }, { value: '@sodacore/cli@alpha', label: 'CLI' }, ], }); if (isCancel(plugins)) { cancel('Operation cancelled'); exit(0); } // Add the base packages. const packages = ['@sodacore/di@alpha', '@sodacore/core@alpha']; packages.push(...plugins); // Run the tasks. await tasks([ { title: 'Initialising project folder', task: async () => { await mkdir(projectPath, { recursive: true }); chdir(projectPath); $.cwd(projectPath); await $ `bun init -y`.quiet(); await rm(resolve(projectPath, './index.ts')); await mkdir(resolve(projectPath, './src'), { recursive: true }); return 'Initialised project folder'; }, }, { title: 'Installing dependencies', task: async () => { chdir(projectPath); await $ `bun install ${{ raw: packages.join(' ') }}`.quiet(); return 'Dependencies installed'; }, }, { title: 'Creating template files', task: async () => { const files = getFiles(packages); const createdFiles = []; for (const file of files) { if (!packages.includes(file.package)) continue; const filePath = resolve(projectPath, file.path); await mkdir(resolve(projectPath, file.path.split('/').slice(0, -1).join('/')), { recursive: true }); await write(filePath, file.content); createdFiles.push(filePath); } return `Created ${createdFiles.length} files:\n${createdFiles.map(file => `- ${file}`).join('\n')}`; }, }, { title: 'Modifying files with context information.', task: async () => { // Load the JSON file. const packageJson = file(resolve(projectPath, './package.json')); if (!await packageJson.exists()) { cancel('Package.json not found.'); exit(1); } // Write the version and script. const packageMeta = await packageJson.json(); packageMeta.version = '0.0.0'; packageMeta.scripts = {}; packageMeta.scripts.dev = 'bun run ./src/main.ts'; await packageJson.write(JSON.stringify(packageMeta, null, '\t')); }, }, ]); // Log the outcome. log.success(`Created project at ${projectPath}\n\nRun \`cd ${projectPath}\` and \`bun run dev\` to get started the project.`); } export async function addConnectionMenu() { // Ask for available hostname. const hostName = await text({ message: 'What hostname would you like to access?', defaultValue: 'localhost', placeholder: 'localhost', }); if (isCancel(hostName)) { cancel('Operation cancelled'); exit(0); } // Ask for available port. const port = await text({ message: 'What port would you like to access?', defaultValue: '36445', placeholder: '36445', }); if (isCancel(port)) { cancel('Operation cancelled'); exit(0); } // Ask for CLI password. const password = await text({ message: 'What is the CLI password?', defaultValue: '', placeholder: 'This is set as a config option in the CLI project.', }); if (isCancel(password)) { cancel('Operation cancelled'); exit(0); } // Get the config. const status = await addConnection(hostName, Number.parseInt(port), password); if (!status) { cancel('Failed to add connection'); exit(1); } // Return to main menu. await main(); } export async function removeConnectionMenu() { // Get the config. const config = await getConfig(); // Show the available connections. const connectionId = await select({ message: 'Select a connection to remove', options: config.connections.map((connection, index) => ({ value: index, label: `${connection.host}:${connection.port}`, })), }); if (isCancel(connectionId)) { cancel('Operation cancelled'); exit(0); } // Remove the connection. const status = await removeConnection(connectionId); if (!status) { cancel('Failed to remove connection'); exit(1); } // Return to main menu. await main(); } export async function accessConnectionMenu(connection) { console.log('accessProject', connection); }