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.

214 lines (185 loc) 6.4 kB
import { BaseFormatter } from '../classes.baseformatter.js'; import type { IPlannedChange } from '../interfaces.format.js'; import * as plugins from '../mod.plugins.js'; import * as paths from '../../paths.js'; import { logger, logVerbose } from '../../gitzone.logging.js'; /** * Ensures a certain dependency exists or is excluded */ const ensureDependency = async ( packageJsonObject: any, position: 'dep' | 'devDep' | 'everywhere', constraint: 'exclude' | 'include' | 'latest', dependencyArg: string, ): Promise<void> => { // Parse package name and version, handling scoped packages like @scope/name@version const isScoped = dependencyArg.startsWith('@'); const lastAtIndex = dependencyArg.lastIndexOf('@'); // For scoped packages, the version @ must come after the / // For unscoped packages, any @ indicates a version const hasVersion = isScoped ? lastAtIndex > dependencyArg.indexOf('/') : lastAtIndex >= 0; const packageName = hasVersion ? dependencyArg.slice(0, lastAtIndex) : dependencyArg; const version = hasVersion ? dependencyArg.slice(lastAtIndex + 1) : 'latest'; const targetSections: string[] = []; switch (position) { case 'dep': targetSections.push('dependencies'); break; case 'devDep': targetSections.push('devDependencies'); break; case 'everywhere': targetSections.push('dependencies', 'devDependencies'); break; } for (const section of targetSections) { if (!packageJsonObject[section]) { packageJsonObject[section] = {}; } switch (constraint) { case 'exclude': delete packageJsonObject[section][packageName]; break; case 'include': if (!packageJsonObject[section][packageName]) { packageJsonObject[section][packageName] = version === 'latest' ? '^1.0.0' : version; } break; case 'latest': try { const registry = new plugins.smartnpm.NpmRegistry(); const packageInfo = await registry.getPackageInfo(packageName); const latestVersion = packageInfo['dist-tags'].latest; packageJsonObject[section][packageName] = `^${latestVersion}`; } catch (error) { logVerbose( `Could not fetch latest version for ${packageName}, using existing or default`, ); if (!packageJsonObject[section][packageName]) { packageJsonObject[section][packageName] = version === 'latest' ? '^1.0.0' : version; } } break; } } }; export class PackageJsonFormatter extends BaseFormatter { get name(): string { return 'packagejson'; } async analyze(): Promise<IPlannedChange[]> { const changes: IPlannedChange[] = []; const packageJsonPath = 'package.json'; // Check if file exists const exists = await plugins.smartfs.file(packageJsonPath).exists(); if (!exists) { logVerbose('package.json does not exist, skipping'); return changes; } // Read current content const currentContent = (await plugins.smartfs .file(packageJsonPath) .encoding('utf8') .read()) as string; // Parse and compute new content const packageJson = JSON.parse(currentContent); // Get gitzone config from npmextra const npmextraConfig = new plugins.npmextra.Npmextra(paths.cwd); const gitzoneData: any = npmextraConfig.dataFor('@git.zone/cli', {}); // Set metadata from gitzone config if (gitzoneData.module) { packageJson.repository = { type: 'git', url: `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}.git`, }; packageJson.bugs = { url: `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}/issues`, }; packageJson.homepage = `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}#readme`; } // Ensure module type if (!packageJson.type) { packageJson.type = 'module'; } // Ensure private field exists if (packageJson.private === undefined) { packageJson.private = true; } // Ensure license field exists if (!packageJson.license) { packageJson.license = 'UNLICENSED'; } // Ensure scripts object exists if (!packageJson.scripts) { packageJson.scripts = {}; } // Ensure build script exists if (!packageJson.scripts.build) { packageJson.scripts.build = `echo "Not needed for now"`; } // Ensure buildDocs script exists if (!packageJson.scripts.buildDocs) { packageJson.scripts.buildDocs = `tsdoc`; } // Set files array packageJson.files = [ 'ts/**/*', 'ts_web/**/*', 'dist/**/*', 'dist_*/**/*', 'dist_ts/**/*', 'dist_ts_web/**/*', 'assets/**/*', 'cli.js', 'npmextra.json', 'readme.md', ]; // Handle dependencies await ensureDependency( packageJson, 'devDep', 'exclude', '@push.rocks/tapbundle', ); await ensureDependency(packageJson, 'devDep', 'latest', '@git.zone/tstest'); await ensureDependency( packageJson, 'devDep', 'latest', '@git.zone/tsbuild', ); // Set pnpm overrides from assets try { const overridesContent = (await plugins.smartfs .file(plugins.path.join(paths.assetsDir, 'overrides.json')) .encoding('utf8') .read()) as string; const overrides = JSON.parse(overridesContent); packageJson.pnpm = packageJson.pnpm || {}; packageJson.pnpm.overrides = overrides; } catch (error) { logVerbose(`Could not read overrides.json: ${error.message}`); } const newContent = JSON.stringify(packageJson, null, 2); // Only add change if content differs if (newContent !== currentContent) { changes.push({ type: 'modify', path: packageJsonPath, module: this.name, description: 'Format package.json', content: newContent, }); } return changes; } async applyChange(change: IPlannedChange): Promise<void> { if (change.type !== 'modify' || !change.content) return; await this.modifyFile(change.path, change.content); logger.log('info', 'Updated package.json'); } }