fix(mod_format): move smartconfig file renaming into the formatter orchestrator

This commit is contained in:
2026-03-24 19:59:13 +00:00
parent b257c82bd6
commit bf858c8650
4 changed files with 48 additions and 60 deletions

View File

@@ -1,5 +1,12 @@
# Changelog # 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) ## 2026-03-24 - 2.13.13 - fix(vscode-template)
update VS Code schema matching to use .smartconfig.json update VS Code schema matching to use .smartconfig.json

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@git.zone/cli', 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.' 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.'
} }

View File

@@ -61,49 +61,33 @@ const migrateAccessLevel = (smartconfigJson: any): boolean => {
return true; return true;
}; };
// Config file names in priority order (newest → oldest) const CONFIG_FILE = '.smartconfig.json';
const CONFIG_FILE_NAMES = ['.smartconfig.json', 'smartconfig.json', 'npmextra.json'];
const TARGET_CONFIG_FILE = '.smartconfig.json';
export class SmartconfigFormatter extends BaseFormatter { export class SmartconfigFormatter extends BaseFormatter {
get name(): string { get name(): string {
return 'smartconfig'; 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<IPlannedChange[]> { async analyze(): Promise<IPlannedChange[]> {
const changes: IPlannedChange[] = []; const changes: IPlannedChange[] = [];
const configFile = await this.findConfigFile(); // File rename (npmextra.json/smartconfig.json → .smartconfig.json)
if (!configFile) { // is handled by the orchestrator before analysis.
logVerbose('No config file found (.smartconfig.json, smartconfig.json, or npmextra.json), skipping'); // 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; return changes;
} }
// Read current content
const currentContent = (await plugins.smartfs const currentContent = (await plugins.smartfs
.file(configFile.path) .file(CONFIG_FILE)
.encoding('utf8') .encoding('utf8')
.read()) as string; .read()) as string;
// Parse and apply migrations
const smartconfigJson = JSON.parse(currentContent); const smartconfigJson = JSON.parse(currentContent);
// Apply key migrations
migrateNamespaceKeys(smartconfigJson); migrateNamespaceKeys(smartconfigJson);
migrateAccessLevel(smartconfigJson); migrateAccessLevel(smartconfigJson);
@@ -117,26 +101,10 @@ export class SmartconfigFormatter extends BaseFormatter {
const newContent = JSON.stringify(smartconfigJson, null, 2); const newContent = JSON.stringify(smartconfigJson, null, 2);
// If file needs renaming, plan a create + delete if (newContent !== currentContent) {
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
changes.push({ changes.push({
type: 'modify', type: 'modify',
path: TARGET_CONFIG_FILE, path: CONFIG_FILE,
module: this.name, module: this.name,
description: 'Migrate and format .smartconfig.json', description: 'Migrate and format .smartconfig.json',
content: newContent, content: newContent,
@@ -147,17 +115,11 @@ export class SmartconfigFormatter extends BaseFormatter {
} }
async applyChange(change: IPlannedChange): Promise<void> { async applyChange(change: IPlannedChange): Promise<void> {
if (change.type === 'delete') { if (change.type !== 'modify' || !change.content) return;
await this.deleteFile(change.path);
logger.log('info', `Removed old config file ${change.path}`);
return;
}
if (!change.content) return;
// Parse the content to check for missing required fields
const smartconfigJson = JSON.parse(change.content); const smartconfigJson = JSON.parse(change.content);
// Check for missing required module information
const expectedRepoInformation: string[] = [ const expectedRepoInformation: string[] = [
'projectType', 'projectType',
'module.githost', 'module.githost',
@@ -202,12 +164,7 @@ export class SmartconfigFormatter extends BaseFormatter {
} }
const finalContent = JSON.stringify(smartconfigJson, null, 2); const finalContent = JSON.stringify(smartconfigJson, null, 2);
await this.modifyFile(change.path, finalContent);
if (change.type === 'create') { logger.log('info', 'Updated .smartconfig.json');
await this.createFile(change.path, finalContent);
} else {
await this.modifyFile(change.path, finalContent);
}
logger.log('info', `Updated ${change.path}`);
} }
} }

View File

@@ -16,6 +16,27 @@ import { PrettierFormatter } from './formatters/prettier.formatter.js';
import { ReadmeFormatter } from './formatters/readme.formatter.js'; import { ReadmeFormatter } from './formatters/readme.formatter.js';
import { CopyFormatter } from './formatters/copy.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<void> {
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() // Shared formatter class map used by both run() and runFormatter()
const formatterMap: Record<string, new (ctx: FormatContext, proj: Project) => BaseFormatter> = { const formatterMap: Record<string, new (ctx: FormatContext, proj: Project) => BaseFormatter> = {
cleanup: CleanupFormatter, cleanup: CleanupFormatter,
@@ -53,6 +74,9 @@ export let run = async (
const shouldWrite = options.write ?? (options.dryRun === false); const shouldWrite = options.write ?? (options.dryRun === false);
// Migrate config file before anything reads it
await migrateConfigFile();
const project = await Project.fromCwd({ requireProjectType: false }); const project = await Project.fromCwd({ requireProjectType: false });
const context = new FormatContext(); const context = new FormatContext();
const planner = new FormatPlanner(); const planner = new FormatPlanner();