-
About
Feb 23, 2024
Hey there, I'm Wesam. I'm an experienced full-stack developer creating immersive user interfaces and scalable applications. As a Vue expert, I focus on optimizing performance and enhancing user interactions with my team. Additionally, I excel in team leadership and mentoring fellow developers. Most projects are unlisted for confidentiality reasons. Print this webpage to get my resumé in PDF form. <p class="print-only center" style="font-weight: bold;"> Wesam Jabali </p> <p class="print-only center"> [email protected] • wesamjabali.com </p> #### Work Experience | | | | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 2021 - present | [Software Development Engineer III - ALDI](https://new.aldi.us/)<br>Building the future of ALDI's global digital experience, focusing on the US market.<br><br><span class="skills">Vue • Nuxt • TypeScript • SCSS • Jest • Express</span> | | 2023 - 2025 | [Software Development Engineer III - ALDI SÜD](https://www.aldi-sued.de/)<br>Building the future of ALDI's global digital experience, on an assignment in Düsseldorf, Germany.<br><br><span class="skills">Vue • Nuxt • TypeScript • SCSS • Jest • Express</span> | | 2019 - 2021 | [Software Developer - ChiBatterySystems](https://chibatterysystems.com/)<br>Created battery hardware products for the budding personal electric vehicle industry.<br><br><span class="skills">Vue • Nuxt • TypeScript • Arduino • C</span> | | 2019 - 2020 | [Research Assistant - Lewis University](https://lewisu.edu/)<br>Analyzed C. elegans somatic nervous system development using machine learning to predict each neuron's function. The algorithm was able to predict correctly with 81% accuracy. The results were presented to other scientists and financial supporters.<br><br><span class="skills">Python • Pandas • Numpy</span> | #### Education | | | | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 2021 | DePaul University <br>Graduated Summa Cum Laude at DePaul University, and created automation tools used by thousands of students and faculty to facilitate courses during the COVID-19 pandemic.<br><br><span class="skills">3.94 GPA • B.S. • Computer Science </span> | #### Personal Projects | | | | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | present | _AssemblyCore_<br>Quality Control software which helps small American manufacturers to optimize their processes.<br><br><span class="skills">GraphQL • TypeScript • Vue • AWS • Docker</span> | | 2023 | _[Resume Template](https://github.com/wesamjabali/resume)_<br>A minimal resume template website built with Vue - responsive and printable.<br><br><span class="skills">Vue • TypeScript</span> | | 2021 | _[BlueDaemon v2](https://github.com/wesamjabali/bluedaemon-v2)_<br>A Discord bot that allows universities to create and manage their own servers.<br><br><span class="skills">Node • TypeScript • PostgresQL • Express • Prisma • Docker • AWS</span> | <style> :root { --text: rgba(255,255,255,0.7); --text-highlighted: rgba(255,255,255,0.9); --text-lowlighted: rgba(255,255,255,0.6); } @page { size: A4; margin: 0 !important; } @supports (-webkit-touch-callout: none) { @page { size: 21cm 29.7cm; } } .print-only { display: none; @media print { display: block; } } .center { text-align: center; } *, *>*{ @media print { background-color: white !important; color: black !important; border-spacing: 0; text-decoration: none; :root { font-size: 14px; } header, footer, br + br + br, .title, .hero-image, p:not([class]) { display: none; } h1,h2,h3,h4,h5,h6 { font-weight: bold; color: black !important; margin: 1rem 0 0.2rem 0.5rem !important; font-size: 1.1rem; } .skills { font-size: 0.8rem; } th, td { padding: 0.2rem 0 !important; } table { border-spacing: 0.5rem 0 !important; } td::first-line { color: black !important; font-weight: bold; } } } body, body > *, p { background-color: black; color: var(--text); } h1,h2,h3,h4,h5,h6 { color: var(--text-highlighted); } th:first-of-type { width: 20%; } table { border-spacing: 1rem 0; } th, td { padding: 0.5rem 0 0.5rem 0; align-content: start; } /* First line of text in tables, not the year */ th::first-line, td:not(:first-of-type)::first-line, td > em, td > a { color: var(--text-highlighted) } /* Year */ td:first-of-type { color: var(--text-lowlighted) } </style>
-
TSC to file checklist
Mar 27, 2025
When doing a big upgrade on a Typescript project, keeping track of your `tsc` output can be difficult. To get a list of files in a markdown checklist, you can dump your `tsc` output to a file, and run this script on that file, and it'll give you a markdown list of the files you need to go through, one on one. > Notes: - This excludes any `.spec` files. - This is usable with vue-tsc or any other tsc extension ```js const fs = require('fs') const errorsFile = '~/file-of-errors.txt' const outputFile = './errorFiles.md' const errorsString = fs.readFileSync(errorsFile, 'utf8') const errorsArrayExcludeStrings = ['>', 'spec', 'ELIFECYCLE'] const errorsArray = errorsString.split('\n').filter(error => !error.startsWith(' ') && error !== '' && !errorsArrayExcludeStrings.some(s => error.includes(s))) const duplicatedFilesArray = errorsArray.map((error) => { const errorParts = error.split('(') const output = `[ ] ${errorParts[0]}` return output }) const files = [...new Set(duplicatedFilesArray)].sort().sort((a,_b) => a.endsWith('.ts') ? 1 : -1) fs.writeFileSync(outputFile, files.join('\n')) console.log(`${errorsArray.length} errors across ${files.length} files remaining. Output: ${outputFile}`) ```
-
Atomic functions
Mar 27, 2025
Often, we see functions that require an entire object to be passed in, even if only 1 or 2 attributes from that object are used. For example: ```ts function getProductImageFromCart(product: Product): string { const { imageUrl } = useProducts(); const image = getImageWithWidth(product, 64); return getImageUrl(image, product.name) || fallbackUrl; } ``` This approach can make functions rigid and less flexible, especially when dealing with partial objects or different types of objects. For instance, a `MiniProduct` might have all the necessary data but cannot be used because it is not specifically a `Product`. A better approach is to create **atomic functions** that only take the data they need. However, making this change can be time-consuming. An alternative solution is to use a functional type like this: ```ts export type RequireSome<T, K extends keyof T> = Partial<T> & { [P in K]-?: T[P]; }; ``` This type creates a partial version of the type you want to use and makes the specified attributes required. ```ts function getProductImageFromCart( product: RequireSome<Product & MiniProduct, "name" | "images"> ): string; ``` This allows us to pass any type that includes the required attributes, ignoring the rest without causing errors. It also enables the use of well-typed objects with only the necessary data, making the function atomic. So, we can use it like this: ```ts getProductImageFromCart({ name: product.name, images: product.images }); ``` This approach provides an atomic structure and dynamic type safety. As you address these TypeScript issues, consider using this method to make functions more flexible and maintainable.
-
Fix Obsidian URLs with Remark (Astro)
Feb 28, 2024
For those deeply invested in knowledge management and note-taking, Obsidian has become a cornerstone application. Its unique features and customizability cater to diverse workflows, but one challenge can arise when integrating Obsidian with a website: URL formatting discrepancies. This blog post delves into the technical aspects of how to create a plugin designed to address this very issue. We'll explore its functionality and implementation details. #### Understanding the Challenge Obsidian employs a specific markdown syntax for internal links, which can differ from the URL structures used on traditional websites. This inconsistency can lead to broken links when transferring content from Obsidian to a website, hindering the user experience and creating maintenance overhead. #### The Remark Internal Url Fixer: A Solution The remark Internal Url Fixer tackles this challenge head-on by seamlessly transforming internal URLs within your Obsidian notes to align with your website's URL format. This plugin operates within the remark ecosystem, a popular toolkit for markdown processing. > Note: This code snippet does not problems with images. This is left as an exercise for the reader, but has a very similar fix. **Prerequisites:** - `Use [[Wikilinks]]` is off - `New link format` is set to `Absolute path in vault` - `Default location for new attachments` is set to `In subfolder under current folder`  #### The code **Short and sweet:** ```ts import { slugifyFileName } from "../utils/sitemap.util"; export const remarkInternalUrlFixer = () => { return (tree: any) => { function visit(node: any) { if (node.children) { node.children.forEach(visit) } if (node.type === 'link' && node.url) { const isInternalLink = !node.url.includes('://') if (isInternalLink) { node.url = `/${slugifyFileName(decodeURI(node.url))}` } } } visit(tree); }; }; export default remarkInternalUrlFixer; ``` - **`remarkInternalUrlFixer`**: This function serves as the entry point, accepting a markdown document represented as a tree structure. - **`visit`**: This recursive function iterates through the entire tree, examining each node for relevant information. If a node possesses child elements, the function calls itself recursively to process them as well. - **`node.type === 'link'`**: This condition checks if the current node represents a link element, indicating the presence of a URL. - **`!node.url.includes('://')`**: This conditional statement determines if the URL is internal. Internal URLs lack the protocol specifier (e.g., `http` or `https`) and are specific to your website's structure. - **`node.url =`/${slugifyFileName(decodeURI(node.url))}`**: If the URL is deemed internal, the plugin rewrites it. This involves: - **Decoding**: Any encoded characters within the URL are decoded using `decodeURI` to ensure proper handling. - **Slugification**: The filename component of the URL is transformed into a slug format suitable for website URLs using `slugifyFileName`. This ensures consistency and adherence to your website's URL structure. - **Path creation**: A forward slash (`/`) is prepended to the transformed URL, creating a valid website path. This plugin should then be used with Astro/Remark like this: ```ts export default defineConfig({ // ... markdown: { remarkPlugins: [remarkInternalUrlFixer] } // ... }); ``` #### The Advantages of this plugin By leveraging the remark Internal Url Fixer, you can: - **Maintain your preferred workflow**: Continue using Obsidian's internal linking system without the need to manually adjust URLs for your website. - **Effortless integration**: The plugin operates silently in the background, automatically transforming URLs during the markdown processing stage. - **Enhanced user experience**: Visitors to your website will encounter only functional links, leading to a smoother and more positive experience. #### Conclusion: A Bridge Between Obsidian and Your Website The remark Internal Url Fixer demonstrates the power of custom plugins in enhancing Obsidian's capabilities. By addressing the URL formatting discrepancy, this plugin fosters a seamless workflow for knowledge management and content creation, allowing you to leverage the strengths of both Obsidian and Remark.
-
Hiding pages from sitemap in Astro Content
Feb 28, 2024
Astro is a modern static site generator that brings together the best of traditional static site generators and modern build tools. When working with content-driven websites, there might be cases where you want to hide certain pages from public access. In this blog post, we'll explore a TypeScript code snippet that accomplishes this task using Astro. ## Understanding the Code Let's delve into the TypeScript code responsible for filtering out non-public or standalone pages. ```typescript import fs from 'fs'; import yaml from 'js-yaml'; import path from 'path'; interface FileData { slug: string; public: boolean; standalone: boolean; } const directory = path.join(process.cwd(), 'src', 'content'); const collectionName = 'notes'; export function getFilteredSlugs(url: string): string[] { const slugs: string[] = []; function traverseDirectory(dir: string) { const files = fs.readdirSync(dir); files.forEach((file) => { const filePath = path.join(dir, file); const stat = fs.statSync(filePath); if (stat.isDirectory()) { traverseDirectory(filePath); } else if (path.extname(file) === '.md' || path.extname(file) === '.mdx') { const fileContent = fs.readFileSync(filePath, 'utf8'); const yamlData = fileContent.split('---')[1]; const fileData: FileData = yaml.load(yamlData) as FileData; const fileNameSlug = file.replace(/\.mdx?$/, '').replace(/ /g, '-').toLowerCase().replace(/[^a-z0-9-]/g, ''); const preSlugPath = path.dirname(filePath).replace(directory, '').replace(collectionName, '') const slug = fileData.slug ? `/${fileData.slug}` : path.join(preSlugPath, fileNameSlug); if (!fileData.public || fileData.standalone) { slugs.push(`${url}${slug}/`); } } }); } traverseDirectory(directory); return slugs; } ``` ## Code Breakdown 1. **FileData Interface**: Describes the structure of the YAML front matter in each markdown file, including the `slug`, `public`, and `standalone` properties. 2. **Configuration Variables**: Defines the root directory and collection name for content files. 3. **getFilteredSlugs Function**: This function takes a base URL as a parameter and returns an array of filtered slugs. 4. **traverseDirectory Function**: Recursively traverses the content directory, extracting information from markdown files. 5. **YAML Parsing**: Reads the YAML front matter of each markdown file to obtain metadata such as `slug`, `public`, and `standalone`. 6. **Slug Generation**: Generates slugs based on file content and YAML data, considering the specified rules for replacement and transformation. 7. **Filtering**: Adds slugs to the result array only if the page is non-public or standalone. ## Implementation in Astro To use this code in an Astro project, import the `getFilteredSlugs` function and call it with the base URL. The returned array of slugs can then be utilized in your Astro configuration to control the visibility of pages. ```typescript import { getFilteredSlugs } from './src/utils/sitemap.util.ts'; const site = 'https://notes.wesamjabali.com' const filteredSlugs = getFilteredSlugs(site); export default defineConfig({ site, integrations: [mdx(), sitemap({ filter: (pageUrl) => !sitemapIgnoreUrls.includes(pageUrl) })], image: { service: squooshImageService() } }); ``` With this setup, you can easily manage the visibility of pages in your Astro-generated site based on the specified criteria in the YAML front matter. In conclusion, this TypeScript code offers a flexible solution for hiding non-public or standalone pages in an Astro content-driven website, giving you control over which pages are accessible to the public.