Skip to main content

39 posts tagged with "webpack"

The webpack module bundler.

View All Tags

Migrating ts-loader to TypeScript 7.1 with AI

· 10 min read
John Reilly
OSS Engineer - TypeScript, Azure, React, Node.js, .NET

TypeScript is now the most popular programming language. Incredible. I was an early adopter back in 2012 and have since worked on a number of open source projects in the TypeScript ecosystem.

One of them is ts-loader, which integrates TypeScript the language with webpack; which is used to build web applications. You might be surprised to know ts-loader has 54 million downloads a month. I know I am. I'll be honest - I'm using Vite far more than I use webpack these days, but there are still a lot of people using webpack and ts-loader. And I intend to continue to support them.

Just lately, TypeScript was ported from being written in TypeScript to being written in Go. The reason being for performance - make TypeScript tooling 10x faster. TypeScript 7 is written in Go.

This has implications for ts-loader as it is built on APIs the TypeScript library exposed. But those APIs were not ported to TypeScript 7. New ones are being written, recently I heard that the first emit APIs had landed in the nightly version of TypeScript.

Porting with AI

I would like ts-loader to be able to support TypeScript 7.1 when it comes. This will be the first Go built version of TypeScript which ships with an API. But I'm quite lazy and I have / had very little interest in doing the migration manually.

Instead I intended to use my GitHub Copilot and Claude licenses. What follows is a diary of the port. The things I did, what was useful / wasn't, the principles followed etc.

In fact, let's start there: principles:

  1. I wanted AI to do most of the work
  2. I reserved the right to not port all features The codebase of ts-loader is more than 10 years old, and I was pretty sure we didn't need everything that we'd accreted over the years.
  3. I still intended to support webpack 4 and 5 unless there was a compelling reason not to.
  4. The minimum supported Node version for ts-loader right now is 12. That has long been end-of-life and so it feels appropriate to bump the minimum supported version of Node to something higher; probably 22.

The initial port

I thought I'd start off by firing this prompt at GitHub Copilot to get it to do some analysis and put that on an existing issue that discussed TypeScript 7:

Screenshot of initial port prompt which reads "following the addition of the emit API in this pull request: https://github.com/microsoft/typescript-go/pull/4699 please determine what this potentially means for ts-loader See context here: https://github.com/TypeStrong/ts-loader/issues/1671#issuecomment-4937039062 Please add a comment to that PR with your findings"

It did some decent analysis and claimed it had added the analysis to the issue. It had not:

Screenshot of me telling GitHub Copilot it had not added a comment

It told me it had added the comment again, and it had not. So I challenged it once more.

Screenshot of me telling GitHub Copilot it had not added a comment again

And then it happened again, so I tried sarcasm.

Screenshot of me telling GitHub Copilot it had not added a comment again sarcastically

At this point I realised I was getting distracted.

The analysis was decent and so I asked it to PR it, which it did. One thing I have generally learned about AI is that it loves backwards compatibility. And this PR was that shaped; it was looking to support both the old and new APIs. This seemed complicated. After some fiddling and thinking, I figured I'd rather just support the new API and drop support for the old one.

Dropping support for the old API

So I asked it to go again, but this time to drop support for the old API:

Screenshot of second prompt which reads "https://github.com/TypeStrong/ts-loader/pull/1703 is a good first attempt at implementing the new tsgo API support but is too complicated because it maintains existing support as well. Please try implementing new API support alone instead. Drop existing support. We would like execution-tests to pass - comparison-tests need not pass initially. Use npm@7.1.0-dev.20260725.1 or newer for implementing. See https://github.com/microsoft/typescript-go/pull/4699 for additional context."

The PR it came up with was decent: https://github.com/TypeStrong/ts-loader/pull/1704 - this became the basis for the port.

A little bit of context; ts-loader has two test packs. The comparison test pack are tests that compare the output of ts-loader with known outputs; they're effectively snapshot tests. The execution test pack are tests that run the output of ts-loader and check that it behaves as expected. The execution tests are more reliable than the comparison tests and can be run against multiple versions of webpack and TypeScript. The comparison tests are only run against a single version of webpack / TypeScript, and are somewhat brittle. But they're useful for checking error states.

With minimal assistance from me, it got the execution tests passing. This was an awesome start.

Getting the comparison tests passing

At this point I had a version of ts-loader that supported TypeScript 7.1. But the comparison tests were failing. It was also at this point that my GitHub Copilot tokens ran out; so I switched to using Claude.

