Files
valere.dev/scripts/convert.mjs
valere 7355b1a9b4
All checks were successful
Deploy valere.dev / deploy (push) Successful in 10s
add cvs
2025-10-29 20:56:34 +01:00

257 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
import { promises as fs } from "fs";
import path from "path";
import { glob } from "glob";
import MarkdownIt from "markdown-it";
import { mdToPdf } from "md-to-pdf";
const PUBLIC_DIR = path.resolve(process.cwd(), "public/cv");
async function ensureDir(dir) {
await fs.mkdir(dir, { recursive: true }).catch(() => {});
}
const STYLE_CSS = `
/* Default styles */
pre {
background: #2d2d2d;
border-radius: 4px;
margin: 0.5em 0;
}
code {
font-family: 'Fira Code', Consolas, Monaco, monospace;
}
/* Custom CSS */
/* ====== Markdown PDF Pro Theme ====== */
@page {
margin: 25mm 20mm;
margin-top: 0mm;
}
body {
font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
font-size: 11pt;
line-height: 1.6;
color: #333;
background: white;
max-width: 800px;
margin: auto;
padding: 60px;
}
/* Headings */
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: "Segoe UI Semibold", "Helvetica Neue", Arial, sans-serif;
font-weight: 600;
margin-top: 2em;
margin-bottom: 0.6em;
line-height: 1.3;
color: #222;
}
h1 {
font-size: 24pt;
border-bottom: 2px solid #000;
/* accent color */
padding-bottom: 0.3em;
margin-top: 0;
}
h2 {
font-size: 18pt;
border-left: 4px solid #000;
padding-left: 0.5em;
}
h3 {
font-size: 14pt;
color: #444;
}
h4,
h5,
h6 {
font-size: 12pt;
color: #555;
}
/* Paragraphs */
p {
margin: 0.5em 0;
text-align: justify;
}
/* Links */
a {
color: #1e7fce;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* Lists */
ul,
ol {
margin: 0.5em 0 0.5em 2em;
}
li {
margin-bottom: 0.3em;
}
/* Blockquote */
blockquote {
border-left: 4px solid #0078D7;
margin: 1em 0;
padding: 0.5em 1em;
color: #555;
background: #f9f9f9;
font-style: italic;
}
/* Code */
code {
font-family: "Fira Code", "Consolas", monospace;
background: #f4f4f4;
padding: 0.2em 0.4em;
border-radius: 4px;
font-size: 0.95em;
}
pre {
background: #1e1e1e;
color: #dcdcdc;
padding: 1em;
border-radius: 6px;
overflow-x: auto;
font-size: 0.9em;
line-height: 1.4;
}
/* Tables */
table {
border-collapse: collapse;
margin: 1em 0;
width: 100%;
font-size: 0.95em;
}
th,
td {
border: 1px solid #ccc;
padding: 0.6em 0.8em;
text-align: left;
}
th {
background: #f0f0f0;
font-weight: 600;
}
tr:nth-child(even) td {
background: #fafafa;
}
/* Horizontal rule */
hr {
border: none;
border-top: 2px solid #eee;
margin: 2em 0;
}`;
function wrapHtml(title, body) {
return `<html><head>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
<link href="https://fonts.googleapis.com/css2?family=Barlow:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&amp;display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&amp;display=swap" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet">
<style>${STYLE_CSS}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-bash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-sql.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-go.min.js"></script>
</head>
<body>
${body}
</body>
</html>`;
}
async function convertFile(mdPath) {
const base = path.basename(mdPath, ".md");
const dir = path.dirname(mdPath);
const rel = path.relative(PUBLIC_DIR, mdPath);
// Créer les dossiers de destination s'ils n'existent pas
const htmlDir = path.join(PUBLIC_DIR, 'html');
const pdfDir = path.join(PUBLIC_DIR, 'pdf');
await ensureDir(htmlDir);
await ensureDir(pdfDir);
let mdContent = await fs.readFile(mdPath, "utf8");
// Remplacer les guillemets courbes par des guillemets droits
mdContent = mdContent
.replace(//g, "'"); // Remplace les apostrophes courbes par des droites
// HTML
const md = new MarkdownIt({ html: true, linkify: true, typographer: true });
const htmlBody = md.render(mdContent);
const html = wrapHtml(base, htmlBody);
const htmlPath = path.join(htmlDir, `${base}.html`);
await fs.writeFile(htmlPath, html, "utf8");
// PDF
const pdfPath = path.join(pdfDir, `${base}.pdf`);
const pdf = await mdToPdf(
{ content: mdContent, path: mdPath },
{
basedir: dir,
launch_options: { args: ["--no-sandbox", "--disable-setuid-sandbox"] },
pdf_options: { format: "A4", printBackground: true },
css: STYLE_CSS,
}
);
if (pdf && pdf.content) {
await fs.writeFile(pdfPath, pdf.content);
}
console.log(
`Converted ${rel} -> ${path.relative(
PUBLIC_DIR,
htmlPath
)}, ${path.relative(PUBLIC_DIR, pdfPath)}`
);
}
async function main() {
await ensureDir(PUBLIC_DIR);
const mdFiles = await glob("public/**/*.md", { nodir: true });
if (!mdFiles.length) {
console.log("No Markdown files found in public/.");
return;
}
for (const file of mdFiles) {
try {
await convertFile(path.resolve(process.cwd(), file));
} catch (err) {
console.error(`Failed to convert ${file}:`, err.message || err);
process.exitCode = 1;
}
}
}
main();