Skip to content

mognog/EofNormalizer

Repository files navigation

EOF Normalizer β€” CLI, CI & Dev Utility

EOF Normalizer is a tiny Node.js CLI tool that normalises end‑of‑file newlines, converts CRLF β†’ LF, and removes unwanted trailing blank lines.

Whether you're cleaning up formatting inconsistencies, fixing line ending drift in mixed‑OS teams, or dealing with editors that add extra blank lines (such as the Cursor editor issue), this tool helps keep your codebase consistent.

If you've ever seen noisy diffs like "+1 blank line" or CI churn because of line ending drift, this tool is for you.


What it does

  • βœ… Converts Windows line endings CRLF (\r\n) β†’ LF (\n)
  • βœ… Ensures exactly one newline at EOF
  • βœ… Removes extra blank lines at end of file
  • βœ… Recursively scans a folder
  • βœ… Skips common directories (e.g. node_modules, .git, build outputs)
  • βœ… Respects .gitignore files (automatically skips binaries, generated files, etc.)
  • βœ… Supports dry‑run mode (preview without changes)
  • βœ… Works with any language (TS/JS/Python/Go/Rust/C#/C++/Pascal/Vue/etc.)

Why you might use it

  • 🧹 Clean diffs before commits (remove unwanted formatting changes)
  • 🚦 Enforce newline consistency in CI
  • πŸ” Mixed‑OS repos: Windows + macOS + Linux teams
  • 🧠 Fix editors that add extra blank lines (including Cursor editor)
  • πŸ› οΈ General formatting/normalisation utility for any project

Requirements

  • Node.js (recent version recommended)
  • No dependencies required (built-in Node APIs only)
    • Optional: Install ignore package for enhanced gitignore pattern matching:
      npm install ignore
      (Works fine without it, but the ignore package provides more accurate gitignore parsing)

Quick start

1) Put the script in your repo

You only need:

eof-normalizer.js

2) Preview changes (recommended)

node eof-normalizer.js --dry-run

3) Apply changes

node eof-normalizer.js

Alternative: Script in a different location

If you put the script in a different directory (e.g., tools/ or a shared location), you can specify the directory to scan:

# Script in tools/, scanning src/ directory
node tools/eof-normalizer.js --dir src --dry-run

# Script in parent directory, scanning current directory
node ../eof-normalizer.js --dir . --dry-run

# Using a .gitignore from the scanned directory
node tools/eof-normalizer.js --dir src --gitignore src/.gitignore --dry-run

# Paths with spaces must be quoted
node "C:/My Tools/eof-normalizer.js" --dir "My Project/src" --dry-run

Note: Replace tools/eof-normalizer.js with your script path, src with your target directory, and src/.gitignore with the path to the .gitignore file in the directory you're scanning.


CLI usage

node eof-normalizer.js [options]

Options

Option Description
--dir <path> Directory to scan (default: current directory)
--ext <list> File extensions to process (e.g. .ts,.js,.py)
--skip <list> Folders to skip (comma-separated)
--gitignore <file> Path to .gitignore file (default: ./.gitignore if exists)
--no-gitignore Disable gitignore filtering
--include-no-ext Include files with no file extension (e.g., LICENSE, README)
--dry-run Preview changes without modifying files
--quiet Minimal output (useful for CI)
--help Show help

Examples

