33.7 Measuring Progress: Counting any Usages and Coverage

Right, so you’ve started the migration. You’ve got tsc running, you’ve silenced the most egregious errors with a few any bandaids, and now you’re staring at a question: “Are we there yet?” The answer, my friend, is almost certainly “no,” but we can at least figure out how far away ’there’ is. We need metrics that tell a true story, not a fairy tale. Relying on the TypeScript compiler’s error count is like measuring a road trip by how many times you’ve screamed at other drivers—dramatic but not terribly useful. Let’s talk about real, actionable metrics.

33.6 Automating Migration with ts-migrate and TypeScript-Migrate

Right, so you’ve decided to stop living in the wild west of JavaScript and put on the structured, slightly-constricting-but-in-a-good-way suit of TypeScript. Good for you. Doing this by hand for a large codebase is a special kind of masochism I don’t recommend. This is where automation tools like ts-migrate (from Airbnb) and typescript-migrate (a similar concept) come in. They’re not magic wands, but they’re the closest thing we have to a bulldozer that can clear the initial path. Think of them as your over-caffeinated intern who does 80% of the grunt work incredibly fast, but you absolutely must check their work before you ship it.

33.5 Using @ts-ignore and @ts-expect-error as Temporary Suppressions

Alright, let’s talk about the duct tape and baling wire of your TypeScript migration: @ts-ignore and its slightly more responsible cousin, @ts-expect-error. You’re going to use these. I’ve used them. We all have. They are the temporary barricades you throw up against the tsunami of red squiggles so you can actually compile your code and keep the business running while you methodically fix things. The key word here is temporary. Think of them as a “Check Engine” light you’re actually going to get around to fixing, not one you just put a piece of electrical tape over.

33.4 Dealing with Untyped Dependencies During Migration

Right, so you’ve started wrapping your own code in types, feeling that sweet, sweet intellisense flow, and then you hit this wall: import someLibrary from 'that-old-untyped-package'. Suddenly, your beautifully typed file is awash in any. It’s like meticulously cleaning your house only to have your roommate drag in a muddy dog. Don’t panic. This is a rite of passage. We’re going to tame that muddy dog, or at least build it a very specific shed so it doesn’t track mud everywhere.

33.3 Renaming Files: .js to .ts One Module at a Time

Right, let’s get our hands dirty. We’re not doing a big-bang rewrite where you rename every single file at once and pray for the best. That’s a fantastic way to ruin your entire weekend. Instead, we’re going to do this surgically, one module at a time. This is the “eat the elephant one bite at a time” strategy, except the elephant is your technical debt and we have a very precise fork.

33.2 Adding tsconfig.json to an Existing Project

Right, let’s get this party started. You’ve got a sprawling JavaScript project, and you’ve decided to inject some sanity into it with TypeScript. The very first, and arguably most important, step is to drop a tsconfig.json file in the root of your project. This file is the mission control for the TypeScript compiler (tsc); it tells it exactly how to behave, what files to look at, and how strictly to judge your life choices. Think of it less as a configuration file and more as a rulebook you get to write for your new, more disciplined coding life.

33.1 The Incremental Migration Strategy: allowJs and checkJs

Right, so you’ve decided to stop living in the wild west of JavaScript and put on the structured, slightly-stiff-but-incredibly-warm jacket of TypeScript. Good choice. But you’re not about to stop the entire product roadmap, lock the team in a basement for six months, and rewrite 200,000 lines of code in one go. That’s a fantastic way to get fired, or at the very least, develop a nervous twitch. We’re going to be smart about this. We’re going to use TypeScript’s built-in escape hatches and incremental flags. This isn’t an all-or-nothing proposition. The two key players in this strategy are allowJs and checkJs. Think of them as the training wheels and the slightly anxious parent running behind the bike, respectively.

1.6 When TypeScript Helps and When It Gets in the Way

