-
TSC to file checklist
Sep 13, 2024
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
Sep 13, 2024
I see in many functions where we use only 1 or 2 attributes from an object, we still require the entire object gets passed, like this: ```ts function getProductImageFromCart(product: Product): string { const { imageUrl } = useProducts() const image = getImageWithWidth(product, 64) return getImageUrl(image, product.name) || fallbackUrl } ``` Ideally, we shouldn't be doing this because it makes our functions really rigid, and not usable for cases where we have partial products, or different real types of products. In this case a `MiniProduct` has all the data needed, but isn't usable because it isn't specifically a `Product.` The best way to go about fixing this is to make **atomic functions** where we only take the data we need. But, making this change could take a lot of effort. There is another way though, which is by using a functional type like this: ```ts export type RequireSome<T, K extends keyof T> = Partial<T> & { [P in K]-?: T[P] } ``` This makes a partial of the type you want to use, then makes all the listed attributes required. ```ts function getProductImageFromCart(product: RequireSome<Product & MiniProduct, 'name' | 'images'>): string ``` This allows us to pass any type we want, as long as it has the two things we need, and will discard the rest but not error out if you pass something with a different form. This also allows you to use well-typed objects with only the data we need, making it atomic. So, we can use it like this: ```ts getProductImageFromCart({ name: product.name, images: product.images }) ``` And this works great, giving us atomic structure and dynamic typesafety. So, as you're fixing these TS issues please use this approach to atomize our functions where needed.
-
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` ![](code/attachments/screenshot.webp) #### 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.
-
About
Feb 23, 2024
Hey there, I'm Wesam. I'm an experienced fullstack developer creating immersive user interfaces and scalable applications. I'm based in Chicago, but currently living in Düsseldorf, Germany. 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. #### Work Experience | | | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 2023 - present | [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>Vue • Nuxt • TypeScript • SCSS • Jest • Express | | 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>Vue • Nuxt • TypeScript • SCSS • Jest • Express | | 2019 - 2021 | [Software Developer - ChiBatterySystems](https://chibatterysystems.com/)<br>Created battery hardware products for the budding personal electric vehicle industry.<br><br>Vue • Nuxt • TypeScript • Arduino • C | | 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>Python • Pandas • Numpy | #### 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>3.94 GPA • B.S. • Computer Science | #### Personal Projects | | | | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 2024 | [_Al-Quran_](https://quran.wesamjabali.com)<br>High quality Quran PWA in both its original text and English. Equipped with caching of all content and audios, offline playback and URL references.<br><br>TypeScript • Nuxt • PWA • Service Workers | | present | _AssemblyCore_<br>Quality Control software which helps small American manufacturers to optimize their processes.<br><br>GraphQL • TypeScript • Vue • AWS • Docker | | 2023 | _[Resume Template](https://github.com/wesamjabali/resume)_<br>A minimal resume template website built with Vue - responsive and printable.<br><br>Vue • TypeScript | | 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>Node • TypeScript • PostgresQL • Express • Prisma • Docker • AWS | <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) } 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>