diff --git a/.eslintrc.js b/.eslintrc.js index 6ce0b65..00e5705 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,8 +1,16 @@ module.exports = { - extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + extends: ["eslint:recommended", "plugin:@typescript-eslint/strict-type-checked"], parser: "@typescript-eslint/parser", plugins: ["@typescript-eslint"], env: { node: true, }, + parserOptions: { + project: true, + tsconfigRootDir: __dirname, + }, + ignorePatterns: ['.eslintrc.js'], + rules: { + "@typescript-eslint/switch-exhaustiveness-check": "error" + } }; diff --git a/CV/snapshot.html b/CV/snapshot.html index ad6ccb5..c2713a8 100644 --- a/CV/snapshot.html +++ b/CV/snapshot.html @@ -1297,7 +1297,7 @@ tuenti-300x300.png
Tuenti Challenge 8: 2nd place - + Achievement
@@ -1338,7 +1338,7 @@ Highlight
Other highlight - + Achievement
diff --git a/action/index.ts b/action/index.ts index 2a202f6..7c568d6 100644 --- a/action/index.ts +++ b/action/index.ts @@ -8,15 +8,15 @@ async function main() { const outputPath = process.argv[3]; const rawCv = await fs.readFile(path.resolve(process.cwd(), macPath)); - const cv: ManfredAwesomicCV = JSON.parse(rawCv.toString()); + const cv: unknown = JSON.parse(rawCv.toString()); // Generate the HTML - const html = await generateHtml(cv); + const html = await generateHtml(cv as ManfredAwesomicCV); // Generate a PDF with the HTML const pdf = await (generatePdf( { content: html }, - { format: "A4", scale: 0.6, printBackground: true } + { format: "A4", scale: 0.6, printBackground: true }, ) as unknown as Promise); await fs.mkdir(path.resolve(process.cwd(), outputPath), { recursive: true }); @@ -24,4 +24,7 @@ async function main() { await fs.writeFile(path.resolve(process.cwd(), outputPath, "cv.pdf"), pdf); } -main(); +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/action/package-lock.json b/action/package-lock.json index 5b96d3c..f938844 100644 --- a/action/package-lock.json +++ b/action/package-lock.json @@ -10,34 +10,35 @@ }, "devDependencies": { "@types/html-pdf-node": "1.0.0", - "ts-node": "^10.9.1" + "ts-node": "10.9.1" } }, "..": { "version": "1.0.2", "license": "MIT", "dependencies": { - "commander": "10.0.0", + "commander": "11.1.0", "escape-html": "1.0.3", "jsonschema": "1.4.1", - "marked": "4.2.12" + "lodash": "4.17.21", + "marked": "9.1.2" }, "bin": { "mac-renderer": "build/cli.js" }, "devDependencies": { - "@types/marked": "4.0.8", - "@typescript-eslint/eslint-plugin": "5.55.0", - "@typescript-eslint/parser": "5.55.0", + "@types/marked": "6.0.0", + "@typescript-eslint/eslint-plugin": "6.9.0", + "@typescript-eslint/parser": "6.9.0", "copyfiles": "2.4.1", - "eslint": "8.36.0", - "json-schema-to-typescript": "12.0.0", - "prettier": "2.8.4", - "prettier-plugin-organize-imports": "3.2.2", - "replace-in-file": "6.3.5", - "rimraf": "4.4.0", - "sass": "1.59.3", - "typescript": "5.0.2" + "eslint": "8.52.0", + "json-schema-to-typescript": "13.1.1", + "prettier": "3.0.3", + "prettier-plugin-organize-imports": "3.2.3", + "replace-in-file": "7.0.1", + "rimraf": "5.0.5", + "sass": "1.69.4", + "typescript": "5.2.2" } }, "node_modules/@cspotcode/source-map-support": { diff --git a/cli.ts b/cli.ts index 9f0489c..b61a0a4 100644 --- a/cli.ts +++ b/cli.ts @@ -2,9 +2,10 @@ import { Command } from "commander"; import fs from "fs"; -import { Validator } from "jsonschema"; +import { Schema, Validator } from "jsonschema"; import path from "path"; import { generateHtml } from "."; +import { ManfredAwesomicCV } from "./lib/mac"; const program = new Command(); @@ -12,15 +13,15 @@ program .name("mac-renderer") .description("Convert a MAC JSON to HTML (stdout)") .argument("", "MAC JSON file") - .action(async (macFile) => { - const schema = JSON.parse(fs.readFileSync(path.resolve(__dirname, "mac-schema.json"), "utf8")); + .action(async (macFile: string) => { + const schema: unknown = JSON.parse(fs.readFileSync(path.resolve(__dirname, "mac-schema.json"), "utf8")); const input = fs.readFileSync(path.resolve(process.cwd(), macFile), "utf8"); - const mac = JSON.parse(input); + const mac: unknown = JSON.parse(input); - new Validator().validate(mac, schema, { throwError: true }); + new Validator().validate(mac, schema as Schema, { throwError: true }); - const html = await generateHtml(mac); + const html = await generateHtml(mac as ManfredAwesomicCV); console.log(html); }); diff --git a/lib/generators/common/skillsGenerator.ts b/lib/generators/common/skillsGenerator.ts index e500f4c..3b8cd9e 100644 --- a/lib/generators/common/skillsGenerator.ts +++ b/lib/generators/common/skillsGenerator.ts @@ -1,6 +1,6 @@ import { generatorFrom } from "../../utils"; -export const generateSkills = generatorFrom(async function* (skills: string[]) { +export const generateSkills = generatorFrom(function* (skills: string[]) { yield `
`; diff --git a/lib/generators/common/typeLabelGenerator.ts b/lib/generators/common/typeLabelGenerator.ts index df2d4ee..10dbc17 100644 --- a/lib/generators/common/typeLabelGenerator.ts +++ b/lib/generators/common/typeLabelGenerator.ts @@ -1,19 +1,52 @@ +import { ProjectType, PublicArtifactType, StudyType } from "../../mac"; import { generatorFrom } from "../../utils"; -export const generateTypeLabels = generatorFrom(async function* (type: string | undefined) { - if (type == "proBono") { - yield `Pro bono`; - } else if (type == "openSource") { - yield `Open source`; - } else if (type == "sideProject") { - yield `Side project`; - } else if (type == "personalAchievement") { - yield `Achievement`; - } else if (type == "officialDegree") { - yield `Official Degree`; - } else if (type == "certification") { - yield `Certification`; - } else if (type == "selfTraining") { - yield `Self Training`; +export const generateTypeLabels = generatorFrom(function* ( + type: StudyType | ProjectType | PublicArtifactType | undefined, +) { + switch (type) { + case "proBono": + yield `Pro bono`; + break; + case "openSource": + yield `Open source`; + break; + case "sideProject": + yield `Side project`; + break; + case "personalAchievement": + yield `Achievement`; + break; + case "officialDegree": + yield `Official Degree`; + break; + case "certification": + yield `Certification`; + break; + case "selfTraining": + yield `Self Training`; + break; + case "unaccredited": + yield `Unaccredited`; + break; + case "post": + yield `Post`; + break; + case "talk": + yield `Talk`; + break; + case "achievement": + yield `Achievement`; + break; + case "launch": + yield `Launch`; + break; + case "video": + yield `Video`; + break; + case "other": + yield `Other`; + break; + case undefined: } }); diff --git a/lib/generators/leftColumn/languageGroupGenerator.ts b/lib/generators/leftColumn/languageGroupGenerator.ts index 9f069c2..2a0b182 100644 --- a/lib/generators/leftColumn/languageGroupGenerator.ts +++ b/lib/generators/leftColumn/languageGroupGenerator.ts @@ -1,7 +1,7 @@ import { Language } from "../../mac"; import { generatorFrom } from "../../utils"; -export const generateLanguageGroup = generatorFrom(async function* ( +export const generateLanguageGroup = generatorFrom(function* ( level: string | undefined, languages: Language[] ) { diff --git a/lib/generators/leftColumnGenerator.ts b/lib/generators/leftColumnGenerator.ts index 4ae46d2..86a39b8 100644 --- a/lib/generators/leftColumnGenerator.ts +++ b/lib/generators/leftColumnGenerator.ts @@ -44,8 +44,7 @@ export const generateLeftColumn = generatorFrom(async function* (mac: ManfredAwe // First, the public contact links const contact = mac.careerPreferences?.contact; - const publicProfileLinks = - (contact && "publicProfiles" in contact && contact?.publicProfiles) || []; + const publicProfileLinks = contact && "publicProfiles" in contact ? contact.publicProfiles : []; if (publicProfileLinks.length) { for (const link of publicProfileLinks) { @@ -56,8 +55,9 @@ export const generateLeftColumn = generatorFrom(async function* (mac: ManfredAwe // Filter relevant links that aren't contact ones const publicProfileUrls = publicProfileLinks.map((link) => link.URL); - const nonContactLinks = - mac.aboutMe.relevantLinks?.filter((link) => !publicProfileUrls.includes(link.URL)) ?? []; + const nonContactLinks = mac.aboutMe.relevantLinks.filter( + (link) => !publicProfileUrls.includes(link.URL), + ); // Second, the known links const knownLinks = nonContactLinks.filter((link) => link.type !== "other"); diff --git a/lib/generators/rightColumn/highlightsGenerator.ts b/lib/generators/rightColumn/highlightsGenerator.ts index 237a14e..e6e13dd 100644 --- a/lib/generators/rightColumn/highlightsGenerator.ts +++ b/lib/generators/rightColumn/highlightsGenerator.ts @@ -15,16 +15,16 @@ export const generateHighlights = generatorFrom(async function* (highlights: Hig let imageUrl = await assets.highlightDefaultIcon; let imageAlt = "Highlight"; - if (details?.image && "link" in details.image) { + if (details.image && "link" in details.image) { imageUrl = details.image.link; } - if (details?.image && "alt" in details.image) { - imageAlt = details.image.alt as string; + if (details.image && details.image.alt) { + imageAlt = details.image.alt; } let urlText = ""; - if (details?.URL) { + if (details.URL) { urlText = `${decodeURI(details.URL.replace(/https?:\/\//, ""))}`; } @@ -33,19 +33,19 @@ export const generateHighlights = generatorFrom(async function* (highlights: Hig
${imageAlt}
- ${details?.name} + ${details.name} ${await generateTypeLabels(highlight.type)}
${ - urlText || details?.description + urlText || details.description ? `
${ urlText ? ` Link ${urlText} @@ -54,7 +54,7 @@ export const generateHighlights = generatorFrom(async function* (highlights: Hig : "" } ${ - details?.description + details.description ? `
${marked(details.description)} diff --git a/lib/generators/rightColumn/jobsGenerator.ts b/lib/generators/rightColumn/jobsGenerator.ts index cec37b0..6b4bbe7 100644 --- a/lib/generators/rightColumn/jobsGenerator.ts +++ b/lib/generators/rightColumn/jobsGenerator.ts @@ -18,10 +18,12 @@ export const generateJobs = generatorFrom(async function* (jobs: Job[]) {
`; - if (organization.image && "link" in organization.image) { + const organizationImage = organization.image; + + if (organizationImage && "link" in organizationImage) { yield `${(`; + organizationImage.link + }" alt="${organizationImage.alt || ""}"/>`; } else { yield `Job`; } diff --git a/lib/generators/rightColumn/sectionGenerator.ts b/lib/generators/rightColumn/sectionGenerator.ts index 9b4535c..1da7e35 100644 --- a/lib/generators/rightColumn/sectionGenerator.ts +++ b/lib/generators/rightColumn/sectionGenerator.ts @@ -1,6 +1,6 @@ import { generatorFrom } from "../../utils"; -export const generateSection = generatorFrom(async function* (title: string, content: string) { +export const generateSection = generatorFrom(function* (title: string, content: string) { yield `
diff --git a/lib/generators/rightColumnGenerator.ts b/lib/generators/rightColumnGenerator.ts index d5e87cf..8ca4afb 100644 --- a/lib/generators/rightColumnGenerator.ts +++ b/lib/generators/rightColumnGenerator.ts @@ -14,7 +14,7 @@ export const generateRightColumn = generatorFrom(async function* (mac: ManfredAw "About me", `
- ${await marked(mac.aboutMe.profile.description)} + ${marked(mac.aboutMe.profile.description)}
` ); diff --git a/lib/types.d.ts b/lib/types.d.ts new file mode 100644 index 0000000..c085b3a --- /dev/null +++ b/lib/types.d.ts @@ -0,0 +1,3 @@ +declare module "html-pdf-node" { + export function generatePdf(file: { content: string }, options?: Options): Promise; +} diff --git a/lib/utils.ts b/lib/utils.ts index 23f14f1..948ab6d 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -2,7 +2,7 @@ import { isEqual, isNil } from "lodash"; import { Highlight, Job, Project, Study } from "./mac"; export function generatorFrom( - generator: (...args: T) => AsyncGenerator + generator: (...args: T) => AsyncGenerator | Generator ): (...args: T) => Promise { return async (...args: T) => { const parts = []; diff --git a/package.json b/package.json index 543f67d..84309f3 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "build": "rimraf ./build && npm run generate && tsc -p tsconfig.build.json && npm run copy-files", "copy-files": "copyfiles ./{generated,images}/** ./build && copyfiles ./mac-schema.json ./build", "generate": "rimraf ./generated && npm run generate-schema && npm run generate-sass", - "generate-schema": "json2ts mac-schema.json generated/mac.d.ts --no-additionalProperties && replace-in-file \"/\\[k: string\\]: unknown;/g\" \"\" generated/mac.d.ts --isRegex", + "generate-schema": "json2ts mac-schema.json generated/mac.d.ts --no-additionalProperties && npm run generate-schema-fixes", + "generate-schema-fixes": "replace-in-file \"/\\[k: string\\]: unknown;/g\" \"\" generated/mac.d.ts --isRegex && replace-in-file \": Image1\" \": Image\" generated/mac.d.ts", "generate-sass": "sass --style=compressed --no-source-map ./sass:./generated/styles && copyfiles --flat ./generated/styles/styles.css ./generated && rimraf ./generated/styles", "format": "prettier ./**/* --write", "lint": "eslint . --ext .ts,.js", diff --git a/tsconfig.json b/tsconfig.json index 427f7fe..61cabf9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,6 +9,6 @@ "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, - "skipLibCheck": true + "skipLibCheck": true, } }