Look, let’s get this out of the way: TypeScript is not a magic wand. It won’t turn your spaghetti code into a Michelin-star meal. It’s more like a brilliant, slightly pedantic sous-chef who won’t let you add cinnamon to the bolognese without a very, very good reason. Most of the time, this saves you from culinary disaster. Sometimes, you just want to make a weird pie and you have to argue about it.

1.5 The TypeScript Ecosystem: Compiler, Language Server, and Definitions

Right, so you’ve installed TypeScript. npm install -g typescript. Feels good, right? Like you’ve just equipped your JavaScript with a fancy new toolbox. But here’s the thing: that tsc command you now have is just the most visible part of a surprisingly sophisticated ecosystem. It’s not a single tool; it’s a whole team of specialists working behind the curtain to make you look good. Let’s meet them. The Compiler (tsc): It’s Not What You Think First, the star of the show, the TypeScript compiler, tsc. You run it, it takes your .ts files, and poops out .js. Simple. But its job is far more nuanced than that. Its primary mission isn’t to transpile code (convert TS to JS)—that’s actually the easiest part of its job. Its real purpose is to be a static type checker.

1.4 Structural vs Nominal Typing: What TypeScript Chose and Why

Right, let’s get this straight. You’ve probably heard TypeScript described as a “statically typed superset of JavaScript.” That’s true, but it’s also a bit of a corporate mission statement. The real magic, and the source of most of its “Wait, that compiles?!” moments, is its typing philosophy: structural typing. Most languages you’re used to, like Java or C#, use nominal typing. In that world, a type’s identity is its name. If you have a class Dog and a class Cat, even if they have exactly the same properties (name, breed, age), you cannot assign a Dog to a Cat. Their names are different, end of story. It’s like trying to board a flight with a driver’s license—the government issued both, but the TSA agent really cares about the specific name on the card.

1.3 How TypeScript Relates to JavaScript: A Typed Superset

Right, so you’ve heard TypeScript is a “typed superset” of JavaScript. That sounds like marketing-speak, but it’s actually the single most important concept to wrap your head around. It means TypeScript is JavaScript, just with a new feature bolted on: a type system. It doesn’t replace JavaScript; it augments it. Every single valid JavaScript program is also a valid TypeScript program. Let that sink in. Your crusty old script.js file? Rename it to script.ts and the TypeScript compiler will just shrug and say, “Okay, cool.” It’s the ultimate in backward compatibility.

1.2 TypeScript's History: Anders Hejlsberg and Microsoft's 2012 Bet

Let’s be honest: JavaScript in 2012 was a bit of a mess. You could build amazing things with it, but scaling a large application felt like building a house of cards in a wind tunnel. You never knew which part would blow over next because you passed a string where a number should be, or tried to call a method on undefined. We were all just one cannot read property 'x' of undefined away from a minor existential crisis.

1.1 The Problem TypeScript Solves: Runtime Errors at Compile Time

Right, let’s get to the heart of the matter. You’re here because you’ve felt the pain. You’ve written a perfectly reasonable-looking piece of JavaScript, you ran it, and… nothing happened. Or worse, something happened, but it was the digital equivalent of a quiet, sad trombone sound. You open the console and there it is: Uncaught TypeError: Cannot read property 'something' of undefined. Your application has, quite rudely, stopped working. You’ve just been introduced to the most common class of bugs in the JavaScript ecosystem: the runtime error.

25.8 The integrity Attribute for Subresource Integrity (SRI)

Right, let’s talk about making your site a fortress. You’ve gone through the trouble of setting up HTTPS, your headers are tight, and then you go and load a script from some third-party CDN. You’re trusting that CDN to serve the exact code you tested, not something a malicious actor slipped in there. That’s a huge, glaring weak spot. This is where Subresource Integrity (SRI) comes in, and Hugo, being the brilliant but occasionally obtuse tool it is, gives us the integrity attribute in its resources pipeline to handle it.

25.7 Concatenation: resources.Concat

