diff --git a/.gitignore b/.gitignore
index 2fa6cce..d380386 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,6 @@ venv/*
venv/**/*
**/.DS_Store
+
+build/
+node_modules/
diff --git a/README.md b/README.md
index 8431b84..0b2e693 100644
--- a/README.md
+++ b/README.md
@@ -1,46 +1,52 @@
-
-
# Hi, I'm Lucà s! 👋
## 🚀 About Me
-I'm a passionate developer from 🇫🇷 **Albi, France**.
+I'm a passionate developer from 🇫🇷 **Albi, France**.
## 🔗 Links
-[](https://lucasvbr.github.io/links/?portfolio)
-[](https://www.linkedin.com/in/lucasvbr)
-[](https://www.freecodecamp.org/LucasVbr)
-[](https://openclassrooms.com/fr/members/97j9zltv6225)
-[](https://exercism.org/profiles/LucasVbr)
+[](https://lucasvbr.github.io/links/?portfolio)
+[](https://www.linkedin.com/in/lucasvbr)
+[](https://www.freecodecamp.org/LucasVbr)
+[](https://openclassrooms.com/fr/members/97j9zltv6225)
+[](https://exercism.org/profiles/LucasVbr)
## 🛠Skills
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
---
-
+


+
\ No newline at end of file
diff --git a/compile.sh b/compile.sh
deleted file mode 100644
index 2c5e4c4..0000000
--- a/compile.sh
+++ /dev/null
@@ -1 +0,0 @@
-deno run --allow-write --allow-read src/core.ts
\ No newline at end of file
diff --git a/model/data.json b/model/data.json
deleted file mode 100644
index 8abc87d..0000000
--- a/model/data.json
+++ /dev/null
@@ -1,134 +0,0 @@
-{
- "name": "Lucà s",
- "about": "I'm a passionate developer from 🇫🇷 **Albi, France**.",
- "links": [
- {
- "href": "https://lucasvbr.github.io/links/?portfolio",
- "img": {
- "src": "https://img.shields.io/badge/my_portfolio-000?style=for-the-badge&logo=ko-fi&logoColor=white",
- "alt": "portfolio"
- }
- },
- {
- "href": "https://www.linkedin.com/in/lucasvbr",
- "img": {
- "src": "https://img.shields.io/badge/linkedin-0e76a8?style=for-the-badge&logo=linkedin&logoColor=white",
- "alt": "linkedin"
- }
- },
- {
- "href": "https://www.freecodecamp.org/LucasVbr",
- "img": {
- "src": "https://img.shields.io/badge/freecodecamp-0a0a23?style=for-the-badge&logo=freecodecamp&logoColor=white",
- "alt": "freecodecamp"
- }
- },
- {
- "href": "https://openclassrooms.com/fr/members/97j9zltv6225",
- "img": {
- "src": "https://img.shields.io/badge/openclassrooms-7451eb?style=for-the-badge&logo=openclassrooms&logoColor=white",
- "alt": "openclassrooms"
- }
- },
- {
- "href": "https://exercism.org/profiles/LucasVbr",
- "img": {
- "src": "https://img.shields.io/badge/exercism-2e57e8?style=for-the-badge&logo=exercism&logoColor=white",
- "alt": "exercism"
- }
- }
-
- ],
- "skills": [
- {
- "src": "https://img.shields.io/static/v1?label=&message=Angular&color=%23df204a&logo=angular&logoColor=white",
- "alt": "Angular"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=Symfony&color=%23df2020&logo=symfony&logoColor=white",
- "alt": "Symfony"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=Git&color=%23df3e20&logo=git&logoColor=white",
- "alt": "Git"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=HTML5&color=%23df4920&logo=html5&logoColor=white",
- "alt": "HTML5"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=Figma&color=%23df4b20&logo=figma&logoColor=white",
- "alt": "Figma"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=JavaScript&color=%23dfca20&logo=javascript&logoColor=white",
- "alt": "JavaScript"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=GNU+Bash&color=%235bdf20&logo=gnubash&logoColor=white",
- "alt": "Bash"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=Android&color=%2320df75&logo=android&logoColor=white",
- "alt": "Android"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=Bulma&color=%2320dfc3&logo=bulma&logoColor=white",
- "alt": "Bulma"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=MariaDB&color=%2320b3df&logo=mariadb&logoColor=white",
- "alt": "MariaDB"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=Java&color=%2320b2df&logo=java&logoColor=white",
- "alt": "Java"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=SQLite&color=%2320a2df&logo=sqlite&logoColor=white",
- "alt": "SQLite"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=CSS3&color=%23208edf&logo=css3&logoColor=white",
- "alt": "CSS3"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=MySQL&color=%23208ddf&logo=mysql&logoColor=white",
- "alt": "MySQL"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=Python&color=%232088df&logo=python&logoColor=white",
- "alt": "Python"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=C&color=%23207adf&logo=c&logoColor=white",
- "alt": "C"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=PostgreSQL&color=%232050df&logo=postgresql&logoColor=white",
- "alt": "PostgreSQL"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=PHP&color=%23202cdf&logo=php&logoColor=white",
- "alt": "PHP"
- },
- {
- "src": "https://img.shields.io/static/v1?label=&message=Bootstrap&color=%236d20df&logo=bootstrap&logoColor=white",
- "alt": "Bootstrap"
- }
- ],
- "badges": [
- {
- "src": "https://komarev.com/ghpvc/?username=lucasvbr&label=Profile%20views&color=0e75b6&style=flat",
- "alt": "Profile views"
- },
- {
- "src": "https://img.shields.io/freecodecamp/points/lucasvbr?label=FreeCodeCamp%20points",
- "alt": "FreeCodeCamp Points"
- },
- {
- "src": "https://img.shields.io/badge/-made%20with%20%E2%9D%A4%EF%B8%8F-red",
- "alt": "Made with love"
- }
- ]
-}
\ No newline at end of file
diff --git a/model/macro.njk b/model/macro.njk
deleted file mode 100644
index 63a3944..0000000
--- a/model/macro.njk
+++ /dev/null
@@ -1,8 +0,0 @@
-{% macro a(src, alt) -%}
- [{{ alt }}]({{ src }})
-{%- endmacro %}
-
-{% macro img(src, alt) -%}
- !{{ a(src, alt) }}
-{%- endmacro %}
-
diff --git a/model/template.njk b/model/template.njk
deleted file mode 100644
index f9ea485..0000000
--- a/model/template.njk
+++ /dev/null
@@ -1,25 +0,0 @@
-{% from "model/macro.njk" import a, img with context %}
-
-# Hi, I'm {{ name }}! 👋
-
-## 🚀 About Me
-{{ about }}
-
-## 🔗 Links
-{% for link in links -%}
- {{ a(link.href, img(link.img.src, link.img.alt)) }}
-{% endfor %}
-
-## 🛠Skills
-{% for skill in skills -%}
- {{ img(skill.src, skill.alt) }}
-{% endfor %}
-
----
-
-
-
-{% for badge in badges -%}
- {{ img(badge.src, badge.alt) }}
-{% endfor %}
-
\ No newline at end of file
diff --git a/src/config.json b/src/config.json
deleted file mode 100644
index 8de3499..0000000
--- a/src/config.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "template": "model/template.njk",
- "data": "model/data.json",
- "output": "README.md"
-}
\ No newline at end of file
diff --git a/src/core.ts b/src/core.ts
deleted file mode 100644
index fc54c19..0000000
--- a/src/core.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import denjucks from "https://deno.land/x/denjucks/mod.js";
-
-let { template, data, output } = JSON.parse(await Deno.readTextFile("src/config.json"));
-
-template = await Deno.readTextFile(template);
-data = JSON.parse(await Deno.readTextFile(data));
-
-const result = denjucks.renderString(template, data);
-await Deno.writeTextFile(output, result);
\ No newline at end of file
diff --git a/src/core/FooterBadge.ts b/src/core/FooterBadge.ts
new file mode 100644
index 0000000..54658f5
--- /dev/null
+++ b/src/core/FooterBadge.ts
@@ -0,0 +1,6 @@
+type FooterBadge = {
+ title: string
+ shield: string
+}
+
+export default FooterBadge;
\ No newline at end of file
diff --git a/src/core/Link.ts b/src/core/Link.ts
new file mode 100644
index 0000000..b436dae
--- /dev/null
+++ b/src/core/Link.ts
@@ -0,0 +1,7 @@
+type Link = {
+ title: string
+ shield: string
+ url: string
+}
+
+export default Link;
\ No newline at end of file
diff --git a/src/core/Log.ts b/src/core/Log.ts
new file mode 100644
index 0000000..eb23d45
--- /dev/null
+++ b/src/core/Log.ts
@@ -0,0 +1,32 @@
+class Log {
+ colors = {
+ red: '\x1b[31m',
+ green: '\x1b[32m',
+ yellow: '\x1b[33m',
+ };
+
+ private readonly logFunction: (message?: any, ...optionalParams: any[]) => void;
+
+ constructor(logFunction: (message?: any, ...optionalParams: any[]) => void) {
+ this.logFunction = logFunction;
+ }
+
+ info(message: string): void {
+ this.logFunction(message);
+ }
+
+ success(message: string): void {
+ this.logFunction(this.colors.green, message);
+ }
+
+ error(message: string): void {
+ this.logFunction(this.colors.red, message);
+ }
+
+ warning(message: string): void {
+ this.logFunction(this.colors.yellow, message);
+ }
+}
+
+const log = new Log(console.log);
+export default log;
diff --git a/src/core/Skill.ts b/src/core/Skill.ts
new file mode 100644
index 0000000..9b9b02d
--- /dev/null
+++ b/src/core/Skill.ts
@@ -0,0 +1,38 @@
+import {SimpleIcon} from 'simple-icons/types';
+
+export default class Skill {
+ private title: string;
+ private slug: string;
+ private shield: string;
+
+ constructor(icon: SimpleIcon) {
+ this.title = icon.title;
+ this.slug = icon.slug;
+ this.shield = this.buildShield(icon);
+ }
+
+ private buildShield(icon: SimpleIcon): string {
+ const baseUrl = 'https://img.shields.io/static/v1';
+ const params = {
+ label: '',
+ message: icon.title,
+ color: icon.hex,
+ logo: icon.slug,
+ logoColor: 'white',
+ };
+
+ const shieldUrl = new URL(baseUrl);
+ Object.entries(params).forEach(param => {
+ const [key, value] = param;
+ shieldUrl.searchParams.append(key, value);
+ });
+
+ return shieldUrl.toString();
+ }
+
+ public static sortBySlug(a: Skill, b: Skill) {
+ if (a.slug < b.slug) return -1;
+ if (a.slug > b.slug) return 1;
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/src/data/footerBadges.ts b/src/data/footerBadges.ts
new file mode 100644
index 0000000..63d7b40
--- /dev/null
+++ b/src/data/footerBadges.ts
@@ -0,0 +1,18 @@
+import FooterBadge from '../core/FooterBadge';
+
+const footerBadges: FooterBadge[] = [
+ {
+ title: 'Profile Views',
+ shield: 'https://komarev.com/ghpvc/?username=lucasvbr&label=Profile%20views&color=0e75b6&style=flat',
+ },
+ {
+ title: "FreeCodeCamp Points",
+ shield: "https://img.shields.io/freecodecamp/points/lucasvbr?label=FreeCodeCamp%20points"
+ },
+ {
+ title: "Made with love",
+ shield: "https://img.shields.io/badge/-made%20with%20%E2%9D%A4%EF%B8%8F-red"
+ }
+];
+
+export default footerBadges;
\ No newline at end of file
diff --git a/src/data/links.ts b/src/data/links.ts
new file mode 100644
index 0000000..06c810c
--- /dev/null
+++ b/src/data/links.ts
@@ -0,0 +1,31 @@
+import Link from '../core/Link';
+
+const links: Link[] = [
+ {
+ title: 'Portfolio',
+ shield: 'https://img.shields.io/badge/my_portfolio-000?style=for-the-badge&logo=ko-fi&logoColor=white',
+ url: 'https://lucasvbr.github.io/links/?portfolio',
+ },
+ {
+ title: 'Linkedin Profile',
+ shield: 'https://img.shields.io/badge/linkedin-0e76a8?style=for-the-badge&logo=linkedin&logoColor=white',
+ url: 'https://www.linkedin.com/in/lucasvbr',
+ },
+ {
+ title: 'FreeCodeCamp Profile',
+ shield: 'https://img.shields.io/badge/freecodecamp-0a0a23?style=for-the-badge&logo=freecodecamp&logoColor=white',
+ url: 'https://www.freecodecamp.org/LucasVbr',
+ },
+ {
+ title: 'OpenClassRooms Profile',
+ shield: 'https://img.shields.io/badge/openclassrooms-7451eb?style=for-the-badge&logo=openclassrooms&logoColor=white',
+ url: 'https://openclassrooms.com/fr/members/97j9zltv6225',
+ },
+ {
+ title: 'Exercism Profile',
+ shield: 'https://img.shields.io/badge/exercism-2e57e8?style=for-the-badge&logo=exercism&logoColor=white',
+ url: 'https://exercism.org/profiles/LucasVbr',
+ },
+];
+
+export default links;
\ No newline at end of file
diff --git a/src/data/skills.ts b/src/data/skills.ts
new file mode 100644
index 0000000..73c4749
--- /dev/null
+++ b/src/data/skills.ts
@@ -0,0 +1,37 @@
+import {SimpleIcon} from 'simple-icons/types';
+import icons from 'simple-icons';
+import Skill from '../core/Skill';
+
+const LIST_OF_ICONS: SimpleIcon[] = [
+ icons.siAngular,
+ icons.siSymfony,
+ icons.siGit,
+ icons.siHtml5,
+ icons.siFigma,
+ icons.siJavascript,
+ icons.siGnubash,
+ icons.siAndroid,
+ icons.siBulma,
+ icons.siMariadb,
+ icons.siSqlite,
+ icons.siCss3,
+ icons.siMysql,
+ icons.siPython,
+ icons.siC,
+ icons.siPostgresql,
+ icons.siPhp,
+ icons.siBootstrap,
+ icons.siExpress,
+ icons.siNodedotjs,
+ icons.siDeno,
+ icons.siTypescript,
+ icons.siOcaml,
+ icons.siMongodb,
+ icons.siReact,
+ icons.siDocker,
+];
+
+export default function getSkills(): Skill[] {
+ return LIST_OF_ICONS.map((icon: SimpleIcon) => new Skill(icon)).
+ sort(Skill.sortBySlug);
+}
\ No newline at end of file
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..6fc54ff
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,24 @@
+import {writeFile} from 'fs/promises';
+import nunjucks from 'nunjucks';
+import getSkills from './data/skills';
+import Skill from './core/Skill';
+import log from './core/Log';
+import links from './data/links';
+import footerBadges from './data/footerBadges';
+
+const TEMPLATE_FILE: string = 'README.njk';
+const OUTPUT_FILE: string = 'README.md';
+
+const skills: Skill[] = getSkills();
+
+nunjucks.configure('views/');
+nunjucks.render(TEMPLATE_FILE, {skills, links, footerBadges}, (err, renderView : string | null) => {
+ if (err) {
+ log.error(err.message)
+ return;
+ }
+
+ writeFile(OUTPUT_FILE, renderView ?? "")
+ .then(() => log.success(`${OUTPUT_FILE} successfully generated`))
+ .catch((err: string) => console.error(err));
+});
\ No newline at end of file
diff --git a/views/README.njk b/views/README.njk
new file mode 100644
index 0000000..9532cea
--- /dev/null
+++ b/views/README.njk
@@ -0,0 +1,24 @@
+# Hi, I'm Lucà s! 👋
+
+## 🚀 About Me
+I'm a passionate developer from 🇫🇷 **Albi, France**.
+
+## 🔗 Links
+{% for link in links -%}
+ []({{link.url}})
+{% endfor %}
+
+## 🛠Skills
+{% for skill in skills -%}
+ 
+{% endfor %}
+
+---
+
+
+
+{% for badge in footerBadges -%}
+ 
+{% endfor %}
+
+
\ No newline at end of file