Skip to content

Instantly share code, notes, and snippets.

@gaabora
Created June 25, 2024 12:33
Show Gist options
  • Save gaabora/86eb2c31976b155be812a7f8b87e022a to your computer and use it in GitHub Desktop.
Save gaabora/86eb2c31976b155be812a7f8b87e022a to your computer and use it in GitHub Desktop.
Sort ini file content for all .ini files in directory recursively
import { readdirSync, readFileSync, writeFileSync, copyFileSync, mkdirSync } from 'fs';
import { join, dirname, relative } from 'path';
function parseIni(content: string) {
const result: Record<string, { comments: string[], options: Record<string, { comments: string[], value: string }> }> = {};
let currentSection: string = '';
let currentComments: string[] = [];
content.split('\n').forEach(line => {
line = line.trim();
if (line.startsWith(';') || line.startsWith('#')) {
currentComments.push(line);
} else if (line.startsWith('[') && line.endsWith(']')) {
currentSection = line.slice(1, -1);
result[currentSection] = { comments: currentComments, options: {} };
currentComments = [];
} else if (line.includes('=') && currentSection) {
const [key, value] = line.split('=');
result[currentSection].options[key.trim()] = { comments: currentComments, value: value.trim() };
currentComments = [];
}
});
return result;
}
function formatIni(iniObject: Record<string, { comments: string[], options: Record<string, { comments: string[], value: string }> }>): string {
let result = '';
for (const section in iniObject) {
iniObject[section].comments.forEach(comment => {
result += `${comment}\n`;
});
result += `[${section}]\n`;
const options = iniObject[section].options;
for (const key in options) {
options[key].comments.forEach(comment => {
result += `${comment}\n`;
});
result += `${key}=${options[key].value}\n`;
}
}
return result;
}
function sortIniObject(iniObject: Record<string, { comments: string[], options: Record<string, { comments: string[], value: string }> }>) {
const sortedObject: Record<string, { comments: string[], options: Record<string, { comments: string[], value: string }> }> = {};
const sortedSections = Object.keys(iniObject).sort();
sortedSections.forEach(section => {
sortedObject[section] = {
comments: iniObject[section].comments,
options: {}
};
const sortedOptions = Object.keys(iniObject[section].options).sort();
sortedOptions.forEach(option => {
sortedObject[section].options[option] = iniObject[section].options[option];
});
});
return sortedObject;
}
function createBackup(srcPath: string, backupDir: string) {
const relativePath = relative(dirname(srcPath), srcPath);
const backupPath = join(backupDir, relativePath);
const backupDirPath = dirname(backupPath);
mkdirSync(backupDirPath, { recursive: true });
copyFileSync(srcPath, backupPath);
return backupPath;
}
async function sortIniFiles(dir: string) {
const date = new Date();
const dateString = date.toISOString().replace(/[-:.T]/g, '').slice(0, 14);
const backupDir = join(dir, `iniBak/${dateString}`);
const files = readdirSync(dir, { withFileTypes: true });
for (const file of files) {
const filePath = join(dir, file.name);
if (file.isDirectory()) {
await sortIniFiles(filePath);
} else if (file.name.endsWith('.ini')) {
const content = readFileSync(filePath, 'utf-8');
const iniObj = parseIni(content);
const sortedIniObj = sortIniObject(iniObj);
const sortedContent = formatIni(sortedIniObj);
const backupPath = createBackup(filePath, backupDir);
console.log(`Backup created at: ${backupPath}`);
if (readFileSync(backupPath, 'utf-8')) {
writeFileSync(filePath, sortedContent, 'utf-8');
} else {
console.error(`Failed to create backup for: ${filePath}`);
}
}
}
}
const [,, dir] = process.argv;
if (!dir) {
console.error("Usage: bun ./sortIniFilesContents.ts /path/to/dir");
process.exit(1);
}
sortIniFiles(dir).catch(err => {
console.error("An error occurred:", err);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment