file-swap
Version:
A versatile file conversion tool
142 lines (122 loc) • 4.16 kB
JavaScript
import path from 'path';
import { promises as fs } from 'fs';
import yaml from 'yaml';
export async function convertText(inputPath, targetFormat) {
const parsedPath = path.parse(inputPath);
const outputPath = path.join(parsedPath.dir, `${parsedPath.name}.${targetFormat}`);
const sourceFormat = parsedPath.ext.toLowerCase().slice(1);
const content = await fs.readFile(inputPath, 'utf-8');
let data;
switch (sourceFormat) {
case 'json':
data = JSON.parse(content);
break;
case 'yaml':
data = yaml.parse(content);
break;
case 'csv':
data = parseCSV(content);
break;
case 'md':
case 'txt':
data = content;
break;
default:
throw new Error(`Unsupported source format: ${sourceFormat}`);
}
let output;
switch (targetFormat) {
case 'json':
output = JSON.stringify(data, null, 2);
break;
case 'yaml':
output = yaml.stringify(data);
break;
case 'csv':
output = stringifyCSV(convertToCSVFormat(data));
break;
case 'md':
output = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
break;
case 'txt':
output = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
break;
default:
throw new Error(`Unsupported target format: ${targetFormat}`);
}
await fs.writeFile(outputPath, output);
return outputPath;
}
function parseCSV(content) {
const lines = content.split('\n').map(line => line.trim()).filter(line => line);
if (lines.length === 0) return [];
const headers = lines[0].split(',').map(h => h.trim());
return lines.slice(1).map(line => {
const values = line.split(',').map(v => v.trim());
return headers.reduce((obj, header, index) => {
obj[header] = values[index] || '';
return obj;
}, {});
});
}
function stringifyCSV(data) {
if (!Array.isArray(data) || data.length === 0) {
throw new Error('CSV output requires an array of objects');
}
const headers = Object.keys(data[0]);
const rows = [headers.join(',')];
for (const row of data) {
const values = headers.map(header => {
const value = row[header];
return typeof value === 'string' && (value.includes(',') || value.includes('"'))
? `"${value.replace(/"/g, '""')}"`
: value;
});
rows.push(values.join(','));
}
return rows.join('\n');
}
function convertToCSVFormat(data) {
if (Array.isArray(data) && data.length > 0 && typeof data[0] === 'object') {
return data;
}
if (typeof data === 'string') {
return data.split('\n')
.map(line => line.trim())
.filter(line => line)
.map(line => ({ content: line }));
}
if (typeof data === 'object' && data !== null) {
const rows = [];
flattenObject(data, rows);
return rows;
}
if (Array.isArray(data)) {
return data.map(item => ({ value: item }));
}
return [{ value: data }];
}
function flattenObject(obj, rows, prefix = '') {
const flatRow = {};
for (const [key, value] of Object.entries(obj)) {
const newKey = prefix ? `${prefix}_${key}` : key;
if (value === null) {
flatRow[newKey] = '';
} else if (typeof value === 'object' && !Array.isArray(value)) {
flattenObject(value, rows, newKey);
} else if (Array.isArray(value)) {
value.forEach((item, index) => {
if (typeof item === 'object' && item !== null) {
flattenObject(item, rows, `${newKey}_${index}`);
} else {
flatRow[`${newKey}_${index}`] = item;
}
});
} else {
flatRow[newKey] = value;
}
}
if (Object.keys(flatRow).length > 0) {
rows.push(flatRow);
}
}