yamlresume 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/cli.js +6 -6
- package/dist/cli.js.map +1 -1
- package/package.json +5 -5
- package/resources/resume.yml +6 -1
package/README.md
CHANGED
|
@@ -83,9 +83,9 @@ For more detailed information, please visit our official documentation:
|
|
|
83
83
|
reference for all commands.
|
|
84
84
|
- **[Schema Validation](https://yamlresume.dev/docs/compiler/schema)**: The complete
|
|
85
85
|
resume schema reference.
|
|
86
|
-
- **[Multi Language Support](https://yamlresume.dev/docs/
|
|
86
|
+
- **[Multi Language Support](https://yamlresume.dev/docs/locale)**:
|
|
87
87
|
Learn how to write resumes in multiple languages.
|
|
88
|
-
- **[Templates](https://yamlresume.dev/docs/
|
|
88
|
+
- **[Templates](https://yamlresume.dev/docs/layouts/latex/templates)**: Learn how to
|
|
89
89
|
use and create templates.
|
|
90
90
|
|
|
91
91
|
## Support the Project
|
package/dist/cli.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {Command}from'commander';import L from'fs';import c from'path';import {YAMLResumeError,joinNonEmptyString,toCodeBlock,DEFAULT_RESUME_LAYOUTS,LOCALE_LANGUAGE_OPTIONS,getLocaleLanguageDetail,LATEX_TEMPLATE_OPTIONS,getLatexTemplateDetail,getResumeRenderer,ResumeSchema}from'@yamlresume/core';import y,{consola}from'consola';import {execa}from'execa';import oe from'which';import m from'chalk';import
|
|
3
|
-
`)[e.line-1]||"",o=`${" ".repeat(e.column-1)}^`,
|
|
4
|
-
`)}function Q(e,t,r){let n=e.match(/at line (\d+), column (\d+)/);if(!n)return joinNonEmptyString([m.white.bold(t),m.red.bold("error"),`${m.white(e)}.`],": ");let
|
|
5
|
-
`)[
|
|
2
|
+
import {Command}from'commander';import L from'fs';import c from'path';import {YAMLResumeError,joinNonEmptyString,toCodeBlock,DEFAULT_RESUME_LAYOUTS,LOCALE_LANGUAGE_OPTIONS,getLocaleLanguageDetail,LATEX_TEMPLATE_OPTIONS,HTML_TEMPLATE_OPTIONS,getLatexTemplateDetail,getHtmlTemplateDetail,getResumeRenderer,ResumeSchema}from'@yamlresume/core';import y,{consola}from'consola';import {execa}from'execa';import oe from'which';import m from'chalk';import H,{LineCounter,parseDocument,isNode}from'yaml';import de from'chokidar';import {coalesce}from'coalescifn';import {markdownTable}from'markdown-table';import {fileURLToPath}from'url';var O={version:"0.9.0"};function K(e,t,r){let s=r.split(`
|
|
3
|
+
`)[e.line-1]||"",o=`${" ".repeat(e.column-1)}^`,a=m.white.bold(`${t}:${e.line}:${e.column}`),i=m.red.bold("warning"),l=m.white(e.message),d=m.white(s),u=m.green.bold(o);return [`${a}: ${i}: ${l}`,`${d}`,`${u}`].join(`
|
|
4
|
+
`)}function Q(e,t,r){let n=e.match(/at line (\d+), column (\d+)/);if(!n)return joinNonEmptyString([m.white.bold(t),m.red.bold("error"),`${m.white(e)}.`],": ");let s=Number.parseInt(n[1],10),o=Number.parseInt(n[2],10),i=r.split(`
|
|
5
|
+
`)[s-1]||"",l=`${" ".repeat(o-1)}^`,d=m.white.bold(`${t}:${s}:${o}`),u=m.red.bold("error"),f=m.white(e.split(`
|
|
6
6
|
`)[0].replace(/ at line \d+, column \d+:?/,".").trim()),g=m.white(i),V=m.green.bold(l);return [`${d}: ${u}: ${f}`,`${g}`,`${V}`].join(`
|
|
7
|
-
`)}function Z(e,t){let r=new LineCounter,n=parseDocument(e,{lineCounter:r,keepSourceTokens:true}),
|
|
7
|
+
`)}function Z(e,t){let r=new LineCounter,n=parseDocument(e,{lineCounter:r,keepSourceTokens:true}),s=t.safeParse(n.toJS());if(s.success)return [];let{error:{issues:o}}=s;return o.map(a=>{let i=a.path,l=n.getIn(i,true),d=1,u=1;if(isNode(l)&&l.range){let f=l.range[0],g=r.linePos(f);d=g.line,u=g.col;}return {message:a.message,line:d,column:u,path:i}}).sort((a,i)=>a.line-i.line)}function b(e,t=true){let r;try{r=L.readFileSync(e,"utf8");}catch{throw new YAMLResumeError("FILE_READ_ERROR",{path:e})}let n;try{n=H.parse(r);}catch(s){let o=Q(s.message,e,r);throw console.log(o),new YAMLResumeError("INVALID_YAML",{error:`Failed to parse ${e}.`})}if(t){let s=Z(r,ResumeSchema);if(s.length>0){for(let o of s)console.log(K(o,e,r));return {resume:n,validated:"failed"}}return {resume:n,validated:"success"}}return {resume:n,validated:"unknown"}}function x(){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}=b(e,!0);t==="success"&&y.success("Resume validation passed."),t==="failed"&&y.fail("Resume validation failed.");}catch(t){y.error(t.message),process.exit(t.errno);}})}function ae(e,t){let r=c.extname(e);if(e.endsWith(".yaml")||e.endsWith(".yml")||e.endsWith(".json")){let n=c.basename(e.replace(/\.yaml|\.yml|\.json$/,".tex"));return t?c.join(t,n):e.replace(/\.yaml|\.yml|\.json$/,".tex")}throw new YAMLResumeError("INVALID_EXTNAME",{extname:r})}function se(e,t,r,n,s){let o=c.basename(e.replace(/\.yaml|\.yml|\.json$/,"")),a=n>1?`${o}.${r}${t}`:`${o}${t}`;return s?c.join(s,a):c.join(c.dirname(e),a)}function ie(e){return e.replace(/\.tex$/,".pdf")}function F(e){try{return !!oe.sync(e)}catch{return false}}function me(){if(F("xelatex"))return "xelatex";if(F("tectonic"))return "tectonic";throw new YAMLResumeError("LATEX_NOT_FOUND",{})}function ce(e,t){let r=me(),n=e.endsWith(".tex")?e:ae(e,t),s="",o=[];switch(r){case "xelatex":s="xelatex",o=["-halt-on-error",c.basename(n)];break;case "tectonic":s="tectonic",o=[c.basename(n)];break}let a=t?c.resolve(t):c.dirname(c.resolve(n));return {command:s,args:o,cwd:a}}function le(e){switch(e){case ".tex":return "tex";case ".md":return "markdown";case ".html":return "html";default:return e.replace(".","")}}function C(e,t,r,n,s,o,a){let i=se(e,o,r,n,s),l=c.dirname(i);L.existsSync(l)||L.mkdirSync(l,{recursive:true});let u=getResumeRenderer(t,a).render();try{L.writeFileSync(i,u),consola.success(joinNonEmptyString([`Generated resume ${le(o)} file successfully:`,i]," "));}catch{throw new YAMLResumeError("FILE_WRITE_ERROR",{path:i})}return i}async function ue(e,t){let{command:r,args:n,cwd:s}=ce(e,t);consola.start(`Generating resume pdf file with command: \`${r} ${n.join(" ")}\`...`);try{let o=await execa(r,n,{cwd:s,encoding:"utf8"});consola.success(`Generated resume pdf file successfully: ${ie(e)}`),consola.debug(joinNonEmptyString(["stdout: ",toCodeBlock(o.stdout)]));}catch(o){throw consola.debug(joinNonEmptyString(["stdout: ",toCodeBlock(o.stdout)])),consola.debug(joinNonEmptyString(["stderr: ",toCodeBlock(o.stderr)])),new YAMLResumeError("LATEX_COMPILE_ERROR",{error:o.message})}}async function E(e,t={pdf:true,validate:true}){let{resume:r}=b(e,t.validate),n=r.layouts??DEFAULT_RESUME_LAYOUTS;r.layouts||(r.layouts=n);let s={latex:n.filter(a=>a.engine==="latex").length,markdown:n.filter(a=>a.engine==="markdown").length,html:n.filter(a=>a.engine==="html").length},o={latex:0,markdown:0,html:0};for(let a=0;a<n.length;a++)switch(n[a].engine){case "latex":{let l=C(e,r,o.latex++,s.latex,t.output,".tex",a);t.pdf===true&&await ue(l,t.output);break}case "markdown":{C(e,r,o.markdown++,s.markdown,t.output,".md",a);break}case "html":{C(e,r,o.html++,s.html,t.output,".html",a);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").action(async(e,t)=>{try{await E(e,t);}catch(r){consola.error(r.message),process.exit(r.errno);}})}function he(e,t={pdf:true,validate:true}){let{pdf:r,validate:n,output:s}=t,o=coalesce(()=>E(e,{pdf:r,validate:n,output:s}));o(),consola.start(`Watching file changes: ${e}...`);let a=de.watch(e,{awaitWriteFinish:{stabilityThreshold:200,pollInterval:200},ignoreInitial:true});return a.on("change",()=>o()),a.on("add",()=>o()),a}function T(){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)=>{he(e,t);})}function Le(){return markdownTable([["locale.language","Language Name"],...LOCALE_LANGUAGE_OPTIONS.map(e=>[e,getLocaleLanguageDetail(e).name])])}function k(){let e=new Command().name("languages").description("i18n and l10n support");return e.command("list").description("list all supported languages").action(()=>{y.log(Le());}),e}function Ee(e){if(L.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"),r;try{r=L.readFileSync(t,"utf8");}catch(n){throw y.debug(joinNonEmptyString(["Error reading template: ",toCodeBlock(n.stack)])),new YAMLResumeError("FILE_READ_ERROR",{path:t})}try{L.writeFileSync(e,r),y.success(`Created ${e} successfully.`);}catch(n){throw y.debug(joinNonEmptyString(["Error creating resume: ",toCodeBlock(n.stack)])),new YAMLResumeError("FILE_WRITE_ERROR",{path:e})}}function N(){return new Command().name("new").description("create a new resume").argument("[filename]","output filename","resume.yml").action(e=>{try{Ee(e);}catch(t){y.error(t.message),process.exit(t.errno);}})}function Se(){let e=[...LATEX_TEMPLATE_OPTIONS.map(t=>{let r=getLatexTemplateDetail(t);return [t,r.engine,r.name,r.description]}),...HTML_TEMPLATE_OPTIONS.map(t=>{let r=getHtmlTemplateDetail(t);return [t,r.engine,r.name,r.description]})];return markdownTable([["layouts.[].template","Engine","Template Name","Description"],...e])}function I(){let e=new Command().name("templates").description("manage resume templates");return e.command("list").description("list all supported templates").action(()=>{y.log(Se());}),e}function S(e){y.level=e?4:3;}function Y(){let e=new Command;return e.name("yamlresume").description(["YAMLResume \u2014 Resume as Code in YAML",`
|
|
8
8
|
__ __ _ __ __ _ ____
|
|
9
9
|
\\ \\ / // \\ | \\/ | | | _ \\ ___ ___ _ _ ___ ___ ___
|
|
10
10
|
\\ V // _ \\ | |\\/| | | | |_) / _ \\/ __| | | / _ \\/ _ \\ / _ \\
|
|
11
11
|
| |/ ___ \\| | | | |___| _ < __/\\__ \\ |_| | | | | | | __/
|
|
12
12
|
|_/_/ \\_\\_| |_|_____|_| \\_\\___||___/\\____|_| |_| |_|\\___|
|
|
13
13
|
`].join(`
|
|
14
|
-
`)).version(
|
|
14
|
+
`)).version(O.version).option("-v, --verbose","verbose output").hook("preAction",r=>{S(r.opts().verbose);}),e.addCommand(N()),e.addCommand(R()),e.addCommand(T()),e.addCommand(k()),e.addCommand(I()),e.addCommand(x()),e}Y().parse();//# sourceMappingURL=cli.js.map
|
|
15
15
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../package.json","../src/commands/validate.ts","../src/commands/build.ts","../src/commands/dev.ts","../src/commands/languages.ts","../src/commands/new.ts","../src/commands/templates.ts","../src/utils/consola.ts","../src/program.ts","../src/cli.ts"],"names":["package_default","prettifySchemaValidationError","error","resumePath","resumeStr","lineContent","pointer","filePath","chalk","errorType","message","codeLine","pointerLine","prettifyYamlParseError","errorMessage","lineMatch","joinNonEmptyString","line","column","validateResume","yamlStr","schema","lineCounter","LineCounter","resumeCST","parseDocument","validationResult","issues","issue","path","node","isNode","startOffset","pos","a","b","readResume","resuemPath","validate","fs","YAMLResumeError","resume","yaml","formattedError","errors","ResumeSchema","createValidateCommand","Command","validated","consola","inferOutput","outputDir","extname","baseName","getOutputPath","extension","index","total","fileName","getPdfPath","texPath","isCommandAvailable","command","which","inferLaTeXEnvironment","inferLaTeXCommand","resumePathOrTexFile","environment","texFile","args","cwd","normalizeExtension","generateOutput","layoutIndex","outputFile","dir","content","getResumeRenderer","compileLaTeX","result","execa","toCodeBlock","buildResume","options","allLayouts","DEFAULT_RESUME_LAYOUTS","totals","l","indices","createBuildCommand","watchResume","pdf","output","exclusiveBuild","coalesce","watcher","chokidar","createDevCommand","listLanguages","markdownTable","LOCALE_LANGUAGE_OPTIONS","value","getLocaleLanguageDetail","createLanguagesCommand","cmd","newResume","filename","templatePath","fileURLToPath","templateContent","createNewCommand","listLaTeXTemplates","LATEX_TEMPLATE_OPTIONS","details","getLatexTemplateDetail","createTemplatesCommand","setVerboseLog","verbose","createProgram","program","thisCommand"],"mappings":"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","file":"cli.js","sourcesContent":["{\n \"name\": \"yamlresume\",\n \"version\": \"0.8.0\",\n \"description\": \"The CLI interface for YAMLResume's engine\",\n \"license\": \"MIT\",\n \"author\": {\n \"name\": \"YAMLResume\",\n \"email\": \"support@yamlresume.com\",\n \"url\": \"https://yamlresume.dev\"\n },\n \"keywords\": [\n \"YAMLResume\",\n \"CV\",\n \"Resume\",\n \"LaTeX\",\n \"Typesetting\",\n \"PDF\",\n \"YAML\",\n \"JSON\"\n ],\n \"type\": \"module\",\n \"bin\": {\n \"yamlresume\": \"./dist/cli.js\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\",\n \"resources/resume.yml\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"build:watch\": \"tsup --watch\",\n \"build:clean\": \"rm -rf dist\",\n \"build:prod\": \"tsup --dts --minify --sourcemap\",\n \"prepublishOnly\": \"pnpm test\",\n \"dev\": \"tsx src/cli.ts\",\n \"test\": \"vitest --run\",\n \"test:cov\": \"vitest --coverage --run\",\n \"test:watch\": \"vitest\"\n },\n \"dependencies\": {\n \"@yamlresume/core\": \"workspace:*\",\n \"chalk\": \"^5.6.0\",\n \"chokidar\": \"^4.0.3\",\n \"coalescifn\": \"^0.1.1\",\n \"commander\": \"^14.0.1\",\n \"consola\": \"^3.4.2\",\n \"execa\": \"^9.6.0\",\n \"extensionless\": \"^1.9.9\",\n \"lodash-es\": \"^4.17.21\",\n \"markdown-table\": \"^3.0.4\",\n \"tslib\": \"^2.8.1\",\n \"which\": \"^5.0.0\",\n \"yaml\": \"^2.8.1\"\n },\n \"devDependencies\": {\n \"@types/chalk\": \"^2.2.4\",\n \"@types/commander\": \"^2.12.5\",\n \"@types/lodash-es\": \"^4.17.12\",\n \"@types/which\": \"^3.0.4\",\n \"tsx\": \"^4.20.5\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/yamlresume/yamlresume.git\",\n \"directory\": \"packages/cli\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/yamlresume/yamlresume/issues\"\n },\n \"homepage\": \"https://yamlresume.dev/docs/cli\",\n \"engines\": {\n \"node\": \">=20.0.0\"\n }\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport fs from 'node:fs'\nimport {\n joinNonEmptyString,\n type Resume,\n ResumeSchema,\n YAMLResumeError,\n} from '@yamlresume/core'\nimport chalk from 'chalk'\nimport { Command } from 'commander'\nimport consola from 'consola'\nimport yaml, { isNode, LineCounter, parseDocument } from 'yaml'\n\n/**\n * A positional error with line number, column number, and path.\n *\n * @param message The error message.\n * @param line The line number.\n * @param column The column number.\n * @param path The path to the error.\n */\nexport interface PositionalError {\n message: string\n line: number\n column: number\n path: (string | number | symbol)[]\n}\n\n/**\n * Formats an error in clang-style with line number, column pointer, and message\n *\n * @param error The positional error to format\n * @param resumePath The source file path\n * @param resumeStr The content of the source file for line display\n * @returns Formatted error string in clang style\n */\nexport function prettifySchemaValidationError(\n error: PositionalError,\n resumePath: string,\n resumeStr: string\n): string {\n const lines = resumeStr.split('\\n')\n const lineContent = lines[error.line - 1] || ''\n\n // Create the pointer line with spaces and caret\n const pointer = `${' '.repeat(error.column - 1)}^`\n\n // Color scheme similar to clang with enhanced visibility\n const filePath = chalk.white.bold(\n `${resumePath}:${error.line}:${error.column}`\n )\n const errorType = chalk.red.bold('warning')\n const message = chalk.white(error.message)\n const codeLine = chalk.white(lineContent)\n const pointerLine = chalk.green.bold(pointer)\n\n return [\n `${filePath}: ${errorType}: ${message}`,\n `${codeLine}`,\n `${pointerLine}`,\n ].join('\\n')\n}\n\n/**\n * Parses YAML parsing error messages and extracts line/column information\n *\n * @param errorMessage The error message from `yaml.parse`\n * @param resumePath The source file path\n * @param resumeStr The content of the source file for line display\n * @returns Formatted error string in clang style\n */\nexport function prettifyYamlParseError(\n errorMessage: string,\n resumePath: string,\n resumeStr: string\n): string {\n // parse the error message to extract line and column\n // example:\n // \"Nested mappings are not allowed in compact mappings at line 6, column 10\"\n const lineMatch = errorMessage.match(/at line (\\d+), column (\\d+)/)\n\n if (!lineMatch) {\n // if we can't parse the error, return a simple formatted message\n return joinNonEmptyString(\n [\n chalk.white.bold(resumePath),\n chalk.red.bold('error'),\n `${chalk.white(errorMessage)}.`,\n ],\n ': '\n )\n }\n\n const line = Number.parseInt(lineMatch[1], 10)\n const column = Number.parseInt(lineMatch[2], 10)\n const lines = resumeStr.split('\\n')\n const lineContent = lines[line - 1] || ''\n\n // create the pointer line with spaces and caret\n const pointer = `${' '.repeat(column - 1)}^`\n\n // color scheme similar to clang with enhanced visibility\n const filePath = chalk.white.bold(`${resumePath}:${line}:${column}`)\n const errorType = chalk.red.bold('error')\n const message = chalk.white(\n errorMessage\n .split('\\n')[0]\n .replace(/ at line \\d+, column \\d+:?/, '.')\n .trim()\n )\n const codeLine = chalk.white(lineContent)\n const pointerLine = chalk.green.bold(pointer)\n\n return [\n `${filePath}: ${errorType}: ${message}`,\n `${codeLine}`,\n `${pointerLine}`,\n ].join('\\n')\n}\n\n/**\n * Validates a YAML string against a Zod schema and returns errors.\n *\n * @param yamlStr The YAML string to validate.\n * @param schema The Zod schema to validate against.\n * @returns A list of positional errors, or an empty array if validation is\n * successful.\n */\nexport function validateResume(\n yamlStr: string,\n schema: typeof ResumeSchema\n): PositionalError[] {\n const lineCounter = new LineCounter()\n\n // CST: Concrete Syntax Tree\n const resumeCST = parseDocument(yamlStr, {\n lineCounter,\n keepSourceTokens: true,\n })\n\n const validationResult = schema.safeParse(resumeCST.toJS())\n\n if (validationResult.success) {\n return []\n }\n\n const {\n error: { issues },\n } = validationResult\n\n return issues\n .map((issue) => {\n const path = issue.path\n const node = resumeCST.getIn(path, true)\n\n let line = 1\n let column = 1\n\n if (isNode(node) && node.range) {\n const startOffset = node.range[0]\n const pos = lineCounter.linePos(startOffset)\n line = pos.line\n column = pos.col\n }\n\n return {\n message: issue.message,\n line,\n column,\n path,\n }\n })\n .sort((a, b) => a.line - b.line)\n}\n\n/**\n * Read the resume from the source file and validate it on request.\n *\n * Steps:\n *\n * 1. read the resume from the source file\n * 2. validate the resume with `yaml.parse`\n * 3. if `validate` is true, validate the resume with `ResumeSchema`\n *\n * @param resuemPath - The source resume file path (YAML, YML, or JSON).\n * @returns The resume object.\n * @throws {Error} If the source file cannot be read or is invalid.\n */\nexport function readResume(\n resuemPath: string,\n validate = true\n): { resume: Resume; validated: 'success' | 'failed' | 'unknown' } {\n let resumeStr: string\n\n try {\n resumeStr = fs.readFileSync(resuemPath, 'utf8')\n } catch (_error) {\n throw new YAMLResumeError('FILE_READ_ERROR', { path: resuemPath })\n }\n\n let resume: Resume\n\n try {\n resume = yaml.parse(resumeStr) as Resume\n } catch (error) {\n // Format YAML parsing errors in clang style\n //\n // Actually you can use `parseDocument` to get a list of errors, here we\n // only use `yaml.parse` to get the first error, which should be enough for\n // users to know that there is something wrong with the yaml file.\n const formattedError = prettifyYamlParseError(\n error.message,\n resuemPath,\n resumeStr\n )\n\n console.log(formattedError)\n throw new YAMLResumeError('INVALID_YAML', {\n error: `Failed to parse ${resuemPath}.`,\n })\n }\n\n if (validate) {\n const errors = validateResume(resumeStr, ResumeSchema)\n\n if (errors.length > 0) {\n for (const error of errors) {\n console.log(prettifySchemaValidationError(error, resuemPath, resumeStr))\n }\n\n return { resume, validated: 'failed' }\n }\n\n return { resume, validated: 'success' }\n }\n\n return { resume, validated: 'unknown' }\n}\n\n/**\n * Create a command instance to validate a YAML resume\n */\nexport function createValidateCommand() {\n return new Command()\n .name('validate')\n .description('validate a resume against the YAMLResume schema')\n .argument('<resume-path>', 'the resume file path')\n .action(async (resumePath: string) => {\n try {\n const { validated } = readResume(resumePath, true)\n\n if (validated === 'success') {\n consola.success('Resume validation passed.')\n }\n if (validated === 'failed') {\n consola.fail('Resume validation failed.')\n }\n } catch (error) {\n consola.error(error.message)\n process.exit(error.errno)\n }\n })\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport {\n DEFAULT_RESUME_LAYOUTS,\n getResumeRenderer,\n joinNonEmptyString,\n type Resume,\n toCodeBlock,\n YAMLResumeError,\n} from '@yamlresume/core'\nimport { Command } from 'commander'\nimport { consola } from 'consola'\nimport { execa } from 'execa'\nimport which from 'which'\n\nimport { readResume } from './validate'\n\n/**\n * Infer the output file name from the source file name\n *\n * For now we support yaml, yml and json file extensions, and the output file\n * will have a `.tex` extension.\n *\n * @param resumePath - The source resume file\n * @param outputDir - Optional output directory to place the file in\n * @returns The output file name\n * @throws {Error} If the source file has an unsupported extension.\n */\nexport function inferOutput(resumePath: string, outputDir?: string): string {\n const extname = path.extname(resumePath)\n\n if (\n resumePath.endsWith('.yaml') ||\n resumePath.endsWith('.yml') ||\n resumePath.endsWith('.json')\n ) {\n const baseName = path.basename(\n resumePath.replace(/\\.yaml|\\.yml|\\.json$/, '.tex')\n )\n if (outputDir) {\n return path.join(outputDir, baseName)\n }\n return resumePath.replace(/\\.yaml|\\.yml|\\.json$/, '.tex')\n }\n\n throw new YAMLResumeError('INVALID_EXTNAME', { extname })\n}\n\n/**\n * Get the output file path with support for multiple outputs and custom extension\n *\n * @param resumePath - The source resume file path\n * @param extension - The target file extension (e.g., '.tex', '.md')\n * @param index - The index of the current layout\n * @param total - The total number of layouts for this engine\n * @param outputDir - Optional output directory\n * @returns The determined output file path\n */\nfunction getOutputPath(\n resumePath: string,\n extension: string,\n index: number,\n total: number,\n outputDir?: string\n): string {\n const baseName = path.basename(resumePath.replace(/\\.yaml|\\.yml|\\.json$/, ''))\n\n // If there are multiple layouts, append the index to the filename\n // e.g., resume.0.tex, resume.1.tex\n // Otherwise, use the base filename\n // e.g., resume.tex\n const fileName =\n total > 1 ? `${baseName}.${index}${extension}` : `${baseName}${extension}`\n\n if (outputDir) {\n return path.join(outputDir, fileName)\n }\n return path.join(path.dirname(resumePath), fileName)\n}\n\n/**\n * Get the PDF output path from a tex file path\n *\n * @param texPath - The tex file path\n * @returns The PDF file path\n */\nexport function getPdfPath(texPath: string): string {\n return texPath.replace(/\\.tex$/, '.pdf')\n}\n\ntype LaTeXEnvironment = 'xelatex' | 'tectonic'\n\n/**\n * Check if a command is available\n *\n * @param command - The command to check\n * @returns True if the command is available, false otherwise\n */\nexport function isCommandAvailable(command: string): boolean {\n try {\n return !!which.sync(command)\n } catch {\n return false\n }\n}\n\n/**\n * Infer the LaTeX environment to use\n *\n * We support xelatex and tectonic, if both are installed we will prioritize\n * xelatex.\n *\n * @returns The LaTeX environment PATH.\n * @throws {Error} If neither 'xelatex' nor 'tectonic' is found in system PATH.\n */\nexport function inferLaTeXEnvironment(): LaTeXEnvironment {\n if (isCommandAvailable('xelatex')) {\n return 'xelatex'\n }\n\n if (isCommandAvailable('tectonic')) {\n return 'tectonic'\n }\n\n throw new YAMLResumeError('LATEX_NOT_FOUND', {})\n}\n\n/**\n * Infer the LaTeX command to use based on the LaTeX environment\n *\n * @param resumePathOrTexFile - The source resume file OR the target .tex file\n * @param outputDir - Optional output directory\n * @returns The LaTeX command\n * @throws {Error} If the LaTeX environment cannot be inferred or the source\n * file extension is unsupported.\n */\nexport function inferLaTeXCommand(\n resumePathOrTexFile: string,\n outputDir?: string\n): { command: string; args: string[]; cwd: string } {\n const environment = inferLaTeXEnvironment()\n\n // If the input is already a .tex file, use it directly; otherwise infer from .yaml/.json\n const texFile = resumePathOrTexFile.endsWith('.tex')\n ? resumePathOrTexFile\n : inferOutput(resumePathOrTexFile, outputDir)\n\n let command = ''\n let args: string[] = []\n\n switch (environment) {\n case 'xelatex':\n command = 'xelatex'\n args = ['-halt-on-error', path.basename(texFile)]\n break\n case 'tectonic':\n command = 'tectonic'\n args = [path.basename(texFile)]\n break\n }\n\n const cwd = outputDir\n ? path.resolve(outputDir)\n : path.dirname(path.resolve(texFile))\n\n return { command, args, cwd }\n}\n\n/**\n * Normalize the file extension that can be used in the output file name\n *\n * @param extension - file extension\n * @returns\n */\nexport function normalizeExtension(extension: string): string {\n switch (extension) {\n case '.tex':\n return 'tex'\n case '.md':\n return 'markdown'\n default:\n return extension.replace('.', '')\n }\n}\n\n/**\n * Shared helper to generate output file from a layout\n */\nfunction generateOutput(\n resumePath: string,\n resume: Resume,\n index: number,\n total: number,\n outputDir: string | undefined,\n extension: string,\n layoutIndex: number\n): string {\n const outputFile = getOutputPath(\n resumePath,\n extension,\n index,\n total,\n outputDir\n )\n\n const dir = path.dirname(outputFile)\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n\n const renderer = getResumeRenderer(resume, layoutIndex)\n const content = renderer.render()\n\n try {\n fs.writeFileSync(outputFile, content)\n consola.success(\n joinNonEmptyString(\n [\n `Generated resume ${normalizeExtension(extension)} file successfully:`,\n outputFile,\n ],\n ' '\n )\n )\n } catch (_error) {\n throw new YAMLResumeError('FILE_WRITE_ERROR', { path: outputFile })\n }\n\n return outputFile\n}\n\n/**\n * Compile a TeX file to PDF\n */\nasync function compileLaTeX(texFile: string, outputDir?: string) {\n const { command, args, cwd } = inferLaTeXCommand(texFile, outputDir)\n\n consola.start(\n `Generating resume pdf file with command: \\`${command} ${args.join(' ')}\\`...`\n )\n\n try {\n const result = await execa(command, args, {\n cwd,\n encoding: 'utf8',\n })\n consola.success(\n `Generated resume pdf file successfully: ${getPdfPath(texFile)}`\n )\n consola.debug(joinNonEmptyString(['stdout: ', toCodeBlock(result.stdout)]))\n } catch (error) {\n consola.debug(joinNonEmptyString(['stdout: ', toCodeBlock(error.stdout)]))\n consola.debug(joinNonEmptyString(['stderr: ', toCodeBlock(error.stderr)]))\n throw new YAMLResumeError('LATEX_COMPILE_ERROR', { error: error.message })\n }\n}\n\n/**\n * Build a YAML resume to LaTeX & PDF and/or Markdown\n *\n * It first validates the resume against the schema (unless `--no-validate` flag\n * is used), then iterates through configured layouts to generate outputs.\n *\n * @param resumePath - The source resume file path (YAML, YML, or JSON).\n * @param options - Build options including validation, PDF generation flags,\n * and output directory.\n */\nexport async function buildResume(\n resumePath: string,\n options: { pdf?: boolean; validate?: boolean; output?: string } = {\n pdf: true,\n validate: true,\n }\n) {\n const { resume } = readResume(resumePath, options.validate)\n\n // Fallback to default layout if none provided\n const allLayouts = resume.layouts ?? DEFAULT_RESUME_LAYOUTS\n // Ensure resume has layouts for the renderer to use\n if (!resume.layouts) {\n resume.layouts = allLayouts\n }\n\n // Count totals for each engine to determine file naming strategy\n // (e.g. resume.0.tex vs resume.tex)\n const totals = {\n latex: allLayouts.filter((l) => l.engine === 'latex').length,\n markdown: allLayouts.filter((l) => l.engine === 'markdown').length,\n }\n\n // Track current index for each engine\n const indices = {\n latex: 0,\n markdown: 0,\n }\n\n for (let layoutIndex = 0; layoutIndex < allLayouts.length; layoutIndex++) {\n const layout = allLayouts[layoutIndex]\n\n switch (layout.engine) {\n case 'latex': {\n const texFile = generateOutput(\n resumePath,\n resume,\n indices.latex++,\n totals.latex,\n options.output,\n '.tex',\n layoutIndex\n )\n\n if (options.pdf === true) {\n await compileLaTeX(texFile, options.output)\n }\n break\n }\n case 'markdown': {\n generateOutput(\n resumePath,\n resume,\n indices.markdown++,\n totals.markdown,\n options.output,\n '.md',\n layoutIndex\n )\n break\n }\n }\n }\n}\n\n/**\n * Create a command instance to build a YAML resume to LaTeX and PDF\n */\nexport function createBuildCommand() {\n return new Command()\n .name('build')\n .description('build a resume to LaTeX, PDF, or Markdown')\n .argument('<resume-path>', 'the resume file path')\n .option(\n '--no-pdf',\n 'only generate TeX file without PDF (for LaTeX layouts)'\n )\n .option('--no-validate', 'skip resume schema validation')\n .option('-o, --output <dir>', 'output directory for generated files')\n .action(\n async (\n resumePath: string,\n options: { pdf: boolean; validate: boolean; output?: string }\n ) => {\n try {\n await buildResume(resumePath, options)\n } catch (error) {\n consola.error(error.message)\n process.exit(error.errno)\n }\n }\n )\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport chokidar from 'chokidar'\n\nimport { coalesce } from 'coalescifn'\nimport { Command } from 'commander'\nimport { consola } from 'consola'\n\nimport { buildResume } from './build'\n\n/**\n * Options for the watchResume function\n *\n * @param pdf - Whether to generate PDF\n * @param validate - Whether to validate the resume\n */\ntype WatchOptions = {\n pdf?: boolean\n validate?: boolean\n output?: string\n}\n\n/**\n * Watch a resume source file and rebuild on changes.\n *\n * - Only one build runs at a time.\n * - If multiple events arrive during a build, run exactly one more build after\n * it finishes (coalesce bursts).\n * - Uses chokidar for robust file watching that handles editor operations.\n *\n * @param resumePath - The resume file to watch\n * @param options - Build and watch options\n * @returns Chokidar watcher instance\n */\nexport function watchResume(\n resumePath: string,\n options: WatchOptions = { pdf: true, validate: true }\n) {\n const { pdf, validate, output } = options\n\n // there should be only one build running at a time\n const exclusiveBuild = coalesce(() =>\n buildResume(resumePath, { pdf, validate, output })\n )\n\n // initial build\n exclusiveBuild()\n\n consola.start(`Watching file changes: ${resumePath}...`)\n\n // use chokidar for robust file watching that handles vim and other editors\n // properly.\n //\n // vim will save the file in a single atomic operation, that being said, it\n // will first create a temporary file (the '.swp' file), then rename it to the\n // final file.\n //\n // Node.js `fs.watch` has trouble with this, so we use chokidar instead.\n const watcher = chokidar.watch(resumePath, {\n awaitWriteFinish: {\n stabilityThreshold: 200, // wait 200ms after file stops changing\n pollInterval: 200, // check every 200ms\n },\n ignoreInitial: true, // don't trigger on initial file discovery\n })\n\n // handle file changes - chokidar's awaitWriteFinish already handles\n // debouncing\n watcher.on('change', () => exclusiveBuild())\n\n // handle file additions (in case file gets recreated)\n watcher.on('add', () => exclusiveBuild())\n\n return watcher\n}\n\n/**\n * Create a command instance to run in watch mode\n */\nexport function createDevCommand() {\n return new Command()\n .name('dev')\n .description('build a resume on file changes (watch mode)')\n .argument('<resume-path>', 'the resume file path')\n .option('--no-pdf', 'only generate TeX file without PDF')\n .option('--no-validate', 'skip resume schema validation')\n .option('-o, --output <dir>', 'output directory for generated files')\n .action(\n (\n resumePath: string,\n options: { pdf: boolean; validate: boolean; output?: string }\n ) => {\n watchResume(resumePath, options)\n }\n )\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n getLocaleLanguageDetail,\n LOCALE_LANGUAGE_OPTIONS,\n} from '@yamlresume/core'\nimport { Command } from 'commander'\nimport consola from 'consola'\nimport { markdownTable } from 'markdown-table'\n\n/**\n * Generates a markdown table listing all supported locale languages.\n *\n * The table includes columns for the language code and the language name.\n *\n * @returns A string containing the formatted markdown table.\n */\nexport function listLanguages() {\n return markdownTable([\n ['layout.locale.language', 'Language Name'],\n ...LOCALE_LANGUAGE_OPTIONS.map((value) => [\n value,\n getLocaleLanguageDetail(value).name,\n ]),\n ])\n}\n\n/**\n * Create a command instance to list supported languages\n */\n\nexport function createLanguagesCommand() {\n const cmd = new Command()\n .name('languages')\n .description('i18n and l10n support')\n\n cmd\n .command('list')\n .description('list all supported languages')\n .action(() => {\n consola.log(listLanguages())\n })\n\n return cmd\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport {\n joinNonEmptyString,\n toCodeBlock,\n YAMLResumeError,\n} from '@yamlresume/core'\nimport { Command } from 'commander'\nimport consola from 'consola'\n\n/**\n * Creates a new resume file with the given filename\n *\n * @param filename - The name of the resume file to create\n * @throws {YAMLResumeError} When there are file-related errors:\n * - FILE_CONFLICT: When the file already exists\n * - FILE_READ_ERROR: When there's an error reading the template\n * - FILE_WRITE_ERROR: When there's an error writing the file\n */\nexport function newResume(filename: string) {\n if (fs.existsSync(filename)) {\n throw new YAMLResumeError('FILE_CONFLICT', { path: filename })\n }\n\n const templatePath = path.join(\n path.dirname(fileURLToPath(import.meta.url)),\n /* v8 ignore start */\n // I din't find a way to mock `import.meta.url` in tests so we have to\n // ignore the following lines for coverage calculation\n import.meta.url.includes('dist')\n ? '../resources/resume.yml'\n : '../../resources/resume.yml'\n /* v8 ignore stop */\n )\n\n let templateContent: string\n\n try {\n templateContent = fs.readFileSync(templatePath, 'utf8')\n } catch (error) {\n consola.debug(\n joinNonEmptyString(['Error reading template: ', toCodeBlock(error.stack)])\n )\n throw new YAMLResumeError('FILE_READ_ERROR', { path: templatePath })\n }\n\n try {\n fs.writeFileSync(filename, templateContent)\n consola.success(`Created ${filename} successfully.`)\n } catch (error) {\n consola.debug(\n joinNonEmptyString(['Error creating resume: ', toCodeBlock(error.stack)])\n )\n throw new YAMLResumeError('FILE_WRITE_ERROR', { path: filename })\n }\n}\n\n/**\n * Create a command instance to create a new YAML resume\n */\nexport function createNewCommand() {\n return new Command()\n .name('new')\n .description('create a new resume')\n .argument('[filename]', 'output filename', 'resume.yml')\n .action((filename) => {\n try {\n newResume(filename)\n } catch (error) {\n consola.error(error.message)\n process.exit(error.errno)\n }\n })\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n getLatexTemplateDetail,\n LATEX_TEMPLATE_OPTIONS,\n} from '@yamlresume/core'\nimport { Command } from 'commander'\nimport consola from 'consola'\nimport { markdownTable } from 'markdown-table'\n\n/**\n * Generates a markdown table listing all supported templates.\n *\n * The table includes columns for the template id, name and description.\n *\n * @returns A string containing the formatted markdown table.\n */\nexport function listLaTeXTemplates() {\n return markdownTable([\n ['layout.template', 'Engine', 'Template Name', 'Description'],\n ...LATEX_TEMPLATE_OPTIONS.map((value) => {\n const details = getLatexTemplateDetail(value)\n return [value, details.engine, details.name, details.description]\n }),\n ])\n}\n\n/**\n * Create a command instance to list supported templates\n */\nexport function createTemplatesCommand() {\n const cmd = new Command()\n .name('templates')\n .description('manage resume templates')\n\n cmd\n .command('list')\n .description('list all supported templates')\n .action(() => {\n consola.log(listLaTeXTemplates())\n })\n\n return cmd\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport consola from 'consola'\n\n/**\n * Set the verbose mode for consola\n *\n * @param verbose - Whether to enable verbose mode\n * @see https://github.com/unjs/consola?tab=readme-ov-file#log-level\n */\nexport function setVerboseLog(verbose: boolean) {\n consola.level = verbose ? 4 : 3\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Command } from 'commander'\n\nimport packageJson from '../package.json' with { type: 'json' }\nimport {\n createBuildCommand,\n createDevCommand,\n createLanguagesCommand,\n createNewCommand,\n createTemplatesCommand,\n createValidateCommand,\n} from './commands'\nimport { setVerboseLog } from './utils'\n\n/**\n * Create the CLI program.\n *\n * @returns The CLI program.\n */\nexport function createProgram(): Command {\n const program = new Command()\n\n const banner = `\n __ __ _ __ __ _ ____\n \\\\ \\\\ / // \\\\ | \\\\/ | | | _ \\\\ ___ ___ _ _ ___ ___ ___\n \\\\ V // _ \\\\ | |\\\\/| | | | |_) / _ \\\\/ __| | | / _ \\\\/ _ \\\\ / _ \\\\\n | |/ ___ \\\\| | | | |___| _ < __/\\\\__ \\\\ |_| | | | | | | __/\n |_/_/ \\\\_\\\\_| |_|_____|_| \\\\_\\\\___||___/\\\\____|_| |_| |_|\\\\___|\n`\n\n program\n .name('yamlresume')\n .description(['YAMLResume — Resume as Code in YAML', banner].join('\\n'))\n .version(packageJson.version)\n .option('-v, --verbose', 'verbose output')\n .hook('preAction', (thisCommand) => {\n setVerboseLog(thisCommand.opts().verbose)\n })\n\n program.addCommand(createNewCommand())\n program.addCommand(createBuildCommand())\n program.addCommand(createDevCommand())\n program.addCommand(createLanguagesCommand())\n program.addCommand(createTemplatesCommand())\n program.addCommand(createValidateCommand())\n\n return program\n}\n","#!/usr/bin/env node\n/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { createProgram } from './program'\n\ncreateProgram().parse()\n"]}
|
|
1
|
+
{"version":3,"sources":["../package.json","../src/commands/validate.ts","../src/commands/build.ts","../src/commands/dev.ts","../src/commands/languages.ts","../src/commands/new.ts","../src/commands/templates.ts","../src/utils/consola.ts","../src/program.ts","../src/cli.ts"],"names":["package_default","prettifySchemaValidationError","error","resumePath","resumeStr","lineContent","pointer","filePath","chalk","errorType","message","codeLine","pointerLine","prettifyYamlParseError","errorMessage","lineMatch","joinNonEmptyString","line","column","validateResume","yamlStr","schema","lineCounter","LineCounter","resumeCST","parseDocument","validationResult","issues","issue","path","node","isNode","startOffset","pos","b","readResume","resuemPath","validate","fs","YAMLResumeError","resume","yaml","formattedError","errors","ResumeSchema","createValidateCommand","Command","validated","consola","inferOutput","outputDir","extname","baseName","getOutputPath","extension","index","total","fileName","getPdfPath","texPath","isCommandAvailable","command","which","inferLaTeXEnvironment","inferLaTeXCommand","resumePathOrTexFile","environment","texFile","args","cwd","normalizeExtension","generateOutput","layoutIndex","outputFile","dir","content","getResumeRenderer","compileLaTeX","result","execa","toCodeBlock","buildResume","options","allLayouts","DEFAULT_RESUME_LAYOUTS","totals","l","indices","createBuildCommand","watchResume","pdf","output","exclusiveBuild","coalesce","watcher","chokidar","createDevCommand","listLanguages","markdownTable","LOCALE_LANGUAGE_OPTIONS","value","getLocaleLanguageDetail","createLanguagesCommand","cmd","newResume","filename","templatePath","fileURLToPath","templateContent","createNewCommand","listTemplates","templates","LATEX_TEMPLATE_OPTIONS","details","getLatexTemplateDetail","HTML_TEMPLATE_OPTIONS","getHtmlTemplateDetail","createTemplatesCommand","setVerboseLog","verbose","createProgram","program","thisCommand"],"mappings":"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","file":"cli.js","sourcesContent":["{\n \"name\": \"yamlresume\",\n \"version\": \"0.9.0\",\n \"description\": \"The CLI interface for YAMLResume's engine\",\n \"license\": \"MIT\",\n \"author\": {\n \"name\": \"YAMLResume\",\n \"email\": \"support@yamlresume.com\",\n \"url\": \"https://yamlresume.dev\"\n },\n \"keywords\": [\n \"YAMLResume\",\n \"CV\",\n \"Resume\",\n \"LaTeX\",\n \"Typesetting\",\n \"PDF\",\n \"YAML\",\n \"JSON\"\n ],\n \"type\": \"module\",\n \"bin\": {\n \"yamlresume\": \"./dist/cli.js\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\",\n \"resources/resume.yml\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"build:watch\": \"tsup --watch\",\n \"build:clean\": \"rm -rf dist\",\n \"build:prod\": \"tsup --dts --minify --sourcemap\",\n \"prepublishOnly\": \"pnpm test\",\n \"dev\": \"tsx src/cli.ts\",\n \"test\": \"vitest --run\",\n \"test:cov\": \"vitest --coverage --run\",\n \"test:watch\": \"vitest\"\n },\n \"dependencies\": {\n \"@yamlresume/core\": \"workspace:*\",\n \"chalk\": \"^5.6.0\",\n \"chokidar\": \"^4.0.3\",\n \"coalescifn\": \"^0.1.1\",\n \"commander\": \"^14.0.1\",\n \"consola\": \"^3.4.2\",\n \"execa\": \"^9.6.1\",\n \"extensionless\": \"^2.0.5\",\n \"lodash-es\": \"^4.17.21\",\n \"markdown-table\": \"^3.0.4\",\n \"tslib\": \"^2.8.1\",\n \"which\": \"^5.0.0\",\n \"yaml\": \"^2.8.2\"\n },\n \"devDependencies\": {\n \"@types/chalk\": \"^2.2.4\",\n \"@types/commander\": \"^2.12.5\",\n \"@types/lodash-es\": \"^4.17.12\",\n \"@types/which\": \"^3.0.4\",\n \"tsx\": \"^4.20.5\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/yamlresume/yamlresume.git\",\n \"directory\": \"packages/cli\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/yamlresume/yamlresume/issues\"\n },\n \"homepage\": \"https://yamlresume.dev/docs/cli\",\n \"engines\": {\n \"node\": \">=20.0.0\"\n }\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport fs from 'node:fs'\nimport {\n joinNonEmptyString,\n type Resume,\n ResumeSchema,\n YAMLResumeError,\n} from '@yamlresume/core'\nimport chalk from 'chalk'\nimport { Command } from 'commander'\nimport consola from 'consola'\nimport yaml, { isNode, LineCounter, parseDocument } from 'yaml'\n\n/**\n * A positional error with line number, column number, and path.\n *\n * @param message The error message.\n * @param line The line number.\n * @param column The column number.\n * @param path The path to the error.\n */\nexport interface PositionalError {\n message: string\n line: number\n column: number\n path: (string | number | symbol)[]\n}\n\n/**\n * Formats an error in clang-style with line number, column pointer, and message\n *\n * @param error The positional error to format\n * @param resumePath The source file path\n * @param resumeStr The content of the source file for line display\n * @returns Formatted error string in clang style\n */\nexport function prettifySchemaValidationError(\n error: PositionalError,\n resumePath: string,\n resumeStr: string\n): string {\n const lines = resumeStr.split('\\n')\n const lineContent = lines[error.line - 1] || ''\n\n // Create the pointer line with spaces and caret\n const pointer = `${' '.repeat(error.column - 1)}^`\n\n // Color scheme similar to clang with enhanced visibility\n const filePath = chalk.white.bold(\n `${resumePath}:${error.line}:${error.column}`\n )\n const errorType = chalk.red.bold('warning')\n const message = chalk.white(error.message)\n const codeLine = chalk.white(lineContent)\n const pointerLine = chalk.green.bold(pointer)\n\n return [\n `${filePath}: ${errorType}: ${message}`,\n `${codeLine}`,\n `${pointerLine}`,\n ].join('\\n')\n}\n\n/**\n * Parses YAML parsing error messages and extracts line/column information\n *\n * @param errorMessage The error message from `yaml.parse`\n * @param resumePath The source file path\n * @param resumeStr The content of the source file for line display\n * @returns Formatted error string in clang style\n */\nexport function prettifyYamlParseError(\n errorMessage: string,\n resumePath: string,\n resumeStr: string\n): string {\n // parse the error message to extract line and column\n // example:\n // \"Nested mappings are not allowed in compact mappings at line 6, column 10\"\n const lineMatch = errorMessage.match(/at line (\\d+), column (\\d+)/)\n\n if (!lineMatch) {\n // if we can't parse the error, return a simple formatted message\n return joinNonEmptyString(\n [\n chalk.white.bold(resumePath),\n chalk.red.bold('error'),\n `${chalk.white(errorMessage)}.`,\n ],\n ': '\n )\n }\n\n const line = Number.parseInt(lineMatch[1], 10)\n const column = Number.parseInt(lineMatch[2], 10)\n const lines = resumeStr.split('\\n')\n const lineContent = lines[line - 1] || ''\n\n // create the pointer line with spaces and caret\n const pointer = `${' '.repeat(column - 1)}^`\n\n // color scheme similar to clang with enhanced visibility\n const filePath = chalk.white.bold(`${resumePath}:${line}:${column}`)\n const errorType = chalk.red.bold('error')\n const message = chalk.white(\n errorMessage\n .split('\\n')[0]\n .replace(/ at line \\d+, column \\d+:?/, '.')\n .trim()\n )\n const codeLine = chalk.white(lineContent)\n const pointerLine = chalk.green.bold(pointer)\n\n return [\n `${filePath}: ${errorType}: ${message}`,\n `${codeLine}`,\n `${pointerLine}`,\n ].join('\\n')\n}\n\n/**\n * Validates a YAML string against a Zod schema and returns errors.\n *\n * @param yamlStr The YAML string to validate.\n * @param schema The Zod schema to validate against.\n * @returns A list of positional errors, or an empty array if validation is\n * successful.\n */\nexport function validateResume(\n yamlStr: string,\n schema: typeof ResumeSchema\n): PositionalError[] {\n const lineCounter = new LineCounter()\n\n // CST: Concrete Syntax Tree\n const resumeCST = parseDocument(yamlStr, {\n lineCounter,\n keepSourceTokens: true,\n })\n\n const validationResult = schema.safeParse(resumeCST.toJS())\n\n if (validationResult.success) {\n return []\n }\n\n const {\n error: { issues },\n } = validationResult\n\n return issues\n .map((issue) => {\n const path = issue.path\n const node = resumeCST.getIn(path, true)\n\n let line = 1\n let column = 1\n\n if (isNode(node) && node.range) {\n const startOffset = node.range[0]\n const pos = lineCounter.linePos(startOffset)\n line = pos.line\n column = pos.col\n }\n\n return {\n message: issue.message,\n line,\n column,\n path,\n }\n })\n .sort((a, b) => a.line - b.line)\n}\n\n/**\n * Read the resume from the source file and validate it on request.\n *\n * Steps:\n *\n * 1. read the resume from the source file\n * 2. validate the resume with `yaml.parse`\n * 3. if `validate` is true, validate the resume with `ResumeSchema`\n *\n * @param resuemPath - The source resume file path (YAML, YML, or JSON).\n * @returns The resume object.\n * @throws {Error} If the source file cannot be read or is invalid.\n */\nexport function readResume(\n resuemPath: string,\n validate = true\n): { resume: Resume; validated: 'success' | 'failed' | 'unknown' } {\n let resumeStr: string\n\n try {\n resumeStr = fs.readFileSync(resuemPath, 'utf8')\n } catch (_error) {\n throw new YAMLResumeError('FILE_READ_ERROR', { path: resuemPath })\n }\n\n let resume: Resume\n\n try {\n resume = yaml.parse(resumeStr) as Resume\n } catch (error) {\n // Format YAML parsing errors in clang style\n //\n // Actually you can use `parseDocument` to get a list of errors, here we\n // only use `yaml.parse` to get the first error, which should be enough for\n // users to know that there is something wrong with the yaml file.\n const formattedError = prettifyYamlParseError(\n error.message,\n resuemPath,\n resumeStr\n )\n\n console.log(formattedError)\n throw new YAMLResumeError('INVALID_YAML', {\n error: `Failed to parse ${resuemPath}.`,\n })\n }\n\n if (validate) {\n const errors = validateResume(resumeStr, ResumeSchema)\n\n if (errors.length > 0) {\n for (const error of errors) {\n console.log(prettifySchemaValidationError(error, resuemPath, resumeStr))\n }\n\n return { resume, validated: 'failed' }\n }\n\n return { resume, validated: 'success' }\n }\n\n return { resume, validated: 'unknown' }\n}\n\n/**\n * Create a command instance to validate a YAML resume\n */\nexport function createValidateCommand() {\n return new Command()\n .name('validate')\n .description('validate a resume against the YAMLResume schema')\n .argument('<resume-path>', 'the resume file path')\n .action(async (resumePath: string) => {\n try {\n const { validated } = readResume(resumePath, true)\n\n if (validated === 'success') {\n consola.success('Resume validation passed.')\n }\n if (validated === 'failed') {\n consola.fail('Resume validation failed.')\n }\n } catch (error) {\n consola.error(error.message)\n process.exit(error.errno)\n }\n })\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport {\n DEFAULT_RESUME_LAYOUTS,\n getResumeRenderer,\n joinNonEmptyString,\n type Resume,\n toCodeBlock,\n YAMLResumeError,\n} from '@yamlresume/core'\nimport { Command } from 'commander'\nimport { consola } from 'consola'\nimport { execa } from 'execa'\nimport which from 'which'\n\nimport { readResume } from './validate'\n\n/**\n * Infer the output file name from the source file name\n *\n * For now we support yaml, yml and json file extensions, and the output file\n * will have a `.tex` extension.\n *\n * @param resumePath - The source resume file\n * @param outputDir - Optional output directory to place the file in\n * @returns The output file name\n * @throws {Error} If the source file has an unsupported extension.\n */\nexport function inferOutput(resumePath: string, outputDir?: string): string {\n const extname = path.extname(resumePath)\n\n if (\n resumePath.endsWith('.yaml') ||\n resumePath.endsWith('.yml') ||\n resumePath.endsWith('.json')\n ) {\n const baseName = path.basename(\n resumePath.replace(/\\.yaml|\\.yml|\\.json$/, '.tex')\n )\n if (outputDir) {\n return path.join(outputDir, baseName)\n }\n return resumePath.replace(/\\.yaml|\\.yml|\\.json$/, '.tex')\n }\n\n throw new YAMLResumeError('INVALID_EXTNAME', { extname })\n}\n\n/**\n * Get the output file path with support for multiple outputs and custom extension\n *\n * @param resumePath - The source resume file path\n * @param extension - The target file extension (e.g., '.tex', '.md')\n * @param index - The index of the current layout\n * @param total - The total number of layouts for this engine\n * @param outputDir - Optional output directory\n * @returns The determined output file path\n */\nfunction getOutputPath(\n resumePath: string,\n extension: string,\n index: number,\n total: number,\n outputDir?: string\n): string {\n const baseName = path.basename(resumePath.replace(/\\.yaml|\\.yml|\\.json$/, ''))\n\n // If there are multiple layouts, append the index to the filename\n // e.g., resume.0.tex, resume.1.tex\n // Otherwise, use the base filename\n // e.g., resume.tex\n const fileName =\n total > 1 ? `${baseName}.${index}${extension}` : `${baseName}${extension}`\n\n if (outputDir) {\n return path.join(outputDir, fileName)\n }\n return path.join(path.dirname(resumePath), fileName)\n}\n\n/**\n * Get the PDF output path from a tex file path\n *\n * @param texPath - The tex file path\n * @returns The PDF file path\n */\nexport function getPdfPath(texPath: string): string {\n return texPath.replace(/\\.tex$/, '.pdf')\n}\n\ntype LaTeXEnvironment = 'xelatex' | 'tectonic'\n\n/**\n * Check if a command is available\n *\n * @param command - The command to check\n * @returns True if the command is available, false otherwise\n */\nexport function isCommandAvailable(command: string): boolean {\n try {\n return !!which.sync(command)\n } catch {\n return false\n }\n}\n\n/**\n * Infer the LaTeX environment to use\n *\n * We support xelatex and tectonic, if both are installed we will prioritize\n * xelatex.\n *\n * @returns The LaTeX environment PATH.\n * @throws {Error} If neither 'xelatex' nor 'tectonic' is found in system PATH.\n */\nexport function inferLaTeXEnvironment(): LaTeXEnvironment {\n if (isCommandAvailable('xelatex')) {\n return 'xelatex'\n }\n\n if (isCommandAvailable('tectonic')) {\n return 'tectonic'\n }\n\n throw new YAMLResumeError('LATEX_NOT_FOUND', {})\n}\n\n/**\n * Infer the LaTeX command to use based on the LaTeX environment\n *\n * @param resumePathOrTexFile - The source resume file OR the target .tex file\n * @param outputDir - Optional output directory\n * @returns The LaTeX command\n * @throws {Error} If the LaTeX environment cannot be inferred or the source\n * file extension is unsupported.\n */\nexport function inferLaTeXCommand(\n resumePathOrTexFile: string,\n outputDir?: string\n): { command: string; args: string[]; cwd: string } {\n const environment = inferLaTeXEnvironment()\n\n // If the input is already a .tex file, use it directly; otherwise infer from .yaml/.json\n const texFile = resumePathOrTexFile.endsWith('.tex')\n ? resumePathOrTexFile\n : inferOutput(resumePathOrTexFile, outputDir)\n\n let command = ''\n let args: string[] = []\n\n switch (environment) {\n case 'xelatex':\n command = 'xelatex'\n args = ['-halt-on-error', path.basename(texFile)]\n break\n case 'tectonic':\n command = 'tectonic'\n args = [path.basename(texFile)]\n break\n }\n\n const cwd = outputDir\n ? path.resolve(outputDir)\n : path.dirname(path.resolve(texFile))\n\n return { command, args, cwd }\n}\n\n/**\n * Normalize the file extension that can be used in the output file name\n *\n * @param extension - file extension\n * @returns\n */\nexport function normalizeExtension(extension: string): string {\n switch (extension) {\n case '.tex':\n return 'tex'\n case '.md':\n return 'markdown'\n case '.html':\n return 'html'\n default:\n return extension.replace('.', '')\n }\n}\n\n/**\n * Shared helper to generate output file from a layout\n */\nfunction generateOutput(\n resumePath: string,\n resume: Resume,\n index: number,\n total: number,\n outputDir: string | undefined,\n extension: string,\n layoutIndex: number\n): string {\n const outputFile = getOutputPath(\n resumePath,\n extension,\n index,\n total,\n outputDir\n )\n\n const dir = path.dirname(outputFile)\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n\n const renderer = getResumeRenderer(resume, layoutIndex)\n const content = renderer.render()\n\n try {\n fs.writeFileSync(outputFile, content)\n consola.success(\n joinNonEmptyString(\n [\n `Generated resume ${normalizeExtension(extension)} file successfully:`,\n outputFile,\n ],\n ' '\n )\n )\n } catch (_error) {\n throw new YAMLResumeError('FILE_WRITE_ERROR', { path: outputFile })\n }\n\n return outputFile\n}\n\n/**\n * Compile a TeX file to PDF\n */\nasync function compileLaTeX(texFile: string, outputDir?: string) {\n const { command, args, cwd } = inferLaTeXCommand(texFile, outputDir)\n\n consola.start(\n `Generating resume pdf file with command: \\`${command} ${args.join(' ')}\\`...`\n )\n\n try {\n const result = await execa(command, args, {\n cwd,\n encoding: 'utf8',\n })\n consola.success(\n `Generated resume pdf file successfully: ${getPdfPath(texFile)}`\n )\n consola.debug(joinNonEmptyString(['stdout: ', toCodeBlock(result.stdout)]))\n } catch (error) {\n consola.debug(joinNonEmptyString(['stdout: ', toCodeBlock(error.stdout)]))\n consola.debug(joinNonEmptyString(['stderr: ', toCodeBlock(error.stderr)]))\n throw new YAMLResumeError('LATEX_COMPILE_ERROR', { error: error.message })\n }\n}\n\n/**\n * Build a YAML resume to LaTeX & PDF and/or Markdown\n *\n * It first validates the resume against the schema (unless `--no-validate` flag\n * is used), then iterates through configured layouts to generate outputs.\n *\n * @param resumePath - The source resume file path (YAML, YML, or JSON).\n * @param options - Build options including validation, PDF generation flags,\n * and output directory.\n */\nexport async function buildResume(\n resumePath: string,\n options: { pdf?: boolean; validate?: boolean; output?: string } = {\n pdf: true,\n validate: true,\n }\n) {\n const { resume } = readResume(resumePath, options.validate)\n\n // Fallback to default layout if none provided\n const allLayouts = resume.layouts ?? DEFAULT_RESUME_LAYOUTS\n // Ensure resume has layouts for the renderer to use\n if (!resume.layouts) {\n resume.layouts = allLayouts\n }\n\n // Count totals for each engine to determine file naming strategy\n // (e.g. resume.0.tex vs resume.tex)\n const totals = {\n latex: allLayouts.filter((l) => l.engine === 'latex').length,\n markdown: allLayouts.filter((l) => l.engine === 'markdown').length,\n html: allLayouts.filter((l) => l.engine === 'html').length,\n }\n\n // Track current index for each engine\n const indices = {\n latex: 0,\n markdown: 0,\n html: 0,\n }\n\n for (let layoutIndex = 0; layoutIndex < allLayouts.length; layoutIndex++) {\n const layout = allLayouts[layoutIndex]\n\n switch (layout.engine) {\n case 'latex': {\n const texFile = generateOutput(\n resumePath,\n resume,\n indices.latex++,\n totals.latex,\n options.output,\n '.tex',\n layoutIndex\n )\n\n if (options.pdf === true) {\n await compileLaTeX(texFile, options.output)\n }\n break\n }\n case 'markdown': {\n generateOutput(\n resumePath,\n resume,\n indices.markdown++,\n totals.markdown,\n options.output,\n '.md',\n layoutIndex\n )\n break\n }\n case 'html': {\n generateOutput(\n resumePath,\n resume,\n indices.html++,\n totals.html,\n options.output,\n '.html',\n layoutIndex\n )\n break\n }\n }\n }\n}\n\n/**\n * Create a command instance to build a YAML resume to LaTeX and PDF\n */\nexport function createBuildCommand() {\n return new Command()\n .name('build')\n .description('build a resume to LaTeX, PDF, Markdown, or HTML')\n .argument('<resume-path>', 'the resume file path')\n .option(\n '--no-pdf',\n 'only generate TeX file without PDF (for LaTeX layouts)'\n )\n .option('--no-validate', 'skip resume schema validation')\n .option('-o, --output <dir>', 'output directory for generated files')\n .action(\n async (\n resumePath: string,\n options: { pdf: boolean; validate: boolean; output?: string }\n ) => {\n try {\n await buildResume(resumePath, options)\n } catch (error) {\n consola.error(error.message)\n process.exit(error.errno)\n }\n }\n )\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport chokidar from 'chokidar'\n\nimport { coalesce } from 'coalescifn'\nimport { Command } from 'commander'\nimport { consola } from 'consola'\n\nimport { buildResume } from './build'\n\n/**\n * Options for the watchResume function\n *\n * @param pdf - Whether to generate PDF\n * @param validate - Whether to validate the resume\n */\ntype WatchOptions = {\n pdf?: boolean\n validate?: boolean\n output?: string\n}\n\n/**\n * Watch a resume source file and rebuild on changes.\n *\n * - Only one build runs at a time.\n * - If multiple events arrive during a build, run exactly one more build after\n * it finishes (coalesce bursts).\n * - Uses chokidar for robust file watching that handles editor operations.\n *\n * @param resumePath - The resume file to watch\n * @param options - Build and watch options\n * @returns Chokidar watcher instance\n */\nexport function watchResume(\n resumePath: string,\n options: WatchOptions = { pdf: true, validate: true }\n) {\n const { pdf, validate, output } = options\n\n // there should be only one build running at a time\n const exclusiveBuild = coalesce(() =>\n buildResume(resumePath, { pdf, validate, output })\n )\n\n // initial build\n exclusiveBuild()\n\n consola.start(`Watching file changes: ${resumePath}...`)\n\n // use chokidar for robust file watching that handles vim and other editors\n // properly.\n //\n // vim will save the file in a single atomic operation, that being said, it\n // will first create a temporary file (the '.swp' file), then rename it to the\n // final file.\n //\n // Node.js `fs.watch` has trouble with this, so we use chokidar instead.\n const watcher = chokidar.watch(resumePath, {\n awaitWriteFinish: {\n stabilityThreshold: 200, // wait 200ms after file stops changing\n pollInterval: 200, // check every 200ms\n },\n ignoreInitial: true, // don't trigger on initial file discovery\n })\n\n // handle file changes - chokidar's awaitWriteFinish already handles\n // debouncing\n watcher.on('change', () => exclusiveBuild())\n\n // handle file additions (in case file gets recreated)\n watcher.on('add', () => exclusiveBuild())\n\n return watcher\n}\n\n/**\n * Create a command instance to run in watch mode\n */\nexport function createDevCommand() {\n return new Command()\n .name('dev')\n .description('build a resume on file changes (watch mode)')\n .argument('<resume-path>', 'the resume file path')\n .option('--no-pdf', 'only generate TeX file without PDF')\n .option('--no-validate', 'skip resume schema validation')\n .option('-o, --output <dir>', 'output directory for generated files')\n .action(\n (\n resumePath: string,\n options: { pdf: boolean; validate: boolean; output?: string }\n ) => {\n watchResume(resumePath, options)\n }\n )\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n getLocaleLanguageDetail,\n LOCALE_LANGUAGE_OPTIONS,\n} from '@yamlresume/core'\nimport { Command } from 'commander'\nimport consola from 'consola'\nimport { markdownTable } from 'markdown-table'\n\n/**\n * Generates a markdown table listing all supported locale languages.\n *\n * The table includes columns for the language code and the language name.\n *\n * @returns A string containing the formatted markdown table.\n */\nexport function listLanguages() {\n return markdownTable([\n ['locale.language', 'Language Name'],\n ...LOCALE_LANGUAGE_OPTIONS.map((value) => [\n value,\n getLocaleLanguageDetail(value).name,\n ]),\n ])\n}\n\n/**\n * Create a command instance to list supported languages\n */\n\nexport function createLanguagesCommand() {\n const cmd = new Command()\n .name('languages')\n .description('i18n and l10n support')\n\n cmd\n .command('list')\n .description('list all supported languages')\n .action(() => {\n consola.log(listLanguages())\n })\n\n return cmd\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport {\n joinNonEmptyString,\n toCodeBlock,\n YAMLResumeError,\n} from '@yamlresume/core'\nimport { Command } from 'commander'\nimport consola from 'consola'\n\n/**\n * Creates a new resume file with the given filename\n *\n * @param filename - The name of the resume file to create\n * @throws {YAMLResumeError} When there are file-related errors:\n * - FILE_CONFLICT: When the file already exists\n * - FILE_READ_ERROR: When there's an error reading the template\n * - FILE_WRITE_ERROR: When there's an error writing the file\n */\nexport function newResume(filename: string) {\n if (fs.existsSync(filename)) {\n throw new YAMLResumeError('FILE_CONFLICT', { path: filename })\n }\n\n const templatePath = path.join(\n path.dirname(fileURLToPath(import.meta.url)),\n /* v8 ignore start */\n // I din't find a way to mock `import.meta.url` in tests so we have to\n // ignore the following lines for coverage calculation\n import.meta.url.includes('dist')\n ? '../resources/resume.yml'\n : '../../resources/resume.yml'\n /* v8 ignore stop */\n )\n\n let templateContent: string\n\n try {\n templateContent = fs.readFileSync(templatePath, 'utf8')\n } catch (error) {\n consola.debug(\n joinNonEmptyString(['Error reading template: ', toCodeBlock(error.stack)])\n )\n throw new YAMLResumeError('FILE_READ_ERROR', { path: templatePath })\n }\n\n try {\n fs.writeFileSync(filename, templateContent)\n consola.success(`Created ${filename} successfully.`)\n } catch (error) {\n consola.debug(\n joinNonEmptyString(['Error creating resume: ', toCodeBlock(error.stack)])\n )\n throw new YAMLResumeError('FILE_WRITE_ERROR', { path: filename })\n }\n}\n\n/**\n * Create a command instance to create a new YAML resume\n */\nexport function createNewCommand() {\n return new Command()\n .name('new')\n .description('create a new resume')\n .argument('[filename]', 'output filename', 'resume.yml')\n .action((filename) => {\n try {\n newResume(filename)\n } catch (error) {\n consola.error(error.message)\n process.exit(error.errno)\n }\n })\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n getHtmlTemplateDetail,\n getLatexTemplateDetail,\n HTML_TEMPLATE_OPTIONS,\n LATEX_TEMPLATE_OPTIONS,\n} from '@yamlresume/core'\nimport { Command } from 'commander'\nimport consola from 'consola'\nimport { markdownTable } from 'markdown-table'\n\n/**\n * Generates a markdown table listing all supported templates.\n *\n * The table includes columns for the template id, name and description.\n *\n * @returns A string containing the formatted markdown table.\n */\nexport function listTemplates() {\n const templates = [\n ...LATEX_TEMPLATE_OPTIONS.map((value) => {\n const details = getLatexTemplateDetail(value)\n return [value, details.engine, details.name, details.description]\n }),\n ...HTML_TEMPLATE_OPTIONS.map((value) => {\n const details = getHtmlTemplateDetail(value)\n return [value, details.engine, details.name, details.description]\n }),\n ]\n\n return markdownTable([\n ['layouts.[].template', 'Engine', 'Template Name', 'Description'],\n ...templates,\n ])\n}\n\n/**\n * Create a command instance to list supported templates\n */\nexport function createTemplatesCommand() {\n const cmd = new Command()\n .name('templates')\n .description('manage resume templates')\n\n cmd\n .command('list')\n .description('list all supported templates')\n .action(() => {\n consola.log(listTemplates())\n })\n\n return cmd\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport consola from 'consola'\n\n/**\n * Set the verbose mode for consola\n *\n * @param verbose - Whether to enable verbose mode\n * @see https://github.com/unjs/consola?tab=readme-ov-file#log-level\n */\nexport function setVerboseLog(verbose: boolean) {\n consola.level = verbose ? 4 : 3\n}\n","/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Command } from 'commander'\n\nimport packageJson from '../package.json' with { type: 'json' }\nimport {\n createBuildCommand,\n createDevCommand,\n createLanguagesCommand,\n createNewCommand,\n createTemplatesCommand,\n createValidateCommand,\n} from './commands'\nimport { setVerboseLog } from './utils'\n\n/**\n * Create the CLI program.\n *\n * @returns The CLI program.\n */\nexport function createProgram(): Command {\n const program = new Command()\n\n const banner = `\n __ __ _ __ __ _ ____\n \\\\ \\\\ / // \\\\ | \\\\/ | | | _ \\\\ ___ ___ _ _ ___ ___ ___\n \\\\ V // _ \\\\ | |\\\\/| | | | |_) / _ \\\\/ __| | | / _ \\\\/ _ \\\\ / _ \\\\\n | |/ ___ \\\\| | | | |___| _ < __/\\\\__ \\\\ |_| | | | | | | __/\n |_/_/ \\\\_\\\\_| |_|_____|_| \\\\_\\\\___||___/\\\\____|_| |_| |_|\\\\___|\n`\n\n program\n .name('yamlresume')\n .description(['YAMLResume — Resume as Code in YAML', banner].join('\\n'))\n .version(packageJson.version)\n .option('-v, --verbose', 'verbose output')\n .hook('preAction', (thisCommand) => {\n setVerboseLog(thisCommand.opts().verbose)\n })\n\n program.addCommand(createNewCommand())\n program.addCommand(createBuildCommand())\n program.addCommand(createDevCommand())\n program.addCommand(createLanguagesCommand())\n program.addCommand(createTemplatesCommand())\n program.addCommand(createValidateCommand())\n\n return program\n}\n","#!/usr/bin/env node\n/**\n * MIT License\n *\n * Copyright (c) 2023–Present PPResume (https://ppresume.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { createProgram } from './program'\n\ncreateProgram().parse()\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yamlresume",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "The CLI interface for YAMLResume's engine",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
"coalescifn": "^0.1.1",
|
|
35
35
|
"commander": "^14.0.1",
|
|
36
36
|
"consola": "^3.4.2",
|
|
37
|
-
"execa": "^9.6.
|
|
38
|
-
"extensionless": "^
|
|
37
|
+
"execa": "^9.6.1",
|
|
38
|
+
"extensionless": "^2.0.5",
|
|
39
39
|
"lodash-es": "^4.17.21",
|
|
40
40
|
"markdown-table": "^3.0.4",
|
|
41
41
|
"tslib": "^2.8.1",
|
|
42
42
|
"which": "^5.0.0",
|
|
43
|
-
"yaml": "^2.8.
|
|
44
|
-
"@yamlresume/core": "0.
|
|
43
|
+
"yaml": "^2.8.2",
|
|
44
|
+
"@yamlresume/core": "0.9.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/chalk": "^2.2.4",
|
package/resources/resume.yml
CHANGED
|
@@ -245,4 +245,9 @@ layouts:
|
|
|
245
245
|
# LaTeX engine only supports 10pt, 11pt, and 12pt
|
|
246
246
|
fontSize: 11pt
|
|
247
247
|
- engine: markdown
|
|
248
|
-
|
|
248
|
+
- engine: html
|
|
249
|
+
# Use `yamlresume templates list` to get the list of available templates
|
|
250
|
+
template: calm
|
|
251
|
+
typography:
|
|
252
|
+
# HTML engine only supports font size in px unit, from 10px to 24px
|
|
253
|
+
fontSize: 16px
|