|
| 1 | +export function resumeToLatex(resume) { |
| 2 | + function escapes(source) { |
| 3 | + return source.replaceAll('&', '\\&'); |
| 4 | + } |
| 5 | + const { basics, education, skills, work: works } = resume, |
| 6 | + flattenSkills = skills |
| 7 | + .map(skill => [skill.name, skill.keywords]) |
| 8 | + .reduce((acc, item) => { |
| 9 | + if (acc[item[0]]) { |
| 10 | + acc[item[0]].push(...item[1]); |
| 11 | + } else { |
| 12 | + acc[item[0]] = item[1]; |
| 13 | + } |
| 14 | + return acc; |
| 15 | + }, {}), |
| 16 | + githubProfileUrl = basics.profiles.find( |
| 17 | + profile => profile.network === 'Github', |
| 18 | + ).url, |
| 19 | + linkedInProfileUrl = basics.profiles.find( |
| 20 | + profile => profile.network === 'Linkedin', |
| 21 | + ).url; |
| 22 | + return `\\documentclass{res} |
| 23 | +
|
| 24 | +\\name{${basics.name}} |
| 25 | +\\address{${basics.location.region}} |
| 26 | +\\address{${basics.email}} |
| 27 | +
|
| 28 | +\\begin{document} |
| 29 | +Name: ${basics.name} |
| 30 | +\\\\ |
| 31 | +Position Applied For: ${basics.label} |
| 32 | +\\\\ |
| 33 | +Email: ${basics.email} |
| 34 | +\\\\ |
| 35 | +Address: ${basics.location.region} |
| 36 | +\\\\ |
| 37 | +My website: https://neviaumi.github.io/portfolio/resume |
| 38 | +\\\\ |
| 39 | +Github: ${githubProfileUrl} |
| 40 | +\\\\ |
| 41 | +LinkedIn: ${linkedInProfileUrl} |
| 42 | +\\\\ |
| 43 | + \\section{ABOUT THIS DOCUMENT} |
| 44 | + This document has been optimized for Applicant Tracking Systems (ATS) to ensure accurate parsing and alignment with job-specific requirements. |
| 45 | + For a comprehensive view of my portfolio and additional details, please visit: https://neviaumi.github.io/portfolio/resume |
| 46 | +\\ |
| 47 | +\\ |
| 48 | +
|
| 49 | + \\hyphenpenalty=10000 |
| 50 | + \\exhyphenpenalty=10000 |
| 51 | + \\section{Summary} |
| 52 | + ${basics.summary} |
| 53 | +\\ |
| 54 | +\\ |
| 55 | + \\section{EXPERIENCE} |
| 56 | + \\begin{itemize} |
| 57 | + ${works |
| 58 | + .map(work => { |
| 59 | + return `\\item[-] ${work.position} | ${work.company}, ${work.location} | ${work.startDate} - ${work.endDate} |
| 60 | + ${work.summary ? `\\\\ \\textit{Role Summary:} ${work.summary.trim()}` : ''} |
| 61 | + ${ |
| 62 | + work.highlights |
| 63 | + ? `\\\\ \\textbf{Highlighted:} |
| 64 | + \\begin{itemize} |
| 65 | + ${work.highlights |
| 66 | + ?.map(highlight => { |
| 67 | + return `\\item[-] ${highlight}`; |
| 68 | + }) |
| 69 | + .join('\n')} |
| 70 | + \\end{itemize}` |
| 71 | + : '' |
| 72 | + } |
| 73 | + |
| 74 | +`; |
| 75 | + }) |
| 76 | + .join('\n')} |
| 77 | + \\end{itemize} |
| 78 | +\\ |
| 79 | +\\ |
| 80 | + |
| 81 | + \\section{TECHNICAL SKILLS} |
| 82 | + \\begin{itemize} |
| 83 | +${Object.entries(flattenSkills) |
| 84 | + .map( |
| 85 | + ([skill, keywords]) => |
| 86 | + ` \\raggedright |
| 87 | + \\item[-] ${skill}: ${escapes(keywords.join(', '))}`, |
| 88 | + ) |
| 89 | + .join('\n')} |
| 90 | + \\end{itemize} |
| 91 | +\\ |
| 92 | +\\ |
| 93 | + \\section{EDUCATION} |
| 94 | + \\begin{itemize} |
| 95 | +${education.map(edu => ` \\item[-] ${edu.studyType} in ${edu.area}, ${edu.institution} \\hfill ${edu.startDate} - ${edu.endDate}`).join('\n')} |
| 96 | + \\end{itemize} |
| 97 | +
|
| 98 | + |
| 99 | +
|
| 100 | +\\end{document}`; |
| 101 | +} |
| 102 | + |
| 103 | +(async () => { |
| 104 | + const os = await import('node:os'); |
| 105 | + const fs = await import('node:fs/promises'); |
| 106 | + const path = await import('node:path'); |
| 107 | + const useTailoredResume = process.env['VITE_IS_TAILORED_RESUME'] |
| 108 | + ? true |
| 109 | + : false; |
| 110 | + const resumeSource = |
| 111 | + process.env['VITE_RESUME_SOURCE'] ?? |
| 112 | + 'https://neviaumi.github.io/portfolio/resume.json'; |
| 113 | + if (useTailoredResume) { |
| 114 | + return fs |
| 115 | + .readFile(path.join(os.tmpdir(), resumeSource), { |
| 116 | + encoding: 'utf8', |
| 117 | + }) |
| 118 | + .then(JSON.parse); |
| 119 | + } |
| 120 | + return fetch(resumeSource).then(res => res.json()); |
| 121 | +})() |
| 122 | + .then(resumeToLatex) |
| 123 | + .then(async latexText => { |
| 124 | + const workspace = await import('./workspace.js'); |
| 125 | + const fs = await import('node:fs/promises'); |
| 126 | + await fs.writeFile(`${workspace.PUBLIC_FOLDER}/resume.tex`, latexText); |
| 127 | + return latexText; |
| 128 | + }); |
0 commit comments