code icon Code

JSON to Markdown

Convert JSON array to markdown table or list

Source Code

import fs from "fs";
import path from "path";

const [inputPath, format = "table", fieldsArg, outputPath] = process.argv.slice(2);

if (!inputPath || !fieldsArg || !outputPath) {
  console.error("Usage: inputPath format fields outputPath");
  process.exit(1);
}

/**
 * Get nested field value using dot notation
 */
function getField(obj, fieldPath) {
  const parts = fieldPath.split(".");
  let value = obj;
  for (const part of parts) {
    if (value == null) return undefined;
    value = value[part];
  }
  return value;
}

/**
 * Escape markdown special characters in cell content
 */
function escapeMarkdown(str) {
  if (str == null) return "";
  return String(str)
    .replace(/\|/g, "\\|")
    .replace(/\n/g, " ")
    .replace(/\r/g, "");
}

/**
 * Format value for display
 */
function formatValue(value, maxLength = 100) {
  if (value == null) return "";
  if (typeof value === "object") {
    const str = JSON.stringify(value);
    return str.length > maxLength ? str.slice(0, maxLength) + "..." : str;
  }
  const str = String(value);
  return str.length > maxLength ? str.slice(0, maxLength) + "..." : str;
}

/**
 * Generate markdown table
 */
function toTable(items, fields) {
  const lines = [];

  // Header
  lines.push("| " + fields.join(" | ") + " |");
  lines.push("| " + fields.map(() => "---").join(" | ") + " |");

  // Rows
  for (const item of items) {
    const cells = fields.map((f) => escapeMarkdown(formatValue(getField(item, f))));
    lines.push("| " + cells.join(" | ") + " |");
  }

  return lines.join("\n");
}

/**
 * Generate markdown list (numbered)
 */
function toList(items, fields) {
  const lines = [];

  for (let i = 0; i < items.length; i++) {
    const item = items[i];
    const values = fields.map((f) => `**${f}:** ${formatValue(getField(item, f))}`);
    lines.push(`${i + 1}. ${values.join(", ")}`);
  }

  return lines.join("\n");
}

/**
 * Generate markdown bullets
 */
function toBullets(items, fields) {
  const lines = [];

  for (const item of items) {
    const values = fields.map((f) => `**${f}:** ${formatValue(getField(item, f))}`);
    lines.push(`- ${values.join(", ")}`);
  }

  return lines.join("\n");
}

try {
  console.log(`Reading ${inputPath}...`);
  const raw = fs.readFileSync(inputPath, "utf-8");
  const data = JSON.parse(raw);

  const items = Array.isArray(data)
    ? data
    : data.items || data.results || data.messages || [];

  if (!Array.isArray(items)) {
    console.error("Input must be a JSON array or object with array property");
    process.exit(1);
  }

  // Determine fields
  let fields;
  if (fieldsArg === "all" && items.length > 0) {
    fields = Object.keys(items[0]);
  } else {
    fields = fieldsArg.split(",").map((f) => f.trim());
  }

  console.log(`Converting ${items.length} items to ${format} with fields: ${fields.join(", ")}`);

  let markdown;
  switch (format) {
    case "table":
      markdown = toTable(items, fields);
      break;
    case "list":
      markdown = toList(items, fields);
      break;
    case "bullets":
      markdown = toBullets(items, fields);
      break;
    default:
      console.error(`Unknown format: ${format}. Use 'table', 'list', or 'bullets'`);
      process.exit(1);
  }

  // Ensure output directory exists
  const dir = path.dirname(outputPath);
  if (dir && dir !== ".") {
    fs.mkdirSync(dir, { recursive: true });
  }

  fs.writeFileSync(outputPath, markdown);

  console.log(`\nāœ“ Converted ${items.length} items to ${format}`);
  console.log(`  Fields: ${fields.join(", ")}`);
  console.log(`  Written to: ${outputPath}`);

  // Show preview
  const previewLines = markdown.split("\n").slice(0, 5);
  console.log(`\n  Preview:`);
  for (const line of previewLines) {
    console.log(`    ${line.slice(0, 80)}${line.length > 80 ? "..." : ""}`);
  }
  if (markdown.split("\n").length > 5) {
    console.log(`    ... (${markdown.split("\n").length - 5} more lines)`);
  }

  console.log(
    JSON.stringify({
      success: true,
      outputPath,
      count: items.length,
      format,
      fields,
    })
  );
} catch (error) {
  console.error("Error:", error.message);
  process.exit(1);
}