diff --git a/changelog.md b/changelog.md index 01ab4cb..47f7b64 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2026-03-24 - 2.13.14 - fix(mod_format) +move smartconfig file renaming into the formatter orchestrator + +- Renames smartconfig.json or npmextra.json to .smartconfig.json before formatters run +- Simplifies the smartconfig formatter to only read and modify .smartconfig.json +- Removes create/delete change planning for config renames and applies only content updates within the formatter + ## 2026-03-24 - 2.13.13 - fix(vscode-template) update VS Code schema matching to use .smartconfig.json diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index f26cb09..644b003 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@git.zone/cli', - version: '2.13.13', + version: '2.13.14', description: '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.' } diff --git a/ts/mod_format/formatters/smartconfig.formatter.ts b/ts/mod_format/formatters/smartconfig.formatter.ts index 57842bb..97d1c10 100644 --- a/ts/mod_format/formatters/smartconfig.formatter.ts +++ b/ts/mod_format/formatters/smartconfig.formatter.ts @@ -61,49 +61,33 @@ const migrateAccessLevel = (smartconfigJson: any): boolean => { return true; }; -// Config file names in priority order (newest → oldest) -const CONFIG_FILE_NAMES = ['.smartconfig.json', 'smartconfig.json', 'npmextra.json']; -const TARGET_CONFIG_FILE = '.smartconfig.json'; +const CONFIG_FILE = '.smartconfig.json'; export class SmartconfigFormatter extends BaseFormatter { get name(): string { return 'smartconfig'; } - /** - * Find the config file, checking in priority order. - * Returns the path and whether it needs renaming. - */ - private async findConfigFile(): Promise<{ path: string; needsRename: boolean } | null> { - for (const filename of CONFIG_FILE_NAMES) { - const exists = await plugins.smartfs.file(filename).exists(); - if (exists) { - return { - path: filename, - needsRename: filename !== TARGET_CONFIG_FILE, - }; - } - } - return null; - } - async analyze(): Promise { const changes: IPlannedChange[] = []; - const configFile = await this.findConfigFile(); - if (!configFile) { - logVerbose('No config file found (.smartconfig.json, smartconfig.json, or npmextra.json), skipping'); + // File rename (npmextra.json/smartconfig.json → .smartconfig.json) + // is handled by the orchestrator before analysis. + // This formatter only operates on .smartconfig.json. + const exists = await plugins.smartfs.file(CONFIG_FILE).exists(); + if (!exists) { + logVerbose('.smartconfig.json does not exist, skipping'); return changes; } - // Read current content const currentContent = (await plugins.smartfs - .file(configFile.path) + .file(CONFIG_FILE) .encoding('utf8') .read()) as string; - // Parse and apply migrations const smartconfigJson = JSON.parse(currentContent); + + // Apply key migrations migrateNamespaceKeys(smartconfigJson); migrateAccessLevel(smartconfigJson); @@ -117,26 +101,10 @@ export class SmartconfigFormatter extends BaseFormatter { const newContent = JSON.stringify(smartconfigJson, null, 2); - // If file needs renaming, plan a create + delete - if (configFile.needsRename) { - changes.push({ - type: 'create', - path: TARGET_CONFIG_FILE, - module: this.name, - description: `Migrate ${configFile.path} to ${TARGET_CONFIG_FILE}`, - content: newContent, - }); - changes.push({ - type: 'delete', - path: configFile.path, - module: this.name, - description: `Remove old ${configFile.path}`, - }); - } else if (newContent !== currentContent) { - // File is already .smartconfig.json, just needs content update + if (newContent !== currentContent) { changes.push({ type: 'modify', - path: TARGET_CONFIG_FILE, + path: CONFIG_FILE, module: this.name, description: 'Migrate and format .smartconfig.json', content: newContent, @@ -147,17 +115,11 @@ export class SmartconfigFormatter extends BaseFormatter { } async applyChange(change: IPlannedChange): Promise { - if (change.type === 'delete') { - await this.deleteFile(change.path); - logger.log('info', `Removed old config file ${change.path}`); - return; - } + if (change.type !== 'modify' || !change.content) return; - if (!change.content) return; - - // Parse the content to check for missing required fields const smartconfigJson = JSON.parse(change.content); + // Check for missing required module information const expectedRepoInformation: string[] = [ 'projectType', 'module.githost', @@ -202,12 +164,7 @@ export class SmartconfigFormatter extends BaseFormatter { } const finalContent = JSON.stringify(smartconfigJson, null, 2); - - if (change.type === 'create') { - await this.createFile(change.path, finalContent); - } else { - await this.modifyFile(change.path, finalContent); - } - logger.log('info', `Updated ${change.path}`); + await this.modifyFile(change.path, finalContent); + logger.log('info', 'Updated .smartconfig.json'); } } diff --git a/ts/mod_format/index.ts b/ts/mod_format/index.ts index 98f187d..15696eb 100644 --- a/ts/mod_format/index.ts +++ b/ts/mod_format/index.ts @@ -16,6 +16,27 @@ import { PrettierFormatter } from './formatters/prettier.formatter.js'; import { ReadmeFormatter } from './formatters/readme.formatter.js'; import { CopyFormatter } from './formatters/copy.formatter.js'; +/** + * Rename npmextra.json or smartconfig.json to .smartconfig.json + * before any formatter tries to read config. + */ +async function migrateConfigFile(): Promise { + const target = '.smartconfig.json'; + const targetExists = await plugins.smartfs.file(target).exists(); + if (targetExists) return; + + for (const oldName of ['smartconfig.json', 'npmextra.json']) { + const exists = await plugins.smartfs.file(oldName).exists(); + if (exists) { + const content = await plugins.smartfs.file(oldName).encoding('utf8').read() as string; + await plugins.smartfs.file(`./${target}`).encoding('utf8').write(content); + await plugins.smartfs.file(oldName).delete(); + logger.log('info', `Migrated ${oldName} to ${target}`); + return; + } + } +} + // Shared formatter class map used by both run() and runFormatter() const formatterMap: Record BaseFormatter> = { cleanup: CleanupFormatter, @@ -53,6 +74,9 @@ export let run = async ( const shouldWrite = options.write ?? (options.dryRun === false); + // Migrate config file before anything reads it + await migrateConfigFile(); + const project = await Project.fromCwd({ requireProjectType: false }); const context = new FormatContext(); const planner = new FormatPlanner();