Right, let’s talk about concatenation. You’re probably thinking, “Isn’t this just gluing files together?” And you’d be right. But in the world of static sites, doing this efficiently without a live build server (like Webpack) watching your every move is a bit of a superpower. That’s where resources.Concat comes in. It’s your go-to for bundling those pesky little CSS or JS files into a single, cache-friendly, HTTP-request-reducing masterpiece. Think of it as the static site equivalent of duct tape and ambition—but it actually works.

25.6 Minification: resources.Minify

Right, let’s talk about minification. You’ve probably heard the term thrown around like a holy mantra for performance. “Minify your assets!” they shout from the conference stages. And they’re not wrong. But what does it actually mean in Hugo? It means taking your beautifully formatted, human-readable CSS, JS, JSON, HTML, or SVG and ruthlessly stripping out every single unnecessary byte. We’re talking whitespace, comments, and sometimes even shortening variable names. It’s the digital equivalent of vacuum-packing your clothes for a trip. The goal isn’t to be pretty; it’s to be small and fast.

25.5 Fingerprinting and Cache-Busting: resources.Fingerprint

Right, let’s talk about cache-busting. It’s one of those problems that sounds trivial until you’ve spent an hour staring at a browser, hitting Ctrl+F5 until your keyboard begs for mercy, because your gorgeous new CSS file is stubbornly refusing to load. The browser is faithfully serving the old, cached version, convinced nothing has changed. We need a way to tell the browser, definitively, “This is a new file. I mean it this time.”

25.4 JavaScript Bundling with js.Build and esbuild

Alright, let’s talk about making your JavaScript not suck. You’ve got a pile of modern JS—maybe some TypeScript, ES6 modules, fancy dependencies—and you need to serve it to a browser that still probably thinks let is a typo. This is where js.Build and its engine, esbuild, come in. Think of js.Build as Hugo’s direct line to one of the fastest, most no-nonsense bundlers on the planet. It doesn’t mess around with a million plugins; it just gets the job done, brutally efficiently.

25.3 CSS Processing: toCSS, PostCSS, and autoprefixer

Right, let’s talk about making your CSS less of a mess and more of a… well, a slightly more organized mess that actually works across browsers. Hugo gives us a fantastic toolkit for this, and if you’re not using it, you’re essentially writing your stylesheets with a rock and a chisel. We’re going to cover the three big hitters: toCSS, PostCSS, and the lifesaver known as autoprefixer. First, the basics. You don’t just throw a regular .css file in your assets directory and call it a day. Instead, you process it. This usually means you’ll create a file with a special extension, like styles.css or, my personal favorite, something.scss (even if you’re not using Sass!). Why? Because this tells Hugo’s asset pipeline, “Hey, I need you to do something to this file before you serve it.”

25.2 resources.Get and resources.GetRemote

Right, let’s talk about getting stuff. In Hugo, your content isn’t just the markdown files you lovingly craft; it’s also every image, PDF, CSS file, and JSON blob your site needs. This is where resources.Get and resources.GetRemote come in. Think of them as your two best friends for asset acquisition: one for the local stuff you’ve already committed to your project (resources.Get), and one for the brave new world of the internet (resources.GetRemote). They’re the foundation of Hugo Pipes, and once you get them, you can make this static site generator do backflips.

25.1 What Hugo Pipes Are: Processing assets/ Files

Right, let’s talk about Hugo Pipes. Forget what you’ve heard about static site generators just slapping together pre-made files. Hugo Pipes is the reason I can, with a straight face, call Hugo a modern frontend build tool that happens to output static HTML. It’s the engine under the hood that takes your raw, un-minified, un-processed assets and transforms them into the optimized, production-ready CSS and JS you actually want to serve.

21.8 Publishing a Theme: Hugo Themes Gallery Requirements

