About the Baseline Audit Linter Project 🛠️ 💡 What Inspired Me My biggest inspiration for the Baseline Audit Linter came from seeing how easily developers, myself included, can overlook simple performance and security best practices, even when using well-established, "Baseline" web features. I realized that while generic linters catch syntax errors, they don't audit against evolving web standards.
I was motivated to use the official web-features dataset—the source of truth for the web platform—to create a smart linter. My goal was simple: build a tool that guides developers toward Baseline excellence, specifically by flagging easy-to-miss performance pitfalls (like unthrottled events) and security risks (like insecure links).
🏗️ How I Built It As a solo developer, I focused on building a flexible, two-part system to tackle different types of code:
Setting the Foundation: I used a simple monorepo structure with local file: dependencies to link my two custom plugins (eslint-plugin-audit and stylelint-plugin-audit) to a dedicated test-app. This setup was crucial for rapid development and self-testing.
Developing the ESLint Rule (JavaScript): I focused on performance. I built the @baseline/audit/no-unthrottled-event-listeners rule to analyze the JavaScript Abstract Syntax Tree (AST). It specifically targets high-frequency events (like 'scroll' and 'mousemove') within addEventListener calls. If my code detected a high-frequency event without an obvious throttle or debounce wrapper, it flagged it with a clear warning.
Developing the Stylelint Rule (CSS): I focused on security and data integration. I made sure to import the web-features module correctly. The rule, @baseline/audit/require-rel-noopener-noreferrer, searches the CSS AST for the insecure attribute selector a[target="_blank"] only on elements confirmed as "high" Baseline features, enforcing the necessary rel attributes to prevent tabnabbing.
🧠 What I Learned Along the Way This project was a deep dive into advanced tooling:
Linter Architecture: I gained invaluable practical experience traversing the JavaScript ESTree AST and the CSS PostCSS AST. Understanding how to pinpoint specific code patterns within these complex trees was my biggest technical takeaway.
Strictness of ESLint 9.x: I learned a tough lesson about the strictness of the new ESLint API. The persistent TypeError: context.report() called with a suggest option without a fix function taught me the absolute necessity of the meta.fixable: "code" flag (and ultimately, why removing suggest was the most stable hackathon fix).
Data Handling: Correctly importing and parsing the nested data structure of the web-features package was surprisingly challenging. Debugging the initial TypeError showed me how vital it is to confirm the exact export structure of third-party data packages.
🚧 Challenges I Overcame My primary challenges revolved around stability and dependency linking, amplified by working solo:
The Persistent ESLint Error: This was the biggest time sink. Even with the fixable flag, the linter kept crashing, which turned into a frustrating debugging loop. I had to force the Node process to recognize the updated local code, proving that local plugin dependencies can be tricky.
Stylelint Data Access: The initial web-features import failing due to a nested export structure (.features) was a roadblock. I solved this by carefully inspecting the package output and adjusting my import logic to be more robust.
Authentication Nightmare: While not coding-related, resolving the Git 403 Permission Denied error required a hard stop to clean out stale Windows credentials. This was a necessary detour to ensure the final project could be publicly accessible on GitHub.
Log in or sign up for Devpost to join the conversation.