<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Rendy Eka Saputra (RES) Blog</title>
        <link>https://res.id.au/blog</link>
        <description>Rendy Eka Saputra (RES) Blog</description>
        <lastBuildDate>Fri, 18 Sep 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Walkthrough: Web Visitor Counter Lambda]]></title>
            <link>https://res.id.au/blog/2026/09/18/lambda-web-visitor-counter</link>
            <guid>https://res.id.au/blog/2026/09/18/lambda-web-visitor-counter</guid>
            <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Inspired by Cloud Resume Challenge, I started building a high-performance, strongly typed AWS Lambda function backed by Amazon DynamoDB for atomic web visitor counting, triggered via Amazon API Gateway HTTP API (Payload v2).]]></description>
            <content:encoded><![CDATA[<p>Inspired by <a href="https://cloudresumechallenge.dev/docs/the-challenge/aws/#7-javascript" target="_blank" rel="noopener noreferrer" class="">Cloud Resume Challenge</a>, I started building a high-performance, strongly typed AWS Lambda function backed by Amazon DynamoDB for atomic web visitor counting, triggered via Amazon API Gateway HTTP API (Payload v2).</p>
<p>Currently I have finished working on the Lambda function, and I am working towards the deployment of the API Gateway HTTP API and DynamoDB table. This post is a walkthrough of the source code, testing, and build process, that I and AI pair-programmed together. The source code is available on <a href="https://github.com/rensaputra/visitor-counter-api" target="_blank" rel="noopener noreferrer" class="">GitHub</a>.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-was-built">What Was Built<a href="https://res.id.au/blog/2026/09/18/lambda-web-visitor-counter#what-was-built" class="hash-link" aria-label="Direct link to What Was Built" title="Direct link to What Was Built" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-source-architecture">1. Source Architecture<a href="https://res.id.au/blog/2026/09/18/lambda-web-visitor-counter#1-source-architecture" class="hash-link" aria-label="Direct link to 1. Source Architecture" title="Direct link to 1. Source Architecture" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://github.com/rensaputra/visitor-counter-api/blob/d180e02969a307162b1cab1ee1589d10396f2bd8/src/types.ts" target="_blank" rel="noopener noreferrer" class=""><code>src/types.ts</code></a>: Strongly typed models for <code>CounterRecord</code>, <code>CounterResponse</code>, <code>ErrorResponse</code>, and <code>AppConfig</code>.</li>
<li class=""><a href="https://github.com/rensaputra/visitor-counter-api/blob/d180e02969a307162b1cab1ee1589d10396f2bd8/src/config.ts" target="_blank" rel="noopener noreferrer" class=""><code>src/config.ts</code></a>: Environment variable loader for <code>TABLE_NAME</code>, <code>ALLOW_ORIGIN</code>, <code>DEFAULT_PAGE_ID</code>, and <code>AWS_REGION</code>.</li>
<li class=""><a href="https://github.com/rensaputra/visitor-counter-api/blob/d180e02969a307162b1cab1ee1589d10396f2bd8/src/utils.ts" target="_blank" rel="noopener noreferrer" class=""><code>src/utils.ts</code></a>: CORS headers, API Gateway v2 response builders, and <code>pageId</code> sanitization (validates characters and restricts max length to 128 characters).</li>
<li class=""><a href="https://github.com/rensaputra/visitor-counter-api/blob/d180e02969a307162b1cab1ee1589d10396f2bd8/src/counter.ts" target="_blank" rel="noopener noreferrer" class=""><code>src/counter.ts</code></a>: DynamoDB atomic operations using AWS SDK v3 <code>DynamoDBDocumentClient</code>:<!-- -->
<ul>
<li class=""><code>incrementCounter</code>: Uses atomic update expression:<!-- -->
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token constant" style="color:#36acaa">SET</span><span class="token plain"> #count </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">if_not_exists</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">#count</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain">zero</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain">inc</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> #updatedAt </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain">now</span><br></div></code></pre></div></div>
</li>
<li class=""><code>getCounter</code>: Reads count without incrementing; returns <code>{ count: 0, updatedAt: null }</code> for unvisited pages.</li>
</ul>
</li>
<li class=""><a href="https://github.com/rensaputra/visitor-counter-api/blob/d180e02969a307162b1cab1ee1589d10396f2bd8/src/handler.ts" target="_blank" rel="noopener noreferrer" class=""><code>src/handler.ts</code></a>: Main AWS Lambda handler (<code>APIGatewayProxyHandlerV2</code>):<!-- -->
<ul>
<li class="">Handles <code>OPTIONS</code> CORS preflight requests (HTTP 204).</li>
<li class="">Routes <code>POST</code> to increment counter.</li>
<li class="">Routes <code>GET</code> to read counter.</li>
<li class="">Returns HTTP 405 for unsupported HTTP methods.</li>
<li class="">Returns HTTP 400 for malformed input or invalid <code>pageId</code>.</li>
<li class="">Returns HTTP 500 for missing config or database errors.</li>
</ul>
</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-testing--tooling">2. Testing &amp; Tooling<a href="https://res.id.au/blog/2026/09/18/lambda-web-visitor-counter#2-testing--tooling" class="hash-link" aria-label="Direct link to 2. Testing &amp; Tooling" title="Direct link to 2. Testing &amp; Tooling" translate="no">​</a></h3>
<ul>
<li class=""><a href="https://github.com/rensaputra/visitor-counter-api/blob/d180e02969a307162b1cab1ee1589d10396f2bd8/package.json" target="_blank" rel="noopener noreferrer" class=""><code>package.json</code></a>: Configured with <code>@aws-sdk/client-dynamodb</code>, <code>@aws-sdk/lib-dynamodb</code>, <code>esbuild</code>, <code>jest</code>, <code>ts-jest</code>, and <code>aws-sdk-client-mock</code>.</li>
<li class=""><a href="https://github.com/rensaputra/visitor-counter-api/blob/d180e02969a307162b1cab1ee1589d10396f2bd8/jest.config.js" target="_blank" rel="noopener noreferrer" class=""><code>jest.config.js</code></a>: Unit test configuration with <code>ts-jest</code> and coverage thresholds.</li>
<li class=""><a href="https://github.com/rensaputra/visitor-counter-api/blob/d180e02969a307162b1cab1ee1589d10396f2bd8/tests/counter.test.ts" target="_blank" rel="noopener noreferrer" class=""><code>tests/counter.test.ts</code></a>: Unit tests for DynamoDB atomic increments and reads.</li>
<li class=""><a href="https://github.com/rensaputra/visitor-counter-api/blob/d180e02969a307162b1cab1ee1589d10396f2bd8/tests/handler.test.ts" target="_blank" rel="noopener noreferrer" class=""><code>tests/handler.test.ts</code></a>: End-to-end event routing, CORS, query/body parsing, and error handling.</li>
<li class=""><a href="https://github.com/rensaputra/visitor-counter-api/blob/d180e02969a307162b1cab1ee1589d10396f2bd8/tests/config.test.ts" target="_blank" rel="noopener noreferrer" class=""><code>tests/config.test.ts</code></a>: Environment variable validation tests.</li>
<li class=""><a href="https://github.com/rensaputra/visitor-counter-api/blob/d180e02969a307162b1cab1ee1589d10396f2bd8/tests/utils.test.ts" target="_blank" rel="noopener noreferrer" class=""><code>tests/utils.test.ts</code></a>: Sanitization, validation, and CORS tests.</li>
<li class=""><a href="https://github.com/rensaputra/visitor-counter-api/blob/d180e02969a307162b1cab1ee1589d10396f2bd8/README.md" target="_blank" rel="noopener noreferrer" class=""><code>README.md</code></a>: Documentation including DynamoDB table creation, IAM permissions, API Gateway HTTP API setup, and frontend client snippet.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="validation-results">Validation Results<a href="https://res.id.au/blog/2026/09/18/lambda-web-visitor-counter#validation-results" class="hash-link" aria-label="Direct link to Validation Results" title="Direct link to Validation Results" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-typescript-strict-typecheck">1. TypeScript Strict Typecheck<a href="https://res.id.au/blog/2026/09/18/lambda-web-visitor-counter#1-typescript-strict-typecheck" class="hash-link" aria-label="Direct link to 1. TypeScript Strict Typecheck" title="Direct link to 1. TypeScript Strict Typecheck" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run typecheck</span><br></div></code></pre></div></div>
<blockquote>
<p>Passed with 0 errors.</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-jest-test-suite-with-coverage">2. Jest Test Suite with Coverage<a href="https://res.id.au/blog/2026/09/18/lambda-web-visitor-counter#2-jest-test-suite-with-coverage" class="hash-link" aria-label="Direct link to 2. Jest Test Suite with Coverage" title="Direct link to 2. Jest Test Suite with Coverage" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run test:coverage</span><br></div></code></pre></div></div>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">PASS tests/config.test.ts</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">PASS tests/utils.test.ts</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">PASS tests/counter.test.ts</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">PASS tests/handler.test.ts</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">------------|---------|----------|---------|---------|-------------------</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">File        | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">------------|---------|----------|---------|---------|-------------------</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">All files   |    96.8 |    90.69 |   85.71 |    96.8 |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> config.ts  |     100 |    88.88 |     100 |     100 |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> counter.ts |     100 |       80 |     100 |     100 |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> handler.ts |    93.1 |      100 |      50 |    93.1 |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> utils.ts   |   97.29 |    94.11 |     100 |   97.29 |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">------------|---------|----------|---------|---------|-------------------</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Test Suites: 4 passed, 4 total</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Tests:       33 passed, 33 total</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-build--package-verification">3. Build &amp; Package Verification<a href="https://res.id.au/blog/2026/09/18/lambda-web-visitor-counter#3-build--package-verification" class="hash-link" aria-label="Direct link to 3. Build &amp; Package Verification" title="Direct link to 3. Build &amp; Package Verification" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run package</span><br></div></code></pre></div></div>
<ul>
<li class="">Bundled via <code>esbuild</code> to CommonJS Node.js 20 target.</li>
<li class="">Produced <code>dist/visitor-counter.zip</code> (577 KB), verified <code>dist/index.js</code> exports <code>handler</code>.</li>
</ul>]]></content:encoded>
            <category>Walkthrough</category>
            <category>Lambda</category>
            <category>DynamoDB</category>
            <category>API Gateway</category>
            <category>Node.js</category>
            <category>TypeScript</category>
            <category>Visitor Counter API</category>
        </item>
        <item>
            <title><![CDATA[How to setup Husky and Lint-Staged]]></title>
            <link>https://res.id.au/blog/2026/09/17/how-to-setup-1-husky-and-lint-staged</link>
            <guid>https://res.id.au/blog/2026/09/17/how-to-setup-1-husky-and-lint-staged</guid>
            <pubDate>Thu, 17 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[In modern software development, maintaining code quality and consistency across a team is crucial. When multiple developers work on a single codebase, differences in formatting, syntax styles, and simple errors can lead to messy pull requests and broken builds. This is where Git hooks, specifically pre-commit hooks, become incredibly valuable.]]></description>
            <content:encoded><![CDATA[<p>In modern software development, maintaining code quality and consistency across a team is crucial. When multiple developers work on a single codebase, differences in formatting, syntax styles, and simple errors can lead to messy pull requests and broken builds. This is where Git hooks, specifically pre-commit hooks, become incredibly valuable.</p>
<p>Two of the most powerful tools in the JavaScript ecosystem for managing Git hooks and code quality are <strong>Husky</strong> and <strong>Lint-Staged</strong>.</p>
<p>In this post, we'll explore what these tools are, why they are important, how to set them up, and how to test your configuration locally.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-are-husky-and-lint-staged">What are Husky and Lint-Staged?<a href="https://res.id.au/blog/2026/09/17/how-to-setup-1-husky-and-lint-staged#what-are-husky-and-lint-staged" class="hash-link" aria-label="Direct link to What are Husky and Lint-Staged?" title="Direct link to What are Husky and Lint-Staged?" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="husky">Husky<a href="https://res.id.au/blog/2026/09/17/how-to-setup-1-husky-and-lint-staged#husky" class="hash-link" aria-label="Direct link to Husky" title="Direct link to Husky" translate="no">​</a></h3>
<p>Husky is a tool that allows you to easily tie scripts to Git hooks. Git hooks are scripts that Git executes before or after events such as: <code>commit</code>, <code>push</code>, and <code>receive</code>. Husky makes it simple to manage these hooks within your <code>package.json</code> or as separate script files, ensuring they are automatically installed for everyone who works on the project.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="lint-staged">Lint-Staged<a href="https://res.id.au/blog/2026/09/17/how-to-setup-1-husky-and-lint-staged#lint-staged" class="hash-link" aria-label="Direct link to Lint-Staged" title="Direct link to Lint-Staged" translate="no">​</a></h3>
<p>Lint-Staged is a tool that allows you to run linters (like ESLint, Prettier, or cspell) against staged git files and don't let it slip into your codebase. Running linters on the entire project can be slow. Lint-Staged solves this by only running checks on files that are actively being modified and staged for a commit.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-is-it-important">Why is it Important?<a href="https://res.id.au/blog/2026/09/17/how-to-setup-1-husky-and-lint-staged#why-is-it-important" class="hash-link" aria-label="Direct link to Why is it Important?" title="Direct link to Why is it Important?" translate="no">​</a></h2>
<ol>
<li class=""><strong>Catches Errors Early</strong>: By running linting, formatting, and spell checks on every commit, you catch simple mistakes before they even leave your local machine or trigger a CI build.</li>
<li class=""><strong>Enforces Consistency</strong>: It ensures that all code pushed to the repository adheres to the team's defined standards (e.g., Prettier formatting, ESLint rules).</li>
<li class=""><strong>Improves Code Reviews</strong>: When the code is consistently formatted, code reviews can focus on the actual logic and architecture rather than nitpicking spacing or syntax issues.</li>
<li class=""><strong>Saves CI Time</strong>: Failing fast locally means your continuous integration (CI) pipeline doesn't have to waste time running jobs that fail on trivial style errors.</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-to-set-it-up">How to Set It Up<a href="https://res.id.au/blog/2026/09/17/how-to-setup-1-husky-and-lint-staged#how-to-set-it-up" class="hash-link" aria-label="Direct link to How to Set It Up" title="Direct link to How to Set It Up" translate="no">​</a></h2>
<p>Let's walk through setting up Husky and Lint-Staged to run ESLint, Prettier, and a spell checker (<code>cspell</code>) on pre-commit.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-install-dependencies">1. Install Dependencies<a href="https://res.id.au/blog/2026/09/17/how-to-setup-1-husky-and-lint-staged#1-install-dependencies" class="hash-link" aria-label="Direct link to 1. Install Dependencies" title="Direct link to 1. Install Dependencies" translate="no">​</a></h3>
<p>First, ensure you have Husky, Lint-Staged, and your desired linters installed as <code>devDependencies</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> --save-dev husky lint-staged eslint prettier cspell</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-initialize-husky">2. Initialize Husky<a href="https://res.id.au/blog/2026/09/17/how-to-setup-1-husky-and-lint-staged#2-initialize-husky" class="hash-link" aria-label="Direct link to 2. Initialize Husky" title="Direct link to 2. Initialize Husky" translate="no">​</a></h3>
<p>Run the Husky initialization script. This sets up the <code>.husky</code> directory and prepares Git to use it:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npx husky init</span><br></div></code></pre></div></div>
<p>This command typically adds a <code>prepare</code> script in your <code>package.json</code> to ensure Husky is installed automatically after <code>npm install</code>.</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"scripts"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"prepare"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"husky"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-create-a-pre-commit-hook">3. Create a Pre-commit Hook<a href="https://res.id.au/blog/2026/09/17/how-to-setup-1-husky-and-lint-staged#3-create-a-pre-commit-hook" class="hash-link" aria-label="Direct link to 3. Create a Pre-commit Hook" title="Direct link to 3. Create a Pre-commit Hook" translate="no">​</a></h3>
<p>Next, we need to instruct Husky to run <code>lint-staged</code> before every commit.</p>
<p>If <code>npx husky init</code> created a default <code>pre-commit</code> hook (usually echoing something or running <code>npm test</code>), you can overwrite it. Run the following command or edit the <code>.husky/pre-commit</code> file directly:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token builtin class-name">echo</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"npx lint-staged"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> .husky/pre-commit</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-configure-lint-staged">4. Configure Lint-Staged<a href="https://res.id.au/blog/2026/09/17/how-to-setup-1-husky-and-lint-staged#4-configure-lint-staged" class="hash-link" aria-label="Direct link to 4. Configure Lint-Staged" title="Direct link to 4. Configure Lint-Staged" translate="no">​</a></h3>
<p>Finally, configure <code>lint-staged</code> in your <code>package.json</code> to define which tools run on which files. Here is an example configuration:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"lint-staged"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"**/*.{md,mdx}"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"cspell lint"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"**/*.{js,jsx,ts,tsx,json,md,mdx,css}"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"prettier --write"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"src/**/*.{js,jsx,ts,tsx}"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"eslint"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><strong>Explanation of the config:</strong></p>
<ul>
<li class=""><code>**/*.{md,mdx}</code>: Runs the spell checker (<code>cspell</code>) on all Markdown and MDX files.</li>
<li class=""><code>**/*.{js,jsx,ts,tsx,json,md,mdx,css}</code>: Runs Prettier to automatically format code. The <code>--write</code> flag tells Prettier to save the formatted changes back to the files.</li>
<li class=""><code>src/**/*.{js,jsx,ts,tsx}</code>: Runs ESLint on your source code to catch potential logic errors or rule violations.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-to-test-it-locally">How to Test It Locally<a href="https://res.id.au/blog/2026/09/17/how-to-setup-1-husky-and-lint-staged#how-to-test-it-locally" class="hash-link" aria-label="Direct link to How to Test It Locally" title="Direct link to How to Test It Locally" translate="no">​</a></h2>
<p>Once configured, testing the setup is straightforward.</p>
<ol>
<li class=""><strong>Make a Change</strong>: Modify a file that matches your lint-staged configuration. For example, add some unformatted code to a JavaScript file or a typo to a Markdown file.</li>
<li class=""><strong>Stage the File</strong>:<!-- -->
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">git</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> path/to/your/file.js</span><br></div></code></pre></div></div>
</li>
<li class=""><strong>Attempt a Commit</strong>:<!-- -->
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">git</span><span class="token plain"> commit </span><span class="token parameter variable" style="color:#36acaa">-m</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Test husky setup"</span><br></div></code></pre></div></div>
</li>
</ol>
<p><strong>What should happen?</strong></p>
<ul>
<li class="">Husky intercepts the commit process.</li>
<li class="">It executes <code>npx lint-staged</code>.</li>
<li class="">Lint-Staged looks at the staged file (<code>file.js</code>).</li>
<li class="">It runs the configured tools (e.g., <code>prettier --write</code> and <code>eslint</code>).</li>
<li class=""><strong>If everything passes:</strong> The formatted file is added to the commit, and the commit succeeds.</li>
<li class=""><strong>If a linter fails (e.g., ESLint finds an error):</strong> The commit is aborted, and the error output is displayed in your terminal, forcing you to fix the issue before committing again.</li>
</ul>
<p>By setting up Husky and Lint-Staged, you automate quality control directly within the developer workflow, leading to cleaner codebases and more efficient teams. Happy coding!</p>]]></content:encoded>
            <category>CI/CD</category>
        </item>
        <item>
            <title><![CDATA[How to setup Eslint, Prettier and CodeQL]]></title>
            <link>https://res.id.au/blog/how-to-setup-eslint-prettier-and-codeql</link>
            <guid>https://res.id.au/blog/how-to-setup-eslint-prettier-and-codeql</guid>
            <pubDate>Thu, 17 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[In modern web development, maintaining a clean, secure, and consistent codebase is paramount, especially when working on teams. This is where tools like ESLint, Prettier, and CodeQL come in.]]></description>
            <content:encoded><![CDATA[<p>In modern web development, maintaining a clean, secure, and consistent codebase is paramount, especially when working on teams. This is where tools like ESLint, Prettier, and CodeQL come in.</p>
<!-- -->
<ul>
<li class=""><strong>ESLint</strong> is a powerful static code analysis tool that finds and fixes problems in your JavaScript or TypeScript code. It ensures that your code adheres to specific stylistic guidelines and catches potential runtime errors before the code is even executed.</li>
<li class=""><strong>Prettier</strong> is an opinionated code formatter. Its sole job is to take your code and re-format it to ensure visual consistency across the entire project. It ends debates over spaces versus tabs, trailing commas, and line length.</li>
<li class=""><strong>CodeQL</strong> is a semantic code analysis engine developed by GitHub. It allows you to query your code as if it were a database to find security vulnerabilities and bugs. It is highly popular because it integrates seamlessly into the GitHub ecosystem.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="installing-packages">Installing packages<a href="https://res.id.au/blog/how-to-setup-eslint-prettier-and-codeql#installing-packages" class="hash-link" aria-label="Direct link to Installing packages" title="Direct link to Installing packages" translate="no">​</a></h2>
<p>To get started, we need to install the necessary packages for ESLint and Prettier. We don't need to install CodeQL locally as it is provided as a free service via GitHub Actions and runs directly in the CI/CD pipeline.</p>
<p>Run the following command in your terminal to install the necessary development dependencies:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-D</span><span class="token plain"> eslint prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/parser @typescript-eslint/eslint-plugin</span><br></div></code></pre></div></div>
<p>Next, you will need to create the configuration files for these tools in the root directory of your project.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="eslint-configuration-eslintrcjs">ESLint Configuration (<code>.eslintrc.js</code>)<a href="https://res.id.au/blog/how-to-setup-eslint-prettier-and-codeql#eslint-configuration-eslintrcjs" class="hash-link" aria-label="Direct link to eslint-configuration-eslintrcjs" title="Direct link to eslint-configuration-eslintrcjs" translate="no">​</a></h3>
<p>Create a <code>.eslintrc.js</code> file and add the following configuration to integrate ESLint with React, TypeScript, and Prettier:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">exports </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">env</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">browser</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">es2021</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">node</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">extends</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'eslint:recommended'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'plugin:@typescript-eslint/recommended'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'plugin:react/recommended'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'plugin:react-hooks/recommended'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'plugin:prettier/recommended'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">parser</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@typescript-eslint/parser'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">parserOptions</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">ecmaFeatures</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">jsx</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">ecmaVersion</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">12</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">sourceType</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'module'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">plugins</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'react'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@typescript-eslint'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'prettier'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">rules</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string-property property" style="color:#36acaa">'prettier/prettier'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'warn'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string-property property" style="color:#36acaa">'react/react-in-jsx-scope'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'off'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string-property property" style="color:#36acaa">'@typescript-eslint/no-unused-vars'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'warn'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">argsIgnorePattern</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'^_'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">settings</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">react</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">version</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'detect'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="prettier-configuration-prettierrc">Prettier Configuration (<code>.prettierrc</code>)<a href="https://res.id.au/blog/how-to-setup-eslint-prettier-and-codeql#prettier-configuration-prettierrc" class="hash-link" aria-label="Direct link to prettier-configuration-prettierrc" title="Direct link to prettier-configuration-prettierrc" translate="no">​</a></h3>
<p>Create a <code>.prettierrc</code> file to define your code formatting rules:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"semi"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"trailingComma"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"all"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"singleQuote"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"printWidth"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"tabWidth"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="configure-packagejson">Configure package.json<a href="https://res.id.au/blog/how-to-setup-eslint-prettier-and-codeql#configure-packagejson" class="hash-link" aria-label="Direct link to Configure package.json" title="Direct link to Configure package.json" translate="no">​</a></h2>
<p>To easily execute ESLint and Prettier, we need to add several scripts to our <code>package.json</code> file.</p>
<p>Update the <code>"scripts"</code> block in your <code>package.json</code> to include the following lines:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"scripts"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"format"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"prettier --write ."</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"format:check"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"prettier --check ."</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"lint"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"eslint \"src/**/*.{js,jsx,ts,tsx}\""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>"format"</code>: Runs Prettier across the entire project directory and overwrites the files with the properly formatted code.</li>
<li class=""><code>"format:check"</code>: Runs Prettier in check mode, outputting any files that are not formatted correctly without modifying them. This is particularly useful for CI environments.</li>
<li class=""><code>"lint"</code>: Runs ESLint targeting all JavaScript and TypeScript files within the <code>src/</code> directory to catch errors and enforce code quality.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ignore-files">Ignore files<a href="https://res.id.au/blog/how-to-setup-eslint-prettier-and-codeql#ignore-files" class="hash-link" aria-label="Direct link to Ignore files" title="Direct link to Ignore files" translate="no">​</a></h2>
<p>We don't want our linting and formatting tools to scan compiled output, third-party libraries, or cache folders. To prevent this, we must create two ignore files: <code>.eslintignore</code> and <code>.prettierignore</code>.</p>
<p>Create both <code>.eslintignore</code> and <code>.prettierignore</code> in the root of your project and add the following folders to them:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">node_modules</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">build</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">dist</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">.docusaurus</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">coverage</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong><code>node_modules</code></strong>: Contains third-party dependencies which we should not lint or format.</li>
<li class=""><strong><code>build</code> &amp; <code>dist</code></strong>: Compiled production outputs.</li>
<li class=""><strong><code>.docusaurus</code></strong>: Docusaurus cache and auto-generated build files.</li>
<li class=""><strong><code>coverage</code></strong>: Test coverage reporting output directory.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="testing">Testing<a href="https://res.id.au/blog/how-to-setup-eslint-prettier-and-codeql#testing" class="hash-link" aria-label="Direct link to Testing" title="Direct link to Testing" translate="no">​</a></h2>
<p>Now the most important part: ensuring our setup works locally.</p>
<p>To test that Prettier is correctly formatting files, run the following command in your terminal:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run </span><span class="token function" style="color:#d73a49">format</span><br></div></code></pre></div></div>
<p><strong>Expected Output:</strong> Prettier will scan through your project and automatically reformat any file that does not meet the standards defined in <code>.prettierrc</code>. The terminal will list the files it has processed.</p>
<p>To check for any potential code quality issues using ESLint, run:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> run lint</span><br></div></code></pre></div></div>
<p><strong>Expected Output:</strong> ESLint will scan the files in the <code>src/</code> directory. If your code is completely valid and follows all rules, the command will silently exit without any output. If there are code errors or stylistic violations, it will print a detailed report indicating the exact file, line number, and error/warning message.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="github-actions-ci-integration">GitHub Actions CI Integration<a href="https://res.id.au/blog/how-to-setup-eslint-prettier-and-codeql#github-actions-ci-integration" class="hash-link" aria-label="Direct link to GitHub Actions CI Integration" title="Direct link to GitHub Actions CI Integration" translate="no">​</a></h2>
<p>To ensure no code regressions are introduced into the main branch, these tools are executed automatically in our CI pipeline.</p>
<p>In our <code>.github/workflows/ci.yml</code> file, we added steps to invoke both our formatting check (<code>npm run format:check</code>) and our linting script (<code>npm run lint</code>). If any of these scripts fail, the CI run will fail, blocking the pull request from being merged.</p>
<p>Additionally, a separate <code>.github/workflows/codeql-analysis.yml</code> workflow was introduced. This workflow initializes the CodeQL environment, builds the project (if necessary), and analyzes the codebase for common vulnerabilities, reporting them directly in the GitHub Security tab of the repository.</p>]]></content:encoded>
            <category>CI/CD</category>
        </item>
        <item>
            <title><![CDATA[Welcome]]></title>
            <link>https://res.id.au/blog/welcome</link>
            <guid>https://res.id.au/blog/welcome</guid>
            <pubDate>Thu, 17 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Welcome to my new blog! This space will be used to share my thoughts, experiences, and technical notes.]]></description>
            <content:encoded><![CDATA[<p>Welcome to my new blog! This space will be used to share my thoughts, experiences, and technical notes.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="docusaurus-blog-features-summary">Docusaurus Blog Features Summary<a href="https://res.id.au/blog/welcome#docusaurus-blog-features-summary" class="hash-link" aria-label="Direct link to Docusaurus Blog Features Summary" title="Direct link to Docusaurus Blog Features Summary" translate="no">​</a></h3>
<p>This blog is powered by Docusaurus, which provides several great features out-of-the-box:</p>
<ul>
<li class=""><strong>MDX Support</strong>: Blog posts support Docusaurus Markdown features, such as MDX. This allows the use of React components (like interactive buttons) and Admonitions (callouts/tips) directly inside the markdown files.</li>
<li class=""><strong>Truncation</strong>: The <code>{/* truncate */}</code> comment can be used to limit the blog post size in the list view, keeping the main blog page clean and readable.</li>
<li class=""><strong>File Organization</strong>: Posts can be standalone markdown files or placed within their own folders, which is convenient for co-locating images specific to a post.</li>
<li class=""><strong>Metadata</strong>: The blog post date can be automatically extracted from filenames (e.g., <code>2026-09-17-welcome.mdx</code>), and author/tag metadata is easily managed via <code>authors.yml</code> and <code>tags.yml</code>.</li>
</ul>]]></content:encoded>
            <category>Blog</category>
        </item>
    </channel>
</rss>