Right, so you’ve built this beautiful, clever Hugo theme. It’s a masterpiece of semantic HTML and clever partials. You, my friend, are an artist. But art locked in a closet is just hoarding. It’s time to unleash your creation upon the world via the Hugo Themes Gallery. This isn’t just a matter of pushing to GitHub and calling it a day. Hugo has a… let’s call it a particular set of requirements. They’re not difficult, but they are non-negotiable, and missing one will leave your theme languishing in “draft” status forever.

21.7 Making a Theme Configurable with Params

Right, so you’ve built a theme. It looks sharp. But here’s the problem: it’s a dictatorship, not a democracy. You’ve hard-coded the accent color to that specific shade of electric teal you’re so fond of, and the site title is set in stone. What if your user wants… gasp… maroon? We’re not barbarians. We need to hand over the reins, but in a controlled, sensible way. That’s where Hugo’s Params come in. Think of them as the control panel for your theme, letting the user tweak things without ever having to touch a line of Go template code.

21.6 Dark Mode Toggle Pattern

Right, so you want a dark mode toggle. Not just a little switch that winks an eye and hopes for the best, but a proper, persistent, system-respecting one. We’ve all seen the janky versions—the ones that flashbang you at 2 AM or forget your preference the moment you reload. We’re not building that. We’re building the one that does it right, because frankly, the user experience here is embarrassingly easy to screw up. Let’s get it right on the first try.

21.5 JavaScript: Bundling with esbuild via Hugo Pipes

Right, so you’ve decided to build a Hugo theme. Good for you. You’ve got your HTML templated, your CSS is piping hot, and now you need some actual, functioning JavaScript. You could just slap a <script src="main.js"> in your head and call it a day, but then you’d be serving a massive, un-minified, ES6+ mess to every browser, including a Nokia phone running Opera Mini. We’re better than that. Enter Hugo Pipes and esbuild. This is where Hugo stops being a simple static site generator and starts feeling like a full-fledged build tool. The beauty of it is that you don’t need a separate package.json, node_modules, or a sprawling Webpack configuration that requires a blood sacrifice to maintain. Hugo handles it all internally, and it’s brilliantly fast.

21.4 CSS Architecture: Vanilla CSS, Tailwind, and SCSS Integration

Right, let’s talk strategy. You’re about to build a theme, which means you’re making decisions that will haunt you—or bless you—for the entire project. The way you structure your CSS isn’t just about writing styles; it’s about writing maintainable styles that won’t make you want to set your computer on fire in six months. We have three main players here: the purity of Vanilla CSS, the utility-first speed of Tailwind, and the programmatic power of SCSS. The good news is, you don’t have to choose just one. The better news is, if you mix them wrong, you’ll create a monster. Let’s get it right.

21.3 Building the Base Template

Right. You’ve decided to build a theme from scratch. Good for you. This is where you stop being a tourist and start being a citizen. You’ll get your hands dirty, you’ll make mistakes, and you’ll learn more in the next ten minutes than you would from installing fifty pre-made themes. The base template is the bedrock. It’s the single most important file in your theme. Get this right, and everything else slots into place. Get it wrong, and you’ll be chasing weird bugs for weeks.

21.2 theme.toml: Theme Metadata

Right, let’s talk about the theme.toml file. This is your theme’s handshake, its business card, and its legally binding contract with Hugo, all rolled into one. Get this wrong, and Hugo will either throw a fit or, worse, build your theme with a whimper instead of a bang. It’s a TOML file because, well, the Hugo team made a choice. I don’t question the cosmic reasoning; I just write the config.

21.1 Theme Directory Structure: layouts, static, assets, i18n

Right, let’s get our hands dirty. You’re building a theme from scratch, which means you’re about to become intimately familiar with a very specific directory structure. This isn’t arbitrary bureaucracy; it’s Hugo’s contract with you. You put things in the right place, and Hugo, being a deeply opinionated but brilliant curmudgeon, knows exactly what to do with them. Break the contract, and things just… stop working. Let’s look at the four key directories you’ll be living in.

— joke —

...