UNPKG

@git.zone/cli

Version:

A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.

219 lines (186 loc) 7.08 kB
import * as plugins from './mod.plugins.js'; import * as paths from '../paths.js'; import { logger } from '../gitzone.logging.js'; import * as ui from './mod.ui.js'; export type ProjectType = 'npm' | 'deno' | 'both' | 'none'; export type VersionType = 'patch' | 'minor' | 'major'; /** * Detects the current git branch * @returns The current branch name, defaults to 'master' if detection fails */ export async function detectCurrentBranch(): Promise<string> { try { const smartshellInstance = new plugins.smartshell.Smartshell({ executor: 'bash', sourceFilePaths: [], }); const result = await smartshellInstance.exec('git branch --show-current'); const branchName = result.stdout.trim(); if (!branchName) { logger.log('warn', 'Could not detect current branch, falling back to "master"'); return 'master'; } logger.log('info', `Detected current branch: ${branchName}`); return branchName; } catch (error) { logger.log('warn', `Failed to detect branch: ${error.message}, falling back to "master"`); return 'master'; } } /** * Detects the project type based on presence of package.json and/or deno.json * @returns The project type */ export async function detectProjectType(): Promise<ProjectType> { const packageJsonPath = plugins.path.join(paths.cwd, 'package.json'); const denoJsonPath = plugins.path.join(paths.cwd, 'deno.json'); const hasPackageJson = await plugins.smartfs.file(packageJsonPath).exists(); const hasDenoJson = await plugins.smartfs.file(denoJsonPath).exists(); if (hasPackageJson && hasDenoJson) { logger.log('info', 'Detected dual project (npm + deno)'); return 'both'; } else if (hasPackageJson) { logger.log('info', 'Detected npm project'); return 'npm'; } else if (hasDenoJson) { logger.log('info', 'Detected deno project'); return 'deno'; } else { throw new Error('No package.json or deno.json found in current directory'); } } /** * Parses a semantic version string and bumps it according to the version type * @param currentVersion Current version string (e.g., "1.2.3") * @param versionType Type of version bump * @returns New version string */ function calculateNewVersion(currentVersion: string, versionType: VersionType): string { const versionMatch = currentVersion.match(/^(\d+)\.(\d+)\.(\d+)/); if (!versionMatch) { throw new Error(`Invalid version format: ${currentVersion}`); } let [, major, minor, patch] = versionMatch.map(Number); switch (versionType) { case 'major': major += 1; minor = 0; patch = 0; break; case 'minor': minor += 1; patch = 0; break; case 'patch': patch += 1; break; } return `${major}.${minor}.${patch}`; } /** * Reads the current version from package.json or deno.json * @param projectType The project type to determine which file to read * @returns The current version string */ async function readCurrentVersion(projectType: ProjectType): Promise<string> { if (projectType === 'npm' || projectType === 'both') { const packageJsonPath = plugins.path.join(paths.cwd, 'package.json'); const content = (await plugins.smartfs .file(packageJsonPath) .encoding('utf8') .read()) as string; const packageJson = JSON.parse(content) as { version?: string }; if (!packageJson.version) { throw new Error('package.json does not contain a version field'); } return packageJson.version; } else { const denoJsonPath = plugins.path.join(paths.cwd, 'deno.json'); const content = (await plugins.smartfs .file(denoJsonPath) .encoding('utf8') .read()) as string; const denoConfig = JSON.parse(content) as { version?: string }; if (!denoConfig.version) { throw new Error('deno.json does not contain a version field'); } return denoConfig.version; } } /** * Updates the version field in a JSON file (package.json or deno.json) * @param filePath Path to the JSON file * @param newVersion The new version to write */ async function updateVersionFile(filePath: string, newVersion: string): Promise<void> { const content = (await plugins.smartfs .file(filePath) .encoding('utf8') .read()) as string; const config = JSON.parse(content) as { version?: string }; config.version = newVersion; await plugins.smartfs .file(filePath) .encoding('utf8') .write(JSON.stringify(config, null, 2) + '\n'); } /** * Bumps the project version based on project type * Handles npm-only, deno-only, and dual projects with unified logic * @param projectType The detected project type * @param versionType The type of version bump * @param currentStep The current step number for progress display * @param totalSteps The total number of steps for progress display * @returns The new version string */ export async function bumpProjectVersion( projectType: ProjectType, versionType: VersionType, currentStep?: number, totalSteps?: number ): Promise<string> { if (projectType === 'none') { throw new Error('Cannot bump version: no package.json or deno.json found'); } const projectEmoji = projectType === 'npm' ? '📦' : projectType === 'deno' ? '🦕' : '🔀'; const description = `🏷️ Bumping version (${projectEmoji} ${projectType})`; if (currentStep && totalSteps) { ui.printStep(currentStep, totalSteps, description, 'in-progress'); } const smartshellInstance = new plugins.smartshell.Smartshell({ executor: 'bash', sourceFilePaths: [], }); try { // 1. Read current version const currentVersion = await readCurrentVersion(projectType); // 2. Calculate new version (reuse existing function!) const newVersion = calculateNewVersion(currentVersion, versionType); logger.log('info', `Bumping version: ${currentVersion}${newVersion}`); // 3. Determine which files to update const filesToUpdate: string[] = []; const packageJsonPath = plugins.path.join(paths.cwd, 'package.json'); const denoJsonPath = plugins.path.join(paths.cwd, 'deno.json'); if (projectType === 'npm' || projectType === 'both') { await updateVersionFile(packageJsonPath, newVersion); filesToUpdate.push('package.json'); } if (projectType === 'deno' || projectType === 'both') { await updateVersionFile(denoJsonPath, newVersion); filesToUpdate.push('deno.json'); } // 4. Stage all updated files await smartshellInstance.exec(`git add ${filesToUpdate.join(' ')}`); // 5. Create version commit await smartshellInstance.exec(`git commit -m "v${newVersion}"`); // 6. Create version tag await smartshellInstance.exec(`git tag v${newVersion} -m "v${newVersion}"`); logger.log('info', `Created commit and tag v${newVersion}`); if (currentStep && totalSteps) { ui.printStep(currentStep, totalSteps, description, 'done'); } return newVersion; } catch (error) { throw new Error(`Failed to bump project version: ${error.message}`); } }