From 50a3251399088bb42954706ae798752d7fcb401b Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 23:12:37 -0700 Subject: [PATCH] docs: v2.2.0 release notes and README What's New Add docs/changelog/v2/v2.2.0.md for the @Last modified by content-edit change (#128) and promote v2.2.0 to Latest in the README, moving v2.1.4 into Recent Releases. --- README.md | 10 +++--- docs/changelog/v2/v2.2.0.md | 62 +++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 docs/changelog/v2/v2.2.0.md diff --git a/README.md b/README.md index 140081e..0d84c03 100644 --- a/README.md +++ b/README.md @@ -14,19 +14,17 @@ Headers stay correct without hand-editing: `@Date` follows the file's real creat ## ✨ What's New -### Latest: v2.1.4 (October 2026) +### Latest: v2.2.0 (October 2026) -- **`require()` fails clearly where Node.js cannot load ES modules synchronously** — the CommonJS entry point (`dist/index.cjs`, a small wrapper around the ES module build) now checks `process.features.require_module` first. On a Node.js version without `require(esm)` it throws an `ERR_REQUIRE_ESM` error that names the package, the versions `require()` needs (^20.19.0 or >=22.12.0) and the running version, and points at `import()`, instead of Node's bare error from inside the package ([#117](https://github.com/CLDMV/fix-headers/pull/117)). New `node:test` checks run the built CommonJS entry point on every `npm test` and coverage run. On supported Node.js versions (`engines.node` is `>=22.12.0`) nothing changes. -- **Never breaks files it can't stamp** — strict JSON (`package.json` included), Markdown named with `--input`, and files with no or an unhandled extension are now skipped and reported instead of getting a JavaScript comment that broke them. Markdown gets a header only when forced with `--force-detector markdown`, as an HTML comment ([#124](https://github.com/CLDMV/fix-headers/pull/124)). -- **Repeatable `--input` and no more dependency folders** — every `--input` value is processed, not just the last ([#125](https://github.com/CLDMV/fix-headers/pull/125)), and `node_modules`, `bower_components`, `jspm_packages`, `.pnpm-store` and `.yarn` are never walked, at any depth, even without a `.gitignore` ([#126](https://github.com/CLDMV/fix-headers/pull/126)). -- [View full v2.1.4 Changelog](https://github.com/CLDMV/fix-headers/blob/master/docs/changelog/v2/v2.1.4.md) +- **`@Last modified by` names whoever last edited the file's content** — a run that only rewrites a header (a date format conversion, a corrected `@Project`, new spacing or margin) now keeps the recorded editor instead of writing the run's identity, so running fix-headers never claims other people's files. The edit check compares the file, header removed, with its body at git `HEAD`: a changed or not-yet-committed body makes the run's identity the last editor, and `@Author` stays the original author unless `forceAuthorUpdate` is set. `@Last modified time` still moves whenever a header is rewritten, and `forceLastModifiedAuthorUpdate` is no longer needed for normal use ([#128](https://github.com/CLDMV/fix-headers/pull/128)). +- [View full v2.2.0 Changelog](https://github.com/CLDMV/fix-headers/blob/master/docs/changelog/v2/v2.2.0.md) ### Recent Releases +- **v2.1.4** (October 2026) — strict JSON, Markdown named with `--input` and files with no or an unhandled extension are skipped and reported instead of getting a JavaScript comment; `--input` is repeatable; dependency folders are never walked; `require()` fails clearly where Node.js cannot load ES modules ([Changelog](https://github.com/CLDMV/fix-headers/blob/master/docs/changelog/v2/v2.1.4.md)) - **v2.1.3** (October 2026) — CI and development-dependency maintenance with no runtime change: a skipped PR run can no longer satisfy `✅ Required PR Check` and let a pull request merge before its tests finish ([Changelog](https://github.com/CLDMV/fix-headers/blob/master/docs/changelog/v2/v2.1.3.md)) - **v2.1.2** (October 2026) — no runtime change: the repository adopts the shared CLDMV fix-headers config from `@cldmv/configs` and stamps uniform file headers across its own sources ([Changelog](https://github.com/CLDMV/fix-headers/blob/master/docs/changelog/v2/v2.1.2.md)) - **v2.1.1** (October 2026) — a file that holds only a header now ends with the header instead of trailing `margin` blank lines; `esbuild` 0.28.2 clears GHSA-g7r4-m6w7-qqqr ([Changelog](https://github.com/CLDMV/fix-headers/blob/master/docs/changelog/v2/v2.1.1.md)) -- **v2.1.0** (September 2026) — `spacing` and `margin` header layout options: every header is framed with empty comment lines and followed by two blank lines ([Changelog](https://github.com/CLDMV/fix-headers/blob/master/docs/changelog/v2/v2.1.0.md)) 📚 For complete release notes, see the [docs/changelog/](https://github.com/CLDMV/fix-headers/tree/master/docs/changelog/) folder. diff --git a/docs/changelog/v2/v2.2.0.md b/docs/changelog/v2/v2.2.0.md new file mode 100644 index 0000000..5b8f16f --- /dev/null +++ b/docs/changelog/v2/v2.2.0.md @@ -0,0 +1,62 @@ +# @cldmv/fix-headers v2.2.0 Changelog + +**Release Date**: October 2026 +**Release Type**: Minor +**Branch**: `release/2.2.0` + +--- + +## Overview + +Version 2.2.0 changes what `@Last modified by` means. It now names whoever last edited the file's content, the part of the file outside the header, instead of whoever last ran fix-headers. A run that only rewrites a header (a date format conversion, a corrected `@Project`, new spacing or margin) keeps the recorded editor, so running the tool never claims other people's files. `@Author` was already the original author and stays that way unless `forceAuthorUpdate` is set. + +No option was removed, the `fixHeaders` API and the header format are unchanged, and `@Last modified time` is still restamped whenever a header is rewritten. See the upgrade notes for the one behaviour change that can show up in an existing setup. + +--- + +## ✨ Features + +### `@Last modified by` follows content edits, not header rewrites ([#128](https://github.com/CLDMV/fix-headers/pull/128), closes [#127](https://github.com/CLDMV/fix-headers/issues/127)) + +Before, any run that changed a header could write the identity detected for that run into `@Last modified by`, and with `forceLastModifiedAuthorUpdate` it did so on every file. With a shared config that forces the author fields, whoever ran the tool became the last editor of every file it touched, including files they never opened. + +fix-headers now decides whether a file's content was edited by comparing it with the file at git `HEAD`. The header is taken out of both versions, along with the blank lines after it, and what is left is compared: + +| File | Content edited? | `@Author` | `@Last modified by` | `@Last modified time` | +| ------------------------------------------------------------ | -------------------------- | -------------------------- | ------------------- | --------------------- | +| Tracked, body the same as at `HEAD`, header rewritten | no | kept | kept | now | +| Tracked, body differs from `HEAD` | yes | kept | run identity | now | +| Not in `HEAD` (new, untracked or ignored, or no commits yet) | yes | kept (filled when missing) | run identity | now | +| Outside a git work tree, header rewritten | cannot tell, treated as no | kept | kept | now | +| Header already current, content not edited | no | unchanged | unchanged | unchanged | + +- **Header-only rewrites keep the recorded editor.** The date format (`normalizeDateFormat`), an epoch repair, `@Date` (`fixCreatedDate`), a time zone conversion, the frame, `spacing` and `margin`, and the `@Project`, `@Filename` and `@Copyright` values all rewrite the header without changing who last edited the file. `@Last modified time` still moves, because the header did. +- **A content edit makes the run's identity the last editor.** That is the identity detected for the run (`authorName` / `authorEmail`, or git as described under `useGpgSignerAuthor`). A missing field is filled with it in every case, and a file with no header gets it in both fields. Adding a header to a committed file is not a content edit, so a later header-only run by someone else keeps whoever was recorded then. +- **A repeat edit by the editor already recorded is stamped once.** When the body differs from `HEAD` and the recorded time is older than the file's last commit, the edit has not been stamped yet, so `@Last modified time` is restamped even though the editor is unchanged. Once stamped, the time is newer than the last commit and running fix-headers again changes nothing until the next commit. +- **`forceLastModifiedAuthorUpdate` keeps its meaning** (write the run identity as `@Last modified by` on every file, edited or not), and `forceAuthorUpdate` still replaces an existing `@Author` / `@Email`. With content-edit detection, `forceLastModifiedAuthorUpdate` is no longer needed for normal use, and the README says so. The `--help` text for both force flags is reworded to match what they do. +- **The `issues` result follows the same rule.** The `lastModifiedByName` / `lastModifiedByEmail` entries appear only when the value that would be written really differs from the existing header, which now depends on whether the content was edited. + +The check compares the working tree with `HEAD`, so run fix-headers before committing (a pre-commit hook, or `npm run fix:headers` before `git commit`). Content committed without a run is not detected later, because by then the body matches `HEAD`. The README gains an **Author and last modified** section with the same table and rules. + +## 🧪 Tests + +- New suite `tests/last-modified-content-edit.test.vitest.mjs` covers header-only rewrites that keep the editor, body edits that take the run identity, `@Author` preserved for a different runner, `forceAuthorUpdate` still replacing it, untracked and new files, files outside a git work tree, and the one-time restamp of a repeat edit. `tests/core-edge.test.vitest.mjs` and `tests/sample-output.test.vitest.mjs` are updated for the new `issues` output. Coverage stays at 100%. + +## 📚 Documentation + +- **NEW:** [docs/changelog/v2/v2.2.0.md](./v2.2.0.md): this changelog. +- README: a new [Author and last modified](https://github.com/CLDMV/fix-headers/blob/master/README.md#-author-and-last-modified) section, a matching Key Features entry, and reworded `--force-author-update` / `--force-last-modified-author-update` CLI and API entries ([#128](https://github.com/CLDMV/fix-headers/pull/128)). + +## 🔧 Dependencies + +_No dependency updates._ `ignore`, the only runtime dependency, is unchanged. + +--- + +## Upgrade notes + +- No breaking changes: no option was removed and the API is unchanged. +- **`@Last modified by` can differ from before after a run.** A header-only rewrite no longer writes the run's identity into it, and a content edit now does. A config that sets `forceLastModifiedAuthorUpdate` keeps the old behaviour (the run identity on every file) and can drop that option once its files carry the right editor. +- **Run the tool before you commit.** The edit check reads the working tree against `HEAD`, so a hook or script that runs fix-headers after the commit will see an unchanged body and keep the recorded editor. +- **Outside a git work tree nothing is detected as an edit**, so recorded editors are kept and only `@Last modified time` moves when a header is rewritten. +- Preview the effect on your project with `--dry-run --diff --verbose`.