From here on out my workflow became pretty simple. I would point Claude at a failing comparison test, and ask it to fix it:

screenshot of a terminal that reads "please fix yarn comparison-tests --single-test basic"

A lesson I learned early on, was to start with the simpler tests and work up to the more complex ones. I'd initially not used that approach and found that Claude was "fixing" the more complex tests in a way that broke other ones. So I started with the simpler tests and worked my way up.

As I was doing this I also decided to drop some functionality that had existing in ts-loader for a while. For instance, Happypack is archived and no longer maintained. So I decided to drop support for it in ts-loader.

Giving Claude a Windows avatar in GitHub Actions

In a couple of days of fiddling in my spare time, we got to the point of fully passing execution tests and comparison tests. However, they were only passing on Linux and Mac. The Windows tests were failing. I do have access to Windows machines, but my boys have hijacked them for gaming. So I pondered if Claude could somehow make use of Windows machines running in GitHub Actions.

The answer was yes. If it had access to the GitHub CLI and we created a GitHub Actions workflow that ran on Windows, it could use the CLI to run commands on the Windows runner. So we created a workflow that ran on Windows: https://github.com/TypeStrong/ts-loader/pull/1705

With that in place, Claude was able to run the comparison tests on Windows and fix the issues. It was a bit of a slow process, but it worked. After some time, we got to the point where all tests were passing on all platforms. This made me very happy!

Also, the boys were pleased as I didn't have to use their machines for testing. I say "their machines" - technically these are my laptops that they borrowed and never gave back. But I digress.

Goodbye chalk, hello picocolors

One of the benefits of moving to Node 22 as a minimum is that we can use the built-in styleText API instead of the chalk library. There's lovely documentation on how to do that here: https://e18e.dev/docs/replacements/chalk.html#styletext-native and it was my initial plan to use it.

However, there was some odd behaviour which meant that output differed between platforms. After trying to work around that and deciding it was too complex, we switched to using picocolors instead. This still reduces the dependency weight of ts-loader and it makes ts-loader faster. It's a win-win.

Goodbye typescript-eslint, hello oxlint-tsgolint

At the moment ts-loader uses typescript-eslint to lint the code. However, that library is not compatible with TypeScript 7.1. So we have switched to using oxlint-tsgolint, which is a new linter that supports TypeScript 7.1. It is also faster than typescript-eslint. You can read more about it here: https://oxc.rs/blog/2026-07-22-type-aware-linting-stable

I feel slightly torn on this one as I've long used and liked typescript-eslint. I may return to it when it becomes compatible, but I couldn't take seeing the red cross in the GitHub Actions workflow for ts-loader any longer.

What is still to come?

Around this time I got excited and posted on Bluesky about the progress. Andrew Branch, one of the TypeScript team members that has worked on the API, replied to my post:

screenshot of Andrew Branch's post on Bluesky saying "Awesome! FYI, transpileModule is coming soon too"

This was a good reminder that what we had done was not the end of the story. If you're a ts-loader user then you might be familiar with the transpileOnly option. This allows you to transpile TypeScript code without type checking. The port as it stands does not properly support this option as the APIs aren't yet there. When they land, we'll look again.

There's also other features that are not yet supported. For instance, custom transformers and project references. If / when APIs appear for these, we'll look to support them in ts-loader.

How good is the code?

It's not too bad. I've deliberately not looked too hard at it as yet. It made a few peculiar choices that I have addressed, but overall it is pretty good. I'm planning to have a proper look at it further down the line.

One surprising thing is that it has added a lot of comments to the code.

screenshot of code with a lot of comments

I don't like it. However, for now I'm leaving the comments in place, on the assumption that it helps Claude understand the code better. I will remove them before we merge.

When do we ship?

Well, not before the API has been released, which is likely to be later this year. But when it is, we will ship a new version of ts-loader that supports TypeScript 7.1. It's probably going to be v10 of ts-loader, so we can drop support for the old TypeScript API, Node 12, Happypack etc.

For now, if you want to try out the port, the pull request is up and can be found here: https://github.com/TypeStrong/ts-loader/pull/1704

Open source: Who is your DR?

· 4 min read
John Reilly
OSS Engineer - TypeScript, Azure, React, Node.js, .NET

It's been a rough year. In March I was diagnosed with a serious illness. I've written about my diagnosis on my personal blog and I won't repeat what I said there here. But the TL;DR is this: I was told that without treatment things looked bleak. Happily, there was a follow up sentence; there is treatment and I am now having it. Whilst nothing is guaranteed, the doctors have used phrases like "cautious optimism". Time will tell what happens, but I am hopeful that I will be around for a while yet.

However, this has made me think about the future of the open source projects I maintain. If I cannot maintain them anymore, what happens? That's what the title of this blog post means; what, or who, is the DR (that's Disaster Recovery not Daniel Rosenwasser) for my open source projects? Maybe there's a better term than "Disaster Recovery". Having worked on various systems over the years, I've often been involved in disaster recovery planning for them. It always comes down to answering this question: what takes over when everything goes wrong?

title image that reads "Open source: Who is your DR?"

ts-loader goes webpack 4... again!

· 6 min read
John Reilly
OSS Engineer - TypeScript, Azure, React, Node.js, .NET

Back in 2021 I published a post called ts-loader goes webpack 5; a big exciting post about how ts-loader was upgraded to directly support webpack 5, and drop support for webpack 4 in v9 of ts-loader.

For reasons which I'll get into shortly, as of v9.6.0, ts-loader now supports both webpack 5 (as it did already) and webpack 4. So if you're a webpack 4 user, you can now use ts-loader@9, rather than using ts-loader@8.

title image that reads "ts-loader goes webpack 4... again!" with TypeScript and webpack logos

Overview of webpack, a JavaScript bundler

· 28 min read
John Reilly
OSS Engineer - TypeScript, Azure, React, Node.js, .NET

If you're a JavaScript developer, you've probably heard of webpack. It's a JavaScript bundler that helps you bundle your code into a single file. It's a great tool for optimizing your code and improving performance. This article will give you an overview of webpack, its history and how it works.

It'll be a little different than your typical "what is webpack?" article, in that I write this as the maintainer of ts-loader, a loader used for integrating TypeScript with webpack. I've worked in the webpack ecosystem for some years now and I'll share some of my experiences with you. I'll go through a little history around bundling, and try to understand why webpack came to be such a popular choice.

title image reading "Overview of webpack, a JavaScript bundler" with the webpack logo

Blog Archive for Docusaurus

· 6 min read
John Reilly
OSS Engineer - TypeScript, Azure, React, Node.js, .NET

Docusaurus doesn't ship with "blog archive" functionality. By which I mean, something that allows you to look at an overview of your historic blog posts. It turns out it is fairly straightforward to implement your own. This post does just that.

Docusaurus blog archive

Updated 2021-09-01

As of v2.0.0-beta.6, Docusauras does ship with blog archive functionality that lives at the archive route. This is down to the work of Gabriel Csapo in this PR.

If you'd like to know how to build your own, read on... But you may not need to!

ts-loader goes webpack 5

· 8 min read
John Reilly
OSS Engineer - TypeScript, Azure, React, Node.js, .NET

ts-loader has just released v9.0.0. This post goes through what this release is all about, and what it took to ship this version. For intrigue, it includes a brief scamper into my mental health along the way. Some upgrades go smoothly - this one had some hiccups. But we'll get into that.

hello world bicep

TypeScript and webpack: Watch It

· 3 min read
John Reilly
OSS Engineer - TypeScript, Azure, React, Node.js, .NET

All I ask for is a compiler and a tight feedback loop. Narrowing the gap between making a change to a program and seeing the effect of that is a productivity boon. The TypeScript team are wise cats and dig this. They've taken strides to improve the developer experience of TypeScript users by introducing a "watch" API which can be leveraged by other tools. To quote the docs:

It's Not Dead 2: mobx-react-devtools and the undead

· 3 min read
John Reilly
OSS Engineer - TypeScript, Azure, React, Node.js, .NET

I spent today digging through our webpack 4 config trying to work out why a production bundle contained code like this:

if("production"!==e.env.NODE_ENV){//...

My expectation was that with webpack 4 and 'mode': 'production' this meant that behind the scenes all process.env.NODE_ENV statements should be converted to 'production'. Subsequently Uglify would automatically get its groove on with the resulting if("production"!=="production") ... and et voilà!... Strip the dead code.

It seemed that was not the case. I was seeing (regrettably) undead code. And who here actually likes the undead?

It's Not Dead: webpack and dead code elimination limitations

· 3 min read
John Reilly
OSS Engineer - TypeScript, Azure, React, Node.js, .NET

webpack has long supported the notion of dead code elimination. webpack facilitates this through use of the DefinePlugin. The compile time value of process.env.NODE_ENV is set either to 'production' or something else. If it's set to 'production' then some dead code hackery can happen. Libraries like React make use of this to serve up different, and crucially smaller, production builds.