UNPKG

yamlresume

Version:

The CLI interface for YAMLResume's engine

22 lines 10.7 kB
import C from'node:fs';import {joinNonEmptyString,YAMLResumeError,DEFAULT_RESUME_LAYOUTS,LOCALE_LANGUAGE_OPTIONS,getLocaleLanguageDetail,toCodeBlock,LATEX_TEMPLATE_OPTIONS,getLatexTemplateDetail,HTML_TEMPLATE_OPTIONS,getHtmlTemplateDetail,ResumeSchema,getResumeRenderer}from'@yamlresume/core';import m from'chalk';import {Command}from'commander';import w,{consola}from'consola';import K,{LineCounter,parseDocument,isNode}from'yaml';import c from'node:path';import {execa}from'execa';import se from'which';import ge from'chokidar';import {coalesce}from'coalescifn';import xe from'envinfo';import {getFonts}from'font-list';import {markdownTable}from'markdown-table';import {fileURLToPath}from'node:url';function Z(e,t,n){let r=n.replace(/\r\n/g,` `).split(` `)[e.line-1]||"",s=`${" ".repeat(e.column-1)}^`,i=m.white.bold(`${t}:${e.line}:${e.column}`),a=m.red.bold("warning"),l=m.white(e.message),p=m.white(r),d=m.green.bold(s);return [`${i}: ${a}: ${l}`,`${p}`,`${d}`].join(` `)}function ee(e,t,n){let o=e.match(/at line (\d+), column (\d+)/);if(!o)return joinNonEmptyString([m.white.bold(t),m.red.bold("error"),`${m.white(e)}.`],": ");let r=Number.parseInt(o[1],10),s=Number.parseInt(o[2],10),a=n.replace(/\r\n/g,` `).split(` `)[r-1]||"",l=`${" ".repeat(s-1)}^`,p=m.white.bold(`${t}:${r}:${s}`),d=m.red.bold("error"),_=m.white(e.split(` `)[0].replace(/ at line \d+, column \d+:?/,".").trim()),y=m.white(a),P=m.green.bold(l);return [`${p}: ${d}: ${_}`,`${y}`,`${P}`].join(` `)}function te(e,t){let n=new LineCounter,o=parseDocument(e,{lineCounter:n,keepSourceTokens:true}),r=t.safeParse(o.toJS());if(r.success)return [];let{error:{issues:s}}=r;return s.map(i=>{let a=i.path,l=o.getIn(a,true),p=1,d=1;if(isNode(l)&&l.range){let _=l.range[0],y=n.linePos(_);p=y.line,d=y.col;}return {message:i.message,line:p,column:d,path:a}}).sort((i,a)=>i.line-a.line)}function x(e,t=true){let n;try{n=C.readFileSync(e,"utf8");}catch{throw new YAMLResumeError("FILE_READ_ERROR",{path:e})}let o;try{o=K.parse(n);}catch(r){let s=ee(r.message,e,n);throw console.log(s),new YAMLResumeError("INVALID_YAML",{error:`Failed to parse ${e}.`})}if(t){let r=te(n,ResumeSchema);if(r.length>0){for(let s of r)console.log(Z(s,e,n));return {resume:o,validated:"failed"}}return {resume:o,validated:"success"}}return {resume:o,validated:"unknown"}}function L(){return new Command().name("validate").description("validate a resume against the YAMLResume schema").argument("<resume-path>","the resume file path").action(async e=>{try{let{validated:t}=x(e,!0);t==="success"&&w.success("Resume validation passed."),t==="failed"&&w.fail("Resume validation failed.");}catch(t){w.error(t.message),process.exit(t.errno);}})}function ae(e,t){let n=c.extname(e);if(e.endsWith(".yaml")||e.endsWith(".yml")||e.endsWith(".json")){let o=c.basename(e.replace(/\.yaml|\.yml|\.json$/,".tex"));return t?c.join(t,o):e.replace(/\.yaml|\.yml|\.json$/,".tex")}throw new YAMLResumeError("INVALID_EXTNAME",{extname:n})}function me(e,t,n,o,r){let s=c.basename(e.replace(/\.yaml|\.yml|\.json$/,"")),i=o>1?`${s}.${n}${t}`:`${s}${t}`;return r?c.join(r,i):c.join(c.dirname(e),i)}function ce(e){return e.replace(/\.tex$/,".pdf")}function j(e){try{return !!se.sync(e)}catch{return false}}function le(){if(j("xelatex"))return "xelatex";if(j("tectonic"))return "tectonic";throw new YAMLResumeError("LATEX_NOT_FOUND",{})}function ue(e,t){let n=le(),o=e.endsWith(".tex")?e:ae(e,t),r="",s=[];switch(n){case "xelatex":r="xelatex",s=["-halt-on-error",c.basename(o)];break;case "tectonic":r="tectonic",s=[c.basename(o)];break}let i=t?c.resolve(t):c.dirname(c.resolve(o));return {command:r,args:s,cwd:i}}function de(e){switch(e){case ".tex":return "tex";case ".md":return "markdown";case ".html":return "html";default:return e.replace(".","")}}function v(e,t,n,o,r,s,i){let a=me(e,s,n,o,r),l=c.dirname(a);C.existsSync(l)||C.mkdirSync(l,{recursive:true});let d=getResumeRenderer(t,i).render();try{C.writeFileSync(a,d),consola.success(joinNonEmptyString([`Generated resume ${de(s)} file successfully:`,a]," "));}catch{throw new YAMLResumeError("FILE_WRITE_ERROR",{path:a})}return a}var h=3e4;async function pe(e,t,n=h){let{command:o,args:r,cwd:s}=ue(e,t);consola.start(`Generating resume pdf file with command: \`${o} ${r.join(" ")}\`...`);let i=n===0?void 0:n;try{let a=await execa(o,r,{cwd:s,encoding:"utf8",timeout:i});consola.success(`Generated resume pdf file successfully: ${ce(e)}`),consola.debug(joinNonEmptyString(["stdout: ",toCodeBlock(a.stdout)]));}catch(a){throw a.timedOut?(a.stdout&&(consola.info("LaTeX output before timeout:"),consola.log(a.stdout)),a.stderr&&(consola.info("LaTeX error output:"),consola.log(a.stderr)),new YAMLResumeError("LATEX_COMPILE_TIMEOUT",{timeout:String(n/1e3)})):(consola.debug(joinNonEmptyString(["stdout: ",toCodeBlock(a.stdout)])),consola.debug(joinNonEmptyString(["stderr: ",toCodeBlock(a.stderr)])),new YAMLResumeError("LATEX_COMPILE_ERROR",{error:a.message}))}}function fe(e){let t=Number(e);return Number.isNaN(t)||t<0?(consola.warn(joinNonEmptyString([`Invalid timeout value: "${e}".`,`Using default timeout: ${h/1e3}s.`,"Timeout must be a non-negative number in seconds (0 to disable)."]," ")),h):t*1e3}async function E(e,t={pdf:true,validate:true,timeout:h}){let{resume:n}=x(e,t.validate),o=n.layouts??DEFAULT_RESUME_LAYOUTS;n.layouts||(n.layouts=o);let r={latex:o.filter(i=>i.engine==="latex").length,markdown:o.filter(i=>i.engine==="markdown").length,html:o.filter(i=>i.engine==="html").length},s={latex:0,markdown:0,html:0};for(let i=0;i<o.length;i++)switch(o[i].engine){case "latex":{let l=v(e,n,s.latex++,r.latex,t.output,".tex",i);t.pdf===true&&await pe(l,t.output,t.timeout);break}case "markdown":{v(e,n,s.markdown++,r.markdown,t.output,".md",i);break}case "html":{v(e,n,s.html++,r.html,t.output,".html",i);break}}}function R(){return new Command().name("build").description("build a resume to LaTeX, PDF, Markdown, or HTML").argument("<resume-path>","the resume file path").option("--no-pdf","only generate TeX file without PDF (for LaTeX layouts)").option("--no-validate","skip resume schema validation").option("-o, --output <dir>","output directory for generated files").option("-t, --timeout <seconds>",joinNonEmptyString(["timeout for LaTeX compilation in seconds",`(default: ${h/1e3}, 0 to disable)`]," "),e=>fe(e)).action(async(e,t)=>{try{await E(e,t);}catch(n){consola.error(n.message),process.exit(n.errno);}})}function we(e,t={pdf:true,validate:true}){let{pdf:n,validate:o,output:r}=t,s=coalesce(()=>E(e,{pdf:n,validate:o,output:r}));s(),consola.start(`Watching file changes: ${e}...`);let i=ge.watch(e,{awaitWriteFinish:{stabilityThreshold:200,pollInterval:200},ignoreInitial:true});return i.on("change",()=>s()),i.on("add",()=>s()),i}function k(){return new Command().name("dev").description("build a resume on file changes (watch mode)").argument("<resume-path>","the resume file path").option("--no-pdf","only generate TeX file without PDF").option("--no-validate","skip resume schema validation").option("-o, --output <dir>","output directory for generated files").action((e,t)=>{we(e,t);})}async function Ce(){let e=await xe.run({System:["OS","CPU"],Binaries:["Node","Yarn","npm","pnpm","Bun"]},{json:false,showNotFound:true});console.log(e);}async function Te(){w.info("Checking XeTeX...");try{let{stdout:e}=await execa("xelatex",["--version"]);console.log(` XeTeX: ${e.split(` `)[0]} `);}catch{console.log(` XeTeX: Not Found `);}}async function ve(){w.info("Checking Tectonic...");try{let{stdout:e}=await execa("tectonic",["--version"]);console.log(` Tectonic: ${e.split(` `)[0]} `);}catch{console.log(` Tectonic: Not Found `);}}async function Ee(){w.info("Checking Fonts...");let e=await getFonts({disableQuoting:true}),t=["Linux Libertine O","Linux Libertine","Noto Serif CJK SC","Noto Sans CJK SC"];for(let n of t){let r=e.some(s=>s.toLowerCase().includes(n.toLowerCase()))?"Installed":"Not Installed";console.log(` ${n}: ${r}`);}}function N(){return new Command().name("doctor").description("check environment for YAMLResume dependencies").action(async()=>{await Ce(),await Te(),await ve(),await Ee();})}function Se(){return markdownTable([["locale.language","Language Name"],...LOCALE_LANGUAGE_OPTIONS.map(e=>[e,getLocaleLanguageDetail(e).name])])}function I(){let e=new Command().name("languages").description("i18n and l10n support");return e.command("list").description("list all supported languages").action(()=>{w.log(Se());}),e}function Xe(e){if(C.existsSync(e))throw new YAMLResumeError("FILE_CONFLICT",{path:e});let t=c.join(c.dirname(fileURLToPath(import.meta.url)),import.meta.url.includes("dist")?"../resources/resume.yml":"../../resources/resume.yml"),n;try{n=C.readFileSync(t,"utf8");}catch(o){throw w.debug(joinNonEmptyString(["Error reading template: ",toCodeBlock(o.stack)])),new YAMLResumeError("FILE_READ_ERROR",{path:t})}try{C.writeFileSync(e,n),w.success(`Created ${e} successfully.`);}catch(o){throw w.debug(joinNonEmptyString(["Error creating resume: ",toCodeBlock(o.stack)])),new YAMLResumeError("FILE_WRITE_ERROR",{path:e})}}function O(){return new Command().name("new").description("create a new resume").argument("[filename]","output filename","resume.yml").action(e=>{try{Xe(e);}catch(t){w.error(t.message),process.exit(t.errno);}})}function We(){let e=[...LATEX_TEMPLATE_OPTIONS.map(t=>{let n=getLatexTemplateDetail(t);return [t,n.engine,n.name,n.description]}),...HTML_TEMPLATE_OPTIONS.map(t=>{let n=getHtmlTemplateDetail(t);return [t,n.engine,n.name,n.description]})];return markdownTable([["layouts.[].template","Engine","Template Name","Description"],...e])}function X(){let e=new Command().name("templates").description("manage resume templates");return e.command("list").description("list all supported templates").action(()=>{w.log(We());}),e}var W={version:"0.12.3"};function M(e){w.level=e?4:3;}function tn(){let e=new Command;return e.name("yamlresume").description(["YAMLResume \u2014 Resume as Code in YAML",` __ __ _ __ __ _ ____ \\ \\ / // \\ | \\/ | | | _ \\ ___ ___ _ _ ___ ___ ___ \\ V // _ \\ | |\\/| | | | |_) / _ \\/ __| | | / _ \\/ _ \\ / _ \\ | |/ ___ \\| | | | |___| _ < __/\\__ \\ |_| | | | | | | __/ |_/_/ \\_\\_| |_|_____|_| \\_\\___||___/\\____|_| |_| |_|\\___| `].join(` `)).version(W.version).option("-v, --verbose","verbose output").hook("preAction",n=>{M(n.opts().verbose);}),e.addCommand(O()),e.addCommand(R()),e.addCommand(k()),e.addCommand(N()),e.addCommand(I()),e.addCommand(X()),e.addCommand(L()),e}export{L as a,R as b,k as c,N as d,I as e,O as f,X as g,tn as h};//# sourceMappingURL=chunk-FTS5HDX6.js.map //# sourceMappingURL=chunk-FTS5HDX6.js.map