All languages (default - now includes C#, C++, Go, Rust, Pascal, Vue, and more)

node eof-normalizer.js

TypeScript / JavaScript / Vue / Angular

node eof-normalizer.js --dir src --ext .ts,.tsx,.js,.jsx,.vue

C# / .NET

node eof-normalizer.js --dir . --ext .cs,.csx

C / C++

node eof-normalizer.js --dir . --ext .c,.cpp,.h,.hpp,.hxx,.cxx,.cc

Go

node eof-normalizer.js --dir . --ext .go

Rust

node eof-normalizer.js --dir . --ext .rs

Pascal / Delphi

node eof-normalizer.js --dir . --ext .pas,.pp,.p

Python / Markdown / YAML

node eof-normalizer.js --ext .py,.md,.yml,.yaml,.json

Using with .gitignore (recommended)

# Automatically uses ./.gitignore if it exists
node eof-normalizer.js

# Use a custom gitignore file
node eof-normalizer.js --gitignore .myignore

# Disable gitignore filtering
node eof-normalizer.js --no-gitignore

Note about gitignore support: The .gitignore feature was added to help distinguish source files from generated files, binaries, and build artifacts across all project types (C#, C++, Go, Rust, Python, JavaScript, etc.). Since .gitignore files are language-agnostic and commonly used to exclude non-source files, this provides a universal way to identify which files should be normalized.

The built-in parser handles most common patterns, but has some limitations (particularly with complex negation patterns). If you need more accurate gitignore matching, you can install the optional ignore package:

npm install ignore

The tool will automatically detect and use the ignore package if it's available, providing more robust pattern matching that closely follows git's behavior.

Including files with no extension

By default, files without extensions (like LICENSE, README, Dockerfile) are not processed. Use --include-no-ext to include them:

# Include files with no extension (e.g., LICENSE, README)
node eof-normalizer.js --include-no-ext --dry-run

Sample output:

Starting EOF normalization...

πŸ” DRY RUN MODE - No files will be modified

Using .gitignore rules (built-in parser): C:\Dev\MyProject\.gitignore

Scanning directories...
  C:\Dev\MyProject: found 8 files

Total files to scan: 8

Processing files...

--- Results ---

Would fix 1 file(s):
  πŸ” LICENSE (1089 β†’ 1056 bytes, -6 lines removed)

Would fix 1 of 8 files.

Example: Scanning a different directory with custom gitignore

When scanning a directory outside your current location, you can specify both the target directory and its .gitignore file:

node eof-normalizer.js --dir "C:\Dev\MyProject" --gitignore "C:\Dev\MyProject\.gitignore" --dry-run

Sample output:

Starting EOF normalization...

πŸ” DRY RUN MODE - No files will be modified

Using .gitignore rules (built-in parser): C:\Dev\MyProject\.gitignore

Scanning directories...
  C:\Dev\MyProject: found 2761 files

Total files to scan: 2761

Processing files...

--- Results ---

Would fix 100 file(s):
  πŸ” C:\Dev\MyProject\.github\renovate.json (2324 β†’ 2257 bytes, +1 line)
  πŸ” C:\Dev\MyProject\.github\scripts\check-schema-drift.sh (1765 β†’ 1764 bytes, -1 line removed)
  πŸ” C:\Dev\MyProject\.github\workflows\ci.yml (14799 β†’ 14797 bytes, -2 lines removed)
  πŸ” C:\Dev\MyProject\.secrets\dev\.gitkeep (56 β†’ 53 bytes, -1 line removed)
  πŸ” C:\Dev\MyProject\.secrets\prod\.gitkeep (56 β†’ 53 bytes, -1 line removed)
  πŸ” C:\Dev\MyProject\.secrets\README.md (2015 β†’ 2014 bytes, -1 line removed)
  πŸ” C:\Dev\MyProject\.serena\.gitignore (8 β†’ 7 bytes)
  ... (93 more files)

Would fix 100 of 2761 files.

Basic workflow

First, preview what would be changed:

node eof-normalizer.js --dry-run

After reviewing the results, apply the changes:

node eof-normalizer.js

Optional integrations (advanced)

These are examples only β€” how you use or integrate this tool is entirely your choice.

Pre-commit hook (example)

You've got two common approaches:

Option A: Simple Git hook (no dependencies)

Create .git/hooks/pre-commit (no file extension) with:

#!/usr/bin/env bash
set -euo pipefail

# Normalise files
node eof-normalizer.js --quiet

# Fail commit if anything changed (forces you to stage the changes)
if ! git diff --quiet; then
  echo "EOF Normalizer updated files. Please review and stage changes, then re-commit."
  git status --porcelain
  exit 1
fi

Make it executable:

chmod +x .git/hooks/pre-commit

Notes

  • This runs locally only (hooks are not committed by default).
  • It prevents β€œhidden” formatting changes slipping into commits.

Option B: Husky (shareable hooks for Node projects)

Install Husky:

npm i -D husky
npx husky init

Add a pre-commit hook:

npx husky add .husky/pre-commit "node eof-normalizer.js --quiet && git diff --quiet || (echo 'EOF Normalizer updated files. Stage changes and retry.' && git status --porcelain && exit 1)"

Now the hook is committed and shared with the team.

GitHub Actions (CI snippet)

This workflow fails CI if EOF Normalizer would change any files.

Create .github/workflows/eof-normalizer.yml:

name: EOF Normalizer

on:
  pull_request:
  push:
    branches: [ main, master ]

jobs:
  eof-normalizer:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Run EOF Normalizer
        run: |
          node eof-normalizer.js --quiet

      - name: Fail if files changed
        run: |
          git diff --exit-code

How it works

  • Runs the normalizer
  • If it modified anything, git diff --exit-code returns non‑zero and CI fails
  • The PR author then runs the tool locally and commits the normalised changes

Optional: Integrating EofNormalizer into a Cursor MCP Server

If you use Cursor and want to trigger EOF Normalizer as part of an MCP workflow, you can run it via an MCP server. This allows Cursor to invoke the existing CLI script programmatically.

Note: This integration has not been formally tested in this repository. The example below shows the intended and theoretical approach based on Cursor's MCP model and is provided as a starting point rather than a supported feature.

For the latest and most accurate MCP implementation details, see the official Cursor documentation:

πŸ‘‰ https://cursor.com/docs/context/mcp


Minimal MCP Server Wrapper (Conceptual)

You would typically create a small MCP server wrapper that shells out to the existing CLI script. For example:

mcp/
  β”œβ”€β”€ package.json
  └── mcp-server.js

The MCP server would:

  • Accept requests from Cursor
  • Execute node eof-normalizer.js with the supplied arguments
  • Return a summary of the result

This repository intentionally does not include a full MCP server implementation, as MCP APIs and best practices may evolve. Refer to the Cursor docs for concrete implementation guidance.


Example mcp.json Configuration (Illustrative)

The following example demonstrates how EOF Normalizer could be registered as an MCP server in Cursor. Paths are anonymised and must be adjusted to match your local setup:

{
  "mcpServers": {
    "eof-normalizer": {
      "command": "node",
      "args": [
        "/PATH/TO/mcp/mcp-server.js"
      ],
      "env": {
        "NODE_ENV": "production",
        "EOF_NORMALIZER_SCRIPT": "/PATH/TO/EofNormalizer/eof-normalizer.js"
      }
    }
  }
}

This configuration is illustrative only and has not been validated end-to-end.


Why include this example?

  • Shows how the tool fits into modern Cursor workflows
  • Provides a concrete reference without over-specifying implementation
  • Leaves flexibility for users to adapt the approach to their own MCP setup

Support expectations

MCP integration is community-driven. If you build and refine an MCP wrapper for this tool, contributions or examples are welcome β€” but this repository does not aim to provide official MCP support.


Development

If you're modifying or contributing to this script, follow these steps to ensure code quality:

Run linting

Check for linting errors:

pnpm lint

Or with npm:

npm run lint

Run unit tests

Run all unit tests:

node eof-normalizer.test.js

Before committing

Before submitting changes, ensure:

  • βœ… All linting checks pass (pnpm lint)
  • βœ… All unit tests pass (node eof-normalizer.test.js)
  • βœ… New features include corresponding unit tests
  • βœ… No linter errors are introduced

Repo contents

File Purpose
eof-normalizer.js The CLI utility (most users only need this)
eof-normalizer.test.js Tests
README.md Documentation
LICENSE MIT
package.json Dev tooling
eslint.config.js Lint config
.cursor/rules/javascript-coding-standards.mdc Cursor IDE coding standards rules
.gitignore Git hygiene

Safety

⚠️ This tool modifies files.

  • Use --dry-run first
  • Commit / back up before applying changes
  • Provided "as is" under the MIT license (no warranty)

Support & maintenance expectations

This project is provided as-is as a free utility.

  • The maintainer may not be able to reproduce or fix all platform-specific issues
  • Edge cases are best handled via community discussion and pull requests
  • Contributions are welcome and encouraged

If you need guaranteed support, consider maintaining a fork.


Pull requests

PRs are welcome, but may not be merged immediately (or at all).

This project prioritises simplicity and low maintenance. Changes that add complexity, dependencies, or platform-specific behaviour may be declined.


SEO keywords (for discoverability)

Cursor blank lines, Cursor adds blank lines, EOF newline, end-of-file newline, newline normalizer, CRLF to LF, line endings, trailing whitespace, trailing blank lines, formatting tool, pre-commit hook, GitHub Actions CI check.


License

MIT β€” see LICENSE.

About

Normalises end-of-file newlines (EOF) across text files to enforce consistent formatting.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published