diff --git a/.gitignore b/.gitignore index 241e833..a1a2711 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .idea/ .vscode/ -venv/ - -.DS_Store +build/ +dist/ +node_modules/ \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 5ca31c5..0000000 --- a/README.md +++ /dev/null @@ -1,68 +0,0 @@ -## Hi there! Waving Hand - -I'm **Lucàs**, a passionate developer based in 🇫🇷 **Pau, France**.
-My journey in the world of programming started 7 years ago when I fell in love with ![HTML5](https://img.shields.io/static/v1?message=HTML5&logo=html5&logoColor=white&label=+&color=E34F26) ![CSS3](https://img.shields.io/static/v1?message=CSS3&logo=css3&logoColor=white&label=+&color=1572B6) ![JavaScript](https://img.shields.io/static/v1?message=JavaScript&logo=javascript&logoColor=white&label=+&color=F7DF1E). - -### Rocket What I do - -- 💻 Currently, I'm studying at **UPPA** for my Master's degree in Informatics. -- 🌐 I specialize in Web Development, and I'm always eager to explore new technologies and frameworks. -- 🌱 I'm constantly learning and expanding my skill set to stay up-to-date with the ever-evolving tech landscape. - -### Globe Showing Europe-Africa Connect with me - -[![Portfolio](https://img.shields.io/static/v1?message=Portfolio&style=for-the-badge&logoColor=white&label=+&color=black&link=https%3A%2F%2Fwww.lucasvbr.dev)](https://www.lucasvbr.dev) -[![Linkedin](https://img.shields.io/static/v1?message=LinkedIn&style=for-the-badge&logo=linkedin&logoColor=white&label=+&color=0A66C2&link=https%3A%2F%2Fwww.linkedin.com%2Fin%2Flucasvbr)](https://www.linkedin.com/in/lucasvbr) -[![FreeCodeCamp](https://img.shields.io/static/v1?message=freeCodeCamp&style=for-the-badge&logo=freecodecamp&logoColor=white&label=+&color=0A0A23&link=https%3A%2F%2Fwww.freecodecamp.org%2FLucasVbr)](https://www.freecodecamp.org/LucasVbr) -[![OpenClassRooms](https://img.shields.io/static/v1?message=OpenClassRooms&style=for-the-badge&logoColor=white&label=+&color=black&link=https%3A%2F%2Fopenclassrooms.com%2Ffr%2Fmembers%2F97j9zltv6225)](https://openclassrooms.com/fr/members/97j9zltv6225) -[![Exercism](https://img.shields.io/static/v1?message=Exercism&style=for-the-badge&logo=exercism&logoColor=white&label=+&color=009CAB&link=https%3A%2F%2Fexercism.org%2Fprofiles%2FLucasVbr)](https://exercism.org/profiles/LucasVbr) - -### Hammer and Wrench Tech Stack - -![Android](https://img.shields.io/static/v1?message=Android&logo=android&logoColor=white&label=+&color=3DDC84) -![Angular](https://img.shields.io/static/v1?message=Angular&logo=angular&logoColor=white&label=+&color=DD0031) -![Astro](https://img.shields.io/static/v1?message=Astro&logo=astro&logoColor=white&label=+&color=FF5D01) -![Bootstrap](https://img.shields.io/static/v1?message=Bootstrap&logo=bootstrap&logoColor=white&label=+&color=7952B3) -![Bulma](https://img.shields.io/static/v1?message=Bulma&logo=bulma&logoColor=white&label=+&color=00D1B2) -![Bun](https://img.shields.io/static/v1?message=Bun&logo=bun&logoColor=white&label=+&color=000000) -![C](https://img.shields.io/static/v1?message=C&logo=c&logoColor=white&label=+&color=A8B9CC) -![CSS3](https://img.shields.io/static/v1?message=CSS3&logo=css3&logoColor=white&label=+&color=1572B6) -![Deno](https://img.shields.io/static/v1?message=Deno&logo=deno&logoColor=white&label=+&color=000000) -![Docker](https://img.shields.io/static/v1?message=Docker&logo=docker&logoColor=white&label=+&color=2496ED) -![Express](https://img.shields.io/static/v1?message=Express&logo=express&logoColor=white&label=+&color=000000) -![Figma](https://img.shields.io/static/v1?message=Figma&logo=figma&logoColor=white&label=+&color=F24E1E) -![GNU Bash](https://img.shields.io/static/v1?message=GNU_Bash&logo=gnubash&logoColor=white&label=+&color=4EAA25) -![Git](https://img.shields.io/static/v1?message=Git&logo=git&logoColor=white&label=+&color=F05032) -![HTML5](https://img.shields.io/static/v1?message=HTML5&logo=html5&logoColor=white&label=+&color=E34F26) -![JavaScript](https://img.shields.io/static/v1?message=JavaScript&logo=javascript&logoColor=white&label=+&color=F7DF1E) -![MariaDB](https://img.shields.io/static/v1?message=MariaDB&logo=mariadb&logoColor=white&label=+&color=003545) -![MongoDB](https://img.shields.io/static/v1?message=MongoDB&logo=mongodb&logoColor=white&label=+&color=47A248) -![MySQL](https://img.shields.io/static/v1?message=MySQL&logo=mysql&logoColor=white&label=+&color=4479A1) -![Node.js](https://img.shields.io/static/v1?message=Node.js&logo=nodedotjs&logoColor=white&label=+&color=339933) -![OCaml](https://img.shields.io/static/v1?message=OCaml&logo=ocaml&logoColor=white&label=+&color=EC6813) -![PHP](https://img.shields.io/static/v1?message=PHP&logo=php&logoColor=white&label=+&color=777BB4) -![PostgreSQL](https://img.shields.io/static/v1?message=PostgreSQL&logo=postgresql&logoColor=white&label=+&color=4169E1) -![Pug](https://img.shields.io/static/v1?message=Pug&logo=pug&logoColor=white&label=+&color=A86454) -![Python](https://img.shields.io/static/v1?message=Python&logo=python&logoColor=white&label=+&color=3776AB) -![React](https://img.shields.io/static/v1?message=React&logo=react&logoColor=white&label=+&color=61DAFB) -![SQLite](https://img.shields.io/static/v1?message=SQLite&logo=sqlite&logoColor=white&label=+&color=003B57) -![Symfony](https://img.shields.io/static/v1?message=Symfony&logo=symfony&logoColor=white&label=+&color=000000) -![TypeScript](https://img.shields.io/static/v1?message=TypeScript&logo=typescript&logoColor=white&label=+&color=3178C6) - -### Handshake Let's collaborate - -👀 I'm always open to collaboration and exciting projects. If you have something in mind, feel free to reach out! - ---- - - diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..512f85a --- /dev/null +++ b/bun.lock @@ -0,0 +1,50 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "lucasvbr", + "dependencies": { + "handlebars": "^4.7.8", + "yaml": "^2.8.0", + "zod": "^4.0.5", + }, + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5.0.0", + }, + }, + }, + "packages": { + "@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="], + + "@types/node": ["@types/node@24.0.13", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ=="], + + "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], + + "bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "handlebars": ["handlebars@4.7.8", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": { "handlebars": "bin/handlebars" } }, "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="], + + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + + "uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="], + + "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], + + "wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="], + + "yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="], + + "zod": ["zod@4.0.5", "", {}, "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA=="], + } +} diff --git a/config.json b/config.json new file mode 100644 index 0000000..98d65ad --- /dev/null +++ b/config.json @@ -0,0 +1,7 @@ +{ + "parser_type": "YAML", + "compiler_type": "HANDLEBARS", + "input_file": "src/templates/README.md.hbs", + "data_file": "static/README.yaml", + "output_file": "README.md" +} \ No newline at end of file diff --git a/config.yaml b/config.yaml deleted file mode 100644 index b38fab3..0000000 --- a/config.yaml +++ /dev/null @@ -1,48 +0,0 @@ -user: "LucasVbr" - -socials: - - name: "Portfolio" - url: "https://www.lucasvbr.dev" - - - name: "Linkedin" - url: "https://www.linkedin.com/in/lucasvbr" - - - name: "FreeCodeCamp" - url: "https://www.freecodecamp.org/LucasVbr" - - - name: "OpenClassRooms" - url: "https://openclassrooms.com/fr/members/97j9zltv6225" - - - name: "Exercism" - url: "https://exercism.org/profiles/LucasVbr" - -skills: - - Android - - Angular - - Astro - - Bun - - Bootstrap - - Bulma - - C - - CSS3 - - Deno - - Docker - - Express - - Figma - - Git - - GNU Bash - - HTML5 - - JavaScript - - MariaDB - - MongoDB - - MySQL - - Node.js - - OCaml - - PHP - - PostgreSQL - - Pug - - Python - - React - - SQLite - - Symfony - - TypeScript \ No newline at end of file diff --git a/github/workflows/deploy.yaml b/github/workflows/deploy.yaml new file mode 100644 index 0000000..58a8e66 --- /dev/null +++ b/github/workflows/deploy.yaml @@ -0,0 +1,43 @@ +name: Build and Commit README + +on: + push: + branches: [dev] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install Bun + uses: oven-sh/setup-bun@v1 + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Run build + run: bun run build + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Commit README.md to main + run: | + git checkout main + git pull origin main + git checkout dev -- README.md + if git diff --quiet README.md; then + echo "No changes to README.md" + else + git add README.md + git commit -m "chore(readme): update after build [CI]" + git push origin main + fi diff --git a/main.py b/main.py deleted file mode 100644 index c878ea5..0000000 --- a/main.py +++ /dev/null @@ -1,17 +0,0 @@ -from src.config import Config -from src.template import Template - -CONFIG_FILE = 'config.yaml' -TEMPLATE_FILE = 'template.md' -OUTPUT_FILE = 'README.md' - -if __name__ == '__main__': - template = Template(TEMPLATE_FILE) - data = Config(CONFIG_FILE).get_data() - - # Generate README file - render = template.render(**data) - with open(OUTPUT_FILE, 'w') as f: - f.write(render) - - print(f"{OUTPUT_FILE} generated successfully! 🎉") diff --git a/package.json b/package.json new file mode 100644 index 0000000..6663d8b --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "lucasvbr", + "module": "src/index.ts", + "type": "module", + "scripts": { + "build": "bun run src/index.ts" + }, + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "handlebars": "^4.7.8", + "yaml": "^2.8.0", + "zod": "^4.0.5" + } +} \ No newline at end of file diff --git a/requirements b/requirements deleted file mode 100644 index 98eb82d..0000000 --- a/requirements +++ /dev/null @@ -1,5 +0,0 @@ -pyyaml -json -requests -simpleicons -urllib3 \ No newline at end of file diff --git a/src/compilers/Compiler.ts b/src/compilers/Compiler.ts new file mode 100644 index 0000000..8618c9d --- /dev/null +++ b/src/compilers/Compiler.ts @@ -0,0 +1,12 @@ +/** + * Compiler interface for compiling templates with data. + */ +export interface Compiler { + + /** + * Compiles the given template with the provided data. + * @param template - The template string to compile. + * @param data - The data object to use for compilation. + */ + compile(template: string, data: object): string; +} \ No newline at end of file diff --git a/src/compilers/CompilerFactory.ts b/src/compilers/CompilerFactory.ts new file mode 100644 index 0000000..7c5d154 --- /dev/null +++ b/src/compilers/CompilerFactory.ts @@ -0,0 +1,22 @@ +import {type Compiler, CompilerType, HandlebarsCompiler} from '.'; + +/** + * Factory class for creating compiler instances. + * This class provides a method to get a compiler based on the specified type. + */ +export class CompilerFactory { + + /** + * Creates a compiler instance based on the specified type. + * @param type - The type of compiler to create. + * @returns An instance of the specified compiler. + */ + public static getCompiler(type: CompilerType): Compiler { + switch (type) { + case CompilerType.HANDLEBARS: + return HandlebarsCompiler.getInstance(); + default: + throw new Error(`Unsupported compiler type: ${type}`); + } + } +} \ No newline at end of file diff --git a/src/compilers/CompilerType.ts b/src/compilers/CompilerType.ts new file mode 100644 index 0000000..4d2d0a6 --- /dev/null +++ b/src/compilers/CompilerType.ts @@ -0,0 +1,6 @@ +/** + * Enum representing different types of compilers. + */ +export enum CompilerType { + HANDLEBARS = 'handlebars', +} \ No newline at end of file diff --git a/src/compilers/HandlebarsCompiler.ts b/src/compilers/HandlebarsCompiler.ts new file mode 100644 index 0000000..5a615f9 --- /dev/null +++ b/src/compilers/HandlebarsCompiler.ts @@ -0,0 +1,25 @@ +import {type Compiler, CompilerType} from '.'; +import {compile} from 'handlebars'; + +export class HandlebarsCompiler implements Compiler { + + static readonly TYPE: CompilerType = CompilerType.HANDLEBARS; + + private static instance: HandlebarsCompiler; + private constructor() {} + + public static getInstance(): HandlebarsCompiler { + if (!HandlebarsCompiler.instance) { + HandlebarsCompiler.instance = new HandlebarsCompiler(); + } + return HandlebarsCompiler.instance; + } + + /** + * @inheritdoc + */ + public compile(template: string, data: object = {}): string { + const compiledTemplate = compile(template); + return compiledTemplate(data); + } +} \ No newline at end of file diff --git a/src/compilers/index.ts b/src/compilers/index.ts new file mode 100644 index 0000000..17e20f0 --- /dev/null +++ b/src/compilers/index.ts @@ -0,0 +1,4 @@ +export * from './Compiler'; +export * from './CompilerType'; +export * from './CompilerFactory'; +export * from './HandlebarsCompiler'; diff --git a/src/config.py b/src/config.py deleted file mode 100644 index 71a72e1..0000000 --- a/src/config.py +++ /dev/null @@ -1,31 +0,0 @@ -import yaml -from src.model.skill_list import skill_list -import requests -from src.model.social_list import social_list - - -class Config: - config_file_path: str - config_data: dict[str, any] = None - - def __init__(self, config_file_path: str): - self.config_file_path = config_file_path - - def load_config_file(self): - with open(self.config_file_path, 'r') as config_file: - self.config_data = yaml.safe_load(config_file) - - def handle_user_info(self): - user = self.config_data["user"] - response = requests.get(f"https://api.github.com/users/{user}") - if response.status_code != 200: - raise Exception("User not found") - self.config_data["user"] = response.json() - - def get_data(self): - self.load_config_file() - self.handle_user_info() - self.config_data["skills"] = skill_list(self.config_data["skills"]) - self.config_data["socials"] = social_list(self.config_data["socials"]) - - return self.config_data diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..17cab9e --- /dev/null +++ b/src/index.ts @@ -0,0 +1,75 @@ +import config from '../config.json'; +import {CompilerFactory, CompilerType} from './compilers'; +import {ParserFactory, ParserType} from './parsers'; +import {Validator} from './validators/Validator.ts'; +import {FileManager} from './io/FileManager.ts'; +import {ReadmeSchema} from './validators/schemas'; + +/** + * Point d'entrée du script + */ +async function main(): Promise { + const compiler = createCompiler(config.compiler_type); + const parser = createParser(config.parser_type); + const validator = new Validator(ReadmeSchema); + + const data = await loadData(config.data_file, parser, validator); + const template = await loadTemplate(config.input_file); + const result = compiler.compile(template, data); + + await saveOutput(config.output_file, result); +} + +/** + * Crée un compilateur en fonction du type spécifié dans la configuration + * @param type - Le type de compilateur à créer + */ +function createCompiler(type: string) { + const compilerType = CompilerType[type as keyof typeof CompilerType]; + return CompilerFactory.getCompiler(compilerType); +} + +/** + * Crée un parseur en fonction du type spécifié dans la configuration + * @param type - Le type de parseur à créer + */ +function createParser(type: string) { + const parserType = ParserType[type as keyof typeof ParserType]; + return ParserFactory.getParser(parserType); +} + +/** + * Charge les données depuis le fichier spécifié, les parse et les valide + * @param path - Le chemin du fichier de données + * @param parser - Le parseur à utiliser pour analyser les données + * @param validator - Le validateur à utiliser pour valider les données + */ +async function loadData( + path: string, parser: ReturnType, + validator: Validator) { + const raw = await FileManager.read(path); + const parsed = parser.parse(raw); + return validator.validate(parsed); +} + +/** + * Charge le modèle de template depuis le fichier spécifié + * @param path - Le chemin du fichier de template + */ +async function loadTemplate(path: string): Promise { + return FileManager.read(path); +} + +/** + * Enregistre le résultat compilé dans le fichier de sortie spécifié + * @param path - Le chemin du fichier de sortie + * @param content - Le contenu à écrire dans le fichier + */ +async function saveOutput(path: string, content: string): Promise { + await FileManager.write(path, content); +} + +// Exécute le script principal +main() + .then(() => console.log('🎉 Exécution terminée avec succès.')) + .catch(err => console.error('❌ Erreur pendant l’exécution:', err)); diff --git a/src/io/FileManager.ts b/src/io/FileManager.ts new file mode 100644 index 0000000..0642542 --- /dev/null +++ b/src/io/FileManager.ts @@ -0,0 +1,33 @@ +import {readFileSync, writeFileSync} from 'node:fs'; + +/** + * FileManager is a singleton class that provides methods to read and write files. + * It ensures that only one instance of the FileManager exists throughout the application. + */ +export class FileManager { + + /** + * Reads the content of a file at the specified path. + * @param {string} filePath - The path to the file to read. + * @returns {Promise} A promise that resolves with the file content. + */ + public static async read(filePath: string): Promise { + return readFileSync(filePath, { + encoding: 'utf-8', + }); + } + + /** + * Writes content to a file at the specified path. + * If the file does not exist, it will be created. + * @param {string} filePath - The path to the file to write. + * @param {string} content - The content to write to the file. + * @returns {Promise} A promise that resolves when the write operation is complete. + */ + public static async write(filePath: string, content: string): Promise { + return writeFileSync(filePath, content, { + encoding: 'utf-8', + flag: 'w', + }); + } +} \ No newline at end of file diff --git a/src/model/skill.py b/src/model/skill.py deleted file mode 100644 index 093024b..0000000 --- a/src/model/skill.py +++ /dev/null @@ -1,13 +0,0 @@ -from src.shield.skill_shield import SkillShield - - -class Skill: - alt: str - src: str - - def __init__(self, name: str): - self.alt = name - self.src = SkillShield(name).__str__() - - def __str__(self) -> str: - return f"![{self.alt}]({self.src})" diff --git a/src/model/skill_list.py b/src/model/skill_list.py deleted file mode 100644 index ee746ad..0000000 --- a/src/model/skill_list.py +++ /dev/null @@ -1,11 +0,0 @@ -from src.model.skill import Skill - - -def skill_list(skills: list[str]) -> dict[str, str]: - # Sort and remove duplicates - skills = list(set(skills)) - skills.sort() - - skills = {skill: Skill(skill).__str__() for skill in skills} - skills["all"] = "\n".join([str(skill) for skill in skills.values()]) - return skills diff --git a/src/model/social.py b/src/model/social.py deleted file mode 100644 index a96ad54..0000000 --- a/src/model/social.py +++ /dev/null @@ -1,14 +0,0 @@ -from src.shield.social_shield import SocialShield - - -class Social: - name: str - img: str - - def __init__(self, name: str, url: str): - self.name = name - self.img = SocialShield(name, url).__str__() - self.url = url - - def __str__(self) -> str: - return f"[![{self.name}]({self.img})]({self.url})" diff --git a/src/model/social_list.py b/src/model/social_list.py deleted file mode 100644 index f3106ca..0000000 --- a/src/model/social_list.py +++ /dev/null @@ -1,8 +0,0 @@ -from src.model.social import Social - - -def social_list(socials: list) -> dict[str, str]: - socials: dict[str, str] = {social.get("name"): str(Social(social.get("name"), social.get("url"))) for social in - socials} - socials["all"] = "\n".join([str(social) for social in socials.values()]) - return socials diff --git a/src/parsers/Parser.ts b/src/parsers/Parser.ts new file mode 100644 index 0000000..51dcf6b --- /dev/null +++ b/src/parsers/Parser.ts @@ -0,0 +1,17 @@ +/** + * Parser interface for parsing data strings into object representations. + */ +export interface Parser { + /** + * Parses the given data string and returns an object representation. + * @param data - The data string to parse. + * @returns An object representation of the parsed data. + */ + parse(data: string): object; + + /** + * Returns an instance of the parser. + * @returns An instance of the parser. + */ + getInstance(): Parser; +} \ No newline at end of file diff --git a/src/parsers/ParserFactory.ts b/src/parsers/ParserFactory.ts new file mode 100644 index 0000000..3bf5f58 --- /dev/null +++ b/src/parsers/ParserFactory.ts @@ -0,0 +1,20 @@ +import {ParserType, type Parser, YamlParser} from "." + +/** + * Factory class for creating parser instances. + */ +export class ParserFactory { + + /** + * Creates and returns a parser instance based on the specified type. + * @param type + */ + static getParser(type: ParserType): Parser { + switch (type) { + case ParserType.YAML: + return YamlParser.getInstance(); + default: + throw new Error(`Unsupported parser type: ${type}`); + } + } +} \ No newline at end of file diff --git a/src/parsers/ParserType.ts b/src/parsers/ParserType.ts new file mode 100644 index 0000000..cc39a8a --- /dev/null +++ b/src/parsers/ParserType.ts @@ -0,0 +1,6 @@ +/** + * Enum representing different parser types. + */ +export enum ParserType { + YAML = 'yaml', +} \ No newline at end of file diff --git a/src/parsers/YamlParser.ts b/src/parsers/YamlParser.ts new file mode 100644 index 0000000..81de66e --- /dev/null +++ b/src/parsers/YamlParser.ts @@ -0,0 +1,49 @@ +import {type Parser, ParserType} from '.'; +import {parse} from 'yaml'; + +/** + * YamlParser class implements the Parser interface for parsing YAML data. + */ +export class YamlParser implements Parser { + + /** + * The type of the parser. + * This is used to identify the parser type in the system. + */ + static readonly TYPE: ParserType = ParserType.YAML; + + /** + * Singleton instance of the parser. + * This ensures that only one instance of the parser exists. + */ + static instance?: YamlParser; + + private constructor() { + } + + /** + * @inheritdoc + */ + public static getInstance(): YamlParser { + if (!this.instance) { + this.instance = new YamlParser(); + } + return this.instance; + } + + getInstance(): Parser { + throw new Error('Method not implemented.'); + } + + /** + * @inheritdoc + */ + public parse(data: string): object { + try { + return parse(data); + } catch (error) { + console.error('Error parsing YAML data:', error); + throw new Error('Failed to parse YAML data'); + } + } +} \ No newline at end of file diff --git a/src/parsers/index.ts b/src/parsers/index.ts new file mode 100644 index 0000000..af983d5 --- /dev/null +++ b/src/parsers/index.ts @@ -0,0 +1,4 @@ +export * from "./Parser"; +export * from "./ParserType"; +export * from "./ParserFactory"; +export * from "./YamlParser"; diff --git a/src/shield/shield_builder.py b/src/shield/shield_builder.py deleted file mode 100644 index b9a2977..0000000 --- a/src/shield/shield_builder.py +++ /dev/null @@ -1,94 +0,0 @@ -from urllib.parse import urlunsplit, urlencode -from simpleicons.all import icons - -class ShieldBuilder: - BASE_URL = "https://img.shields.io/static/v1" - - message: str = None - style: str = None - logo: str = None - logo_color: str = None - label: str = None - label_color: str = None - color: str = None - cache_seconds: int = None - link: str = None - - def __init__(self): - self.logo_color = "white" - self.label = " " - self.color = "black" - - def set_message(self, message: str): - self.message = ( - message - .replace("_", "__") - .replace("-", "--") - .replace(" ", "_") - ) - return self - - def set_style(self, style: str): - if not style in ["flat", "flat-square", "plastic", "for-the-badge", "social"]: - raise Exception("Invalid style") - - self.style = style - return self - - def set_logo(self, logo: str): - self.logo = icons.get(logo).slug if logo in icons else None - return self - - def set_logo_color(self, logo_color: str): - self.logo_color = logo_color - return self - - def set_label(self, label: str): - self.label = label - return self - - def set_label_color(self, label_color: str): - self.label_color = label_color - return self - - def set_color(self, color: str): - self.color = color - return self - - def set_cache_seconds(self, cache_seconds: int): - self.cache_seconds = cache_seconds - return self - - def set_link(self, link: str): - self.link = link - return self - - def get_query(self): - query = { - "message": self.message, - "style": self.style, - "logo": self.logo, - "logoColor": self.logo_color, - "label": self.label, - "labelColor": self.label_color, - "color": self.color, - "cacheSeconds": self.cache_seconds, - "link": self.link - } - - # Remove None values - return {k: v for k, v in query.items() if v is not None} - - def build(self): - query = urlencode(self.get_query()) - return urlunsplit(("", "", self.BASE_URL, query, "")) - - -if "__main__" == __name__: - shield_builder = ( - ShieldBuilder() - .set_logo("HTML5") - .set_message("HTML5") - .build() - ) - print(shield_builder) diff --git a/src/shield/skill_shield.py b/src/shield/skill_shield.py deleted file mode 100644 index 3853513..0000000 --- a/src/shield/skill_shield.py +++ /dev/null @@ -1,33 +0,0 @@ -from simpleicons.all import icons - -from src.shield.shield_builder import ShieldBuilder - - -class SkillShield: - - def __init__(self, name: str = None): - self.builder = ShieldBuilder() - self.skill = self.set_skill(name) if name is not None else None - - def get_skill(self): - return self.skill - - def set_skill(self, name: str): - self.skill = icons.get(name) - - if self.skill is not None: - (self.builder.set_message(self.skill.title) - .set_logo(self.skill.slug) - .set_color(self.skill.hex) - ) - else: - self.builder.set_message(name) - - def get_builder(self): - return self.builder - - def __repr__(self): - self.__str__() - - def __str__(self): - return self.builder.build() diff --git a/src/shield/social_shield.py b/src/shield/social_shield.py deleted file mode 100644 index d164572..0000000 --- a/src/shield/social_shield.py +++ /dev/null @@ -1,37 +0,0 @@ -from simpleicons.all import icons - -from src.shield.shield_builder import ShieldBuilder - - -class SocialShield: - - def __init__(self, name: str = None, url: str = None): - self.builder = ShieldBuilder().set_style("for-the-badge") - self.name = name - self.url = url - self.social = self.set_social(name, url) if name is not None and url is not None else None - - def get_social(self): - return self.social - - def set_social(self, name: str, url: str): - self.social = icons.get(name) - - if self.social is not None: - (self.builder.set_message(self.social.title) - .set_logo(self.social.slug) - .set_color(self.social.hex) - ) - else: - self.builder.set_message(name) - - self.builder.set_link(url) - - def get_builder(self): - return self.builder - - def __repr__(self): - self.__str__() - - def __str__(self): - return self.builder.build() diff --git a/src/template.py b/src/template.py deleted file mode 100644 index b5a91f9..0000000 --- a/src/template.py +++ /dev/null @@ -1,10 +0,0 @@ -class Template: - - def __init__(self, template_path: str): - self.template_path = template_path - - def render(self, **kwargs): - with open(self.template_path, 'r') as f: - template = f.read() - - return template.format(**kwargs) diff --git a/src/templates/README.md.hbs b/src/templates/README.md.hbs new file mode 100644 index 0000000..2c4c2af --- /dev/null +++ b/src/templates/README.md.hbs @@ -0,0 +1,37 @@ +## Hi there! 👋 + +I'm **{{author.first_name}}**, a passionate developer based in **{{author.location}}**. + +### 🚀 What I do + +- 💻 Currently, I'm studying at **{{author.company}}** for my Master's degree in Informatics. +- 🌐 I specialize in Web Development, and I'm always eager to explore new technologies and frameworks. +- 🌱 I'm constantly learning and expanding my skill set to stay up-to-date with the ever-evolving tech landscape. + +### 🌍 Connect with me + +{{#each socials as | social |}} +[![{{social.name}}]({{social.icon}})]({{social.url}}) +{{/each}} + +### 🛠️ Tech Stack + +{{#each skills as | skill |}} +![{{skill.name}}]({{skill.icon}}) +{{/each}} + +### 🤝 Let's collaborate + +👀 I'm always open to collaboration and exciting projects. If you have something in mind, feel free to reach out! + +--- + +
+ +![SVG Stats](https://github-stats-alpha.vercel.app/api?username=LucasVbr&cc=000&tc=fff&ic=fff&bc=000) + +![Profile Views](https://komarev.com/ghpvc/?username=lucasvbr&label=Profile%20views&color=0e75b6&style=flat) +![FreeCodeCamp Points](https://img.shields.io/freecodecamp/points/lucasvbr?label=FreeCodeCamp%20points) +![Made with love](https://img.shields.io/badge/-made%20with%20%E2%9D%A4%EF%B8%8F-red) + +
\ No newline at end of file diff --git a/src/validators/Validator.ts b/src/validators/Validator.ts new file mode 100644 index 0000000..c355814 --- /dev/null +++ b/src/validators/Validator.ts @@ -0,0 +1,43 @@ +import type {ZodObject} from 'zod'; + +/** + * Validator class for validating and parsing objects against a Zod schema. + */ +export class Validator { + + /** + * Creates an instance of the Validator. + * @param schema - The Zod schema to validate against. + */ + constructor(private schema: ZodObject) { + } + + /** + * Validates the given value against the schema. + * @param value - The object to validate. + * @returns true if the value is valid, false otherwise. + */ + public isValid(value: object): boolean { + try { + this.schema.parse(value); + return true; + } catch (error) { + console.error('Validation error:', error); + return false; + } + } + + /** + * Parses the given value against the schema. + * @param value - The object to parse. + * @returns The parsed object if valid, throws an error otherwise. + */ + public validate(value: object): object { + try { + return this.schema.parse(value); + } catch (error) { + console.error('Parsing error:', error); + throw new Error('Failed to parse value'); + } + } +} \ No newline at end of file diff --git a/src/validators/schemas/AuthorSchema.ts b/src/validators/schemas/AuthorSchema.ts new file mode 100644 index 0000000..35252bd --- /dev/null +++ b/src/validators/schemas/AuthorSchema.ts @@ -0,0 +1,7 @@ +import {z} from 'zod'; + +export const AuthorSchema = z.object({ + first_name: z.string(), + location: z.string(), + company: z.string(), +}); \ No newline at end of file diff --git a/src/validators/schemas/ReadmeSchema.ts b/src/validators/schemas/ReadmeSchema.ts new file mode 100644 index 0000000..10ca244 --- /dev/null +++ b/src/validators/schemas/ReadmeSchema.ts @@ -0,0 +1,8 @@ +import {z} from 'zod'; +import {AuthorSchema, SkillSchema, SocialSchema} from '.'; + +export const ReadmeSchema = z.object({ + author: AuthorSchema, + socials: z.array(SocialSchema), + skills: z.array(SkillSchema), +}); \ No newline at end of file diff --git a/src/validators/schemas/SkillSchema.ts b/src/validators/schemas/SkillSchema.ts new file mode 100644 index 0000000..c8c258c --- /dev/null +++ b/src/validators/schemas/SkillSchema.ts @@ -0,0 +1,6 @@ +import {z} from 'zod'; + +export const SkillSchema = z.object({ + name: z.string(), + icon: z.string(), +}); \ No newline at end of file diff --git a/src/validators/schemas/SocialSchema.ts b/src/validators/schemas/SocialSchema.ts new file mode 100644 index 0000000..2a141d8 --- /dev/null +++ b/src/validators/schemas/SocialSchema.ts @@ -0,0 +1,7 @@ +import {z} from 'zod'; + +export const SocialSchema = z.object({ + name: z.string(), + icon: z.string(), + url: z.string(), +}); \ No newline at end of file diff --git a/src/validators/schemas/index.ts b/src/validators/schemas/index.ts new file mode 100644 index 0000000..31d0df5 --- /dev/null +++ b/src/validators/schemas/index.ts @@ -0,0 +1,4 @@ +export * from './AuthorSchema'; +export * from './SocialSchema'; +export * from './SkillSchema'; +export * from './ReadmeSchema'; \ No newline at end of file diff --git a/static/README.yaml b/static/README.yaml new file mode 100644 index 0000000..1b52f09 --- /dev/null +++ b/static/README.yaml @@ -0,0 +1,70 @@ +author: + first_name: Lucàs + location: 🇫🇷 Toulouse, France + company: UPPA +socials: + - name: Portfolio + icon: "" + url: https://www.lucasvbr.dev + - name: LinkedIn + icon: "" + url: https://www.linkedin.com/in/lucasvbr +skills: + - name: Android + icon: "" + - name: Angular + icon: "" + - name: Astro + icon: "" + - name: Bun + icon: "" + - name: Bootstrap + icon: "" + - name: Bulma + icon: "" + - name: C + icon: "" + - name: CSS3 + icon: "" + - name: Deno + icon: "" + - name: Docker + icon: "" + - name: Express + icon: "" + - name: Figma + icon: "" + - name: Git + icon: "" + - name: GNU Bash + icon: "" + - name: HTML5 + icon: "" + - name: JavaScript + icon: "" + - name: MariaDB + icon: "" + - name: MongoDB + icon: "" + - name: MySQL + icon: "" + - name: Node.js + icon: "" + - name: OCaml + icon: "" + - name: PHP + icon: "" + - name: PostgreSQL + icon: "" + - name: Pug + icon: "" + - name: Python + icon: "" + - name: React + icon: "" + - name: SQLite + icon: "" + - name: Symfony + icon: "" + - name: TypeScript + icon: "" \ No newline at end of file diff --git a/template.md b/template.md deleted file mode 100644 index 181c03b..0000000 --- a/template.md +++ /dev/null @@ -1,36 +0,0 @@ -## Hi there! Waving Hand - -I'm **{user[name]}**, a passionate developer based in 🇫🇷 **{user[location]}**.
-My journey in the world of programming started 7 years ago when I fell in love with {skills[HTML5]} {skills[CSS3]} {skills[JavaScript]}. - -### Rocket What I do - -- 💻 Currently, I'm studying at **{user[company]}** for my Master's degree in Informatics. -- 🌐 I specialize in Web Development, and I'm always eager to explore new technologies and frameworks. -- 🌱 I'm constantly learning and expanding my skill set to stay up-to-date with the ever-evolving tech landscape. - -### Globe Showing Europe-Africa Connect with me - -{socials[all]} - -### Hammer and Wrench Tech Stack - -{skills[all]} - -### Handshake Let's collaborate - -👀 I'm always open to collaboration and exciting projects. If you have something in mind, feel free to reach out! - ---- - - diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..238655f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +}