Table of Contents
1. The problem: "works on my Mac"
CRXJS is an open-source Vite plugin with 3.9k+ GitHub stars that makes building extensions for Chrome-based browsers feel like building a modern web app. It handles HMR, manifest parsing, content scripts, and the entire build pipeline.
But like many open-source projects born on macOS and Linux, it had a blind spot: Windows. The CI ran only on Ubuntu. Tests were never validated on Windows. Path handling assumed forward slashes. File copying used Unix-specific behaviors. Snapshot tests captured platform-dependent output.
For the significant portion of developers who work on Windows — and for enterprise teams where Windows is the mandated platform — this meant dealing with cryptic build failures, broken HMR, and missing public files with no clear path to fixing them.
2. Making CRXJS build and test on Windows
We have been developing Chrome extensions on Windows since 2021 using CRXJS. We knew where the bugs were because we hit them daily. When we became contributors to the project, making it work cross-platform was the first thing we tackled.
The approach was straightforward: add Windows to the CI matrix, run the entire test suite, and fix everything that fails. The reality was more complex — the flagship PR ( #1008 ) ended up touching 45 files with 6,110 additions and 3,926 deletions.
3. What actually broke (and why)
Windows is not just "Linux with a different UI." The file system, path separators, line endings, and file locking behavior are fundamentally different. Here is what we found and fixed:
Public files not being copied
Static assets like .ico, .svg, and .js files in the public directory were not being copied to the build output on Windows. The file copy logic used path patterns that worked on Unix but failed silently on Windows. We first identified this in 2024 and finally fixed it as part of the Windows CI work.
HMR not triggering on Windows
Vite's HMR (Hot Module Replacement) relies on file system events to detect changes. On Windows, fs.copy() alone does not always update the file's modified timestamp the way HMR expects. We had to modify the code to explicitly touch the file (update its modified date) after copying, ensuring HMR picks up changes reliably.
Path separator differences
Windows uses backslashes (\) while Unix uses forward slashes (/). Any path comparison, file resolution, or manifest entry that hard-coded forward slashes broke on Windows. We normalized path handling across the codebase.
Platform-sensitive snapshots
Svelte's add_location() function includes character positions that depend on line endings. Windows uses \r\n (2 bytes) while Unix uses \n (1 byte), shifting character positions and making snapshot tests fail. We implemented platform-agnostic snapshot scrubbing and fixed base64 sourcemap placeholders that were accidentally committed.
CI timeout issues
Windows CI runners are slower than Ubuntu. E2E tests that ran fine on Ubuntu hit timeout limits on Windows. The existing Ubuntu e2e tests were also flaky due to timeout issues, making it harder to verify the Windows PR. We increased timeouts, stabilized the Ubuntu tests, and added configurable timeout limits across all CI jobs.
4. The CI/CD pipeline we built
Beyond the Windows fix, we restructured the entire CI/CD pipeline for CRXJS. Here is what the pipeline looks like now:
Cross-platform matrix
Every PR and push to main runs tests on both Ubuntu and Windows. The matrix ensures that platform-specific regressions are caught immediately, not months later when a Windows user reports a bug.
Multi-Vite version testing
We consolidated separate workflows into a single matrix-based CI that tests across Vite 3, 6, 7, and 8 ( #1093). One workflow definition, full version coverage. When Vite 8 beta dropped, we had CI support ready on day one ( #1096).
Timeout guards
CI jobs now have configurable timeout limits ( #1106). No more stuck jobs burning CI minutes. If a test hangs, it fails fast with a clear error instead of silently consuming resources.
Automated release notifications
Every npm publish from the changeset pipeline triggers a Discord notification ( #1094). The community knows about new releases instantly.
CI on CI changes
We added a trigger that runs the full build when the CI workflow definition itself changes ( #1025). CI changes are code changes — they deserve the same validation.
5. 27 pull requests: the full contribution list
Our contributions to CRXJS go beyond CI/CD. Here are the key PRs, followed by the complete list:
Key contributions
feat: make the project build and test on windows
The flagship PR. Added Windows CI matrix, fixed path handling across the codebase, resolved platform-sensitive snapshot differences (Svelte line endings), fixed public file copying on Windows, and made HMR work correctly with Windows file system behavior.
Unlocked Windows development for the entire CRXJS community
refactor: consolidate Vite version tests into matrix-based CI
Replaced duplicated CI workflows with a single matrix-based pipeline testing across Vite 3, 6, 7, and 8. Reduced CI config complexity while increasing coverage.
One workflow definition covers all Vite versions
feat: add Vite 8.0.0-beta.7 support and CI testing
Added Vite 8 beta support to CRXJS before the official release, ensuring day-one compatibility for extension developers.
CRXJS users get Vite 8 support from day one
ci: add timeout limits to CI jobs
Added configurable timeout limits to prevent CI jobs from hanging indefinitely. A small change with big impact on CI reliability and cost.
No more stuck CI jobs burning minutes
feat: add Discord notifications after changeset publish
Automated release notifications to the CRXJS Discord community. Every npm publish now triggers a notification so developers know when to upgrade.
Community knows about releases instantly
feat: make crxjs development easier by providing dev setup
Streamlined the contributor onboarding experience with dev container configs and setup scripts. Lowering the barrier for new contributors.
New contributors can start coding in minutes
All 27 pull requests
6. What this means for your team
This is not just an open-source contribution story. It directly reflects how we approach DevOps for our clients:
Cross-platform by default
If your team uses Windows, your extension development pipeline works on Windows. No workarounds, no "just use WSL," no second-class experience. We have done the hard work of making it cross-platform at the tool level.
CI/CD that catches real bugs
Matrix-based CI testing across platforms and Vite versions means we catch regressions before they reach your extension users. The same approach we built for CRXJS is what we use for client projects.
Deep tooling knowledge
When you hire a team that contributes to the build tools they use, you get a team that can debug at every level of the stack. Not just "the extension does not work" — but exactly why it does not work, down to the Vite plugin internals, the file system behavior, and the CI runner configuration.
We do not just use open-source tools. We improve them. That same engineering discipline is what we bring to every client project.
Need a team that understands the full stack?
From Vite plugin internals to cross-platform CI/CD — we build browser extensions with the same engineering rigor we bring to open-source. Let us build yours.
