惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Jina AI
Jina AI
大猫的无限游戏
大猫的无限游戏
T
Tailwind CSS Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
WordPress大学
WordPress大学
Last Week in AI
Last Week in AI
Hugging Face - Blog
Hugging Face - Blog
阮一峰的网络日志
阮一峰的网络日志
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
人人都是产品经理
人人都是产品经理
V
V2EX
博客园 - 叶小钗
雷峰网
雷峰网
小众软件
小众软件
量子位
V
Visual Studio Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
The GitHub Blog
The GitHub Blog
Martin Fowler
Martin Fowler
G
Google Developers Blog
博客园_首页
博客园 - Franky
有赞技术团队
有赞技术团队
宝玉的分享
宝玉的分享

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Your repo has whitespace problems you can't see — I built...
benjamin · 2026-06-20 · via DEV Community
Cover image for Your repo has whitespace problems you can't see — I built a zero-dep CLI that finds and fixes them all

benjamin

Whitespace problems are the ones you can't see until they bite. A pull request where half the "changes" are trailing-space diffs. A shell script that breaks in CI because someone's editor saved it CRLF. A .env with a UTF-8 BOM that makes the first variable name mysteriously not match. A file with no final newline that turns one-line changes into two-line diffs forever.

None of it shows up on screen. All of it shows up in git blame.

Today, catching this takes three or four tools stitched together — and I got tired of that, so I built wssweep: one zero-config command that finds all the common whitespace smells and, with --fix, cleans them in place.

$ npx wssweep

  src/app.js  (2)
      14: trailing-whitespace  trailing whitespace
       -  missing-final-newline  no newline at end of file
  config.yml  (1)
       -  mixed-eol  mixed line endings (CRLF×3, LF×1)

  ✖ 3 whitespace issues in 2 files  (mixed-eol=1, missing-final-newline=1, trailing-whitespace=1)

$ npx wssweep --fix     # clean them

It checks seven things: trailing whitespace, mixed CRLF/LF line endings, lone CRs, a missing final newline, extra trailing blank lines, a UTF-8 BOM, and tabs mixed with spaces in one indent. Non-zero exit on findings, so it's a CI gate. pip install wssweep gets the same tool in Python — byte-for-byte identical output and fixes.

Why not editorconfig-checker / pre-commit / prettier?

Because each does part of it:

  • editorconfig-checker reports — but you have to author an .editorconfig first, and it can't fix anything.
  • pre-commit's trailing-whitespace / end-of-file-fixer / mixed-line-ending hooks do fix, but only inside the pre-commit framework, and they're three separate hooks. Nobody runs them ad-hoc on a fresh checkout.
  • prettier fixes whitespace only as a side effect of reformatting all your code, and won't touch files it can't parse.
  • dos2unix does line endings and nothing else.

wssweep is the one npx/pip command, no config and no framework, that does the whole set at once and drops into any CI regardless of toolchain.

The opinions that make it zero-config

A whitespace tool with no config has to make the right calls by default, or it's noise:

  • A consistently-CRLF file is fine — only a file mixing CRLF and LF is flagged. .bat/.cmd even keep CRLF when fixed, so it never breaks a Windows script.
  • In Markdown, a line ending in exactly two spaces is a hard line break — semantically meaningful — so the trailing-whitespace check is skipped in .md. (BOM, final-newline, and EOL checks still apply.)
  • mixed-indentation is report-only. Auto-converting tabs↔spaces needs a tab-width guess that can silently wreck alignment, so wssweep tells you and leaves it.

The fun part: --fix that two languages agree on, to the byte

I wanted a Node build and a Python build that produce identical reports and write identical bytes on --fix. Whitespace is the worst possible domain for that, because every shortcut leaks platform behavior:

  • str.splitlines() in Python splits on VT, FF, NEL, U+2028, U+2029 too — it would invent phantom lines a split(/\r\n|\r|\n/) never sees. Forbidden.
  • \s matches different things in JS and Python (NBSP, the BOM char, …). So "whitespace" here means exactly [ \t] — explicit, never \s.
  • Python's text-mode file IO silently translates \r\n\n on read and \nos.linesep on write — it would rewrite the very bytes the tool inspects. So everything is raw bytes, and the scan runs on a latin-1 (byte-faithful) view where every byte maps 1:1 to a code point.
  • --fix builds the corrected bytes, compares to the original, and writes only if they differ — atomically (temp file + rename), preserving the file mode, never touching a binary or skipped file. And it's idempotent: run it twice, the second run does nothing.

A differential test fixes two identical trees with the two builds and cmps every file — zero differing bytes.

Install

npx wssweep --fix      # Node, zero deps
pip install wssweep    # Python, zero deps, identical fixes

MIT licensed, both builds open source:

Run npx wssweep on your current project. How many trailing-whitespace lines and missing newlines is it hiding? (Mine's never zero.)