#!/usr/bin/env node 'use strict'; const path = require('path'); const fs = require('fs'); const minimist = require('minimist'); const { convert, buildIndex } = require('./lib/converter'); const { startServer } = require('./lib/server'); const { startWatcher } = require('./lib/watcher'); const argv = minimist(process.argv.slice(2), { alias: { o: 'output', p: 'port', w: 'watch', h: 'help' }, default: { port: 3000 }, boolean: ['watch', 'help'], }); if (argv.help) { console.log(` Usage: md-render [options] Options: -w, --watch Surveille les modifications et recharge automatiquement -p, --port Port du serveur en mode watch (défaut: 3000) -o, --output Dossier de sortie HTML (mode dossier, sans --watch) -h, --help Affiche cette aide Exemples: md-render README.md Convertit README.md → README.html md-render README.md --watch Prévisualisation live sur localhost:3000 md-render ../my-portal.wiki/ Convertit tous les .md du dossier md-render ../my-portal.wiki/ --watch Wiki live sur localhost:3000 `); process.exit(0); } const input = argv._[0]; if (!input) { console.error('\n✗ Erreur : veuillez fournir un fichier ou un dossier en argument.'); console.error(' Aide : md-render --help\n'); process.exit(1); } const absInput = path.resolve(input); if (!fs.existsSync(absInput)) { console.error(`\n✗ Erreur : introuvable : ${absInput}\n`); process.exit(1); } const stat = fs.statSync(absInput); const isDir = stat.isDirectory(); // ── WATCH MODE ────────────────────────────────────────────────────────────── if (argv.watch) { const port = parseInt(argv.port, 10); if (isNaN(port) || port < 1 || port > 65535) { console.error(`\n✗ Erreur : port invalide "${argv.port}".\n`); process.exit(1); } const server = startServer({ inputFile: isDir ? null : absInput, inputDir: isDir ? absInput : null, port, }); startWatcher({ inputFile: isDir ? null : absInput, inputDir: isDir ? absInput : null, server, }); return; } // ── ONE-SHOT MODE ──────────────────────────────────────────────────────────── if (isDir) { // Convert all .md files in the directory const outputDir = argv.output ? path.resolve(argv.output) : absInput; const files = findMarkdownFilesSync(absInput); if (files.length === 0) { console.error(`\n✗ Aucun fichier .md trouvé dans : ${absInput}\n`); process.exit(1); } let ok = 0; files.forEach((f) => { try { const rel = path.relative(absInput, f).replace(/\.md$/i, '.html'); const outFile = path.join(outputDir, rel); fs.mkdirSync(path.dirname(outFile), { recursive: true }); fs.writeFileSync(outFile, convert(f, false), 'utf-8'); console.log(` ✓ ${rel}`); ok++; } catch (err) { console.error(` ✗ ${path.relative(absInput, f)} — ${err.message}`); } }); console.log(`\n${ok}/${files.length} fichier(s) converti(s).`); } else { const outputFile = argv.output ? path.resolve(argv.output) : absInput.replace(/\.md$/i, '.html'); try { fs.writeFileSync(outputFile, convert(absInput, false), 'utf-8'); console.log(`✓ Converti : ${outputFile}`); } catch (err) { console.error(`\n✗ Erreur de conversion : ${err.message}\n`); process.exit(1); } } function findMarkdownFilesSync(dir) { const results = []; for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { if (entry.name.startsWith('.')) continue; const full = path.join(dir, entry.name); if (entry.isDirectory()) results.push(...findMarkdownFilesSync(full)); else if (/\.md$/i.test(entry.name)) results.push(full); } return results; }