Skip to content

feat(fix): auto-apply ignoreExports rules to fallow config (closes #330)#366

Merged
BartWaardenburg merged 1 commit into
mainfrom
feat/issue-330-config-writer
May 13, 2026
Merged

feat(fix): auto-apply ignoreExports rules to fallow config (closes #330)#366
BartWaardenburg merged 1 commit into
mainfrom
feat/issue-330-config-writer

Conversation

@BartWaardenburg
Copy link
Copy Markdown
Collaborator

Summary

fallow fix can now apply the add-to-config action for duplicate-export findings directly to the user's fallow config file, instead of just emitting a paste-ready snippet. The action flips from auto_fixable: false to auto_fixable: true whenever a config file exists; with no config, fix skips with a clear Run 'fallow init' to create one message.

This unblocks shadcn / Radix / bits-ui-style namespace-barrel projects where users had to hand-paste 30+ ignoreExports entries.

What ships

  • Writer module fallow_config::add_ignore_exports_rule covers all four supported config formats atomically. JSON/JSONC go through jsonc-parser (format-preserving CST); TOML / .fallow.toml go through toml_edit. Both round-trip comments, ordering, inline-vs-newline shape, trailing commas, CRLF/LF line endings, and a UTF-8 BOM.
  • Merge semantics: append-only, dedupe on exact file match, no reorder, no replace. Absolute existing entries dedupe against the writer's relative emissions when they resolve under the config dir.
  • Workspace path re-anchoring: when the config lives in a workspace subdir (e.g. packages/ui/.fallowrc.json), emitted paths are relative to that config's directory.
  • Symlink-safe writes: atomic_write canonicalizes the target before persisting so configs mounted as symlinks (common in Docker setups) get their target updated rather than being replaced with a regular file.
  • Parser convergence: all json_comments::StripComments parse sites replaced with jsonc-parser so reads and writes share the same JSONC dialect.
  • Schema: AddToConfigAction.auto_fixable widens from const false to boolean. TypeScript contract regenerated for both editors/vscode/src/generated/output-contract.d.ts and npm/fallow/types/output-contract.d.ts.

Test plan

  • cargo test -p fallow-cli -p fallow-config --all-targets
  • cargo clippy -p fallow-cli -p fallow-config -- -D warnings
  • cargo fmt --all -- --check
  • typos
  • check:codegen (regenerated TS contract is in sync)
  • End-to-end smoke combining BOM-prefixed JSON config + symlinked path + pre-existing absolute-path entry: write succeeds, BOM preserved (count=1), symlink intact, no abs/rel duplicate, post-fix dead-code reports 0 duplicates, second fix is byte-idempotent
  • TOML BOM round-trip end-to-end smoke
  • Workspace-subdir re-anchor smoke (packages/ui/.fallowrc.json emits src/index.ts not packages/ui/src/index.ts)
  • Missing-config skip message verified

Out of scope (separate issues)

  • Optional value_schema URL field on add-to-config actions for AI-agent input validation
  • Missing-config-file auto-creation policy and fallow fix --dry-run config diff preview
  • Threading explicit --config <path> through into auto_fixable (narrow edge case: user passes --config pointing outside root's ancestors AND agent gates on auto_fixable rather than attempting the fix)

`fallow fix` can now apply the `add-to-config` action for duplicate-export
findings directly to the user's fallow config, instead of just emitting a
paste-ready snippet. The action flips from `auto_fixable: false` to
`auto_fixable: true` whenever a config file exists at the project root;
with no config, fix skips with a clear `Run 'fallow init' to create one`
message. Unblocks shadcn / Radix / bits-ui-style namespace-barrel projects
where users had to hand-paste 30+ ignoreExports entries.

Writer

* `fallow_config::add_ignore_exports_rule(path, entries)` covers all four
  supported config formats atomically:
  - `.json` / `.jsonc` via `jsonc-parser` CST (format-preserving;
    dprint-author crate).
  - `.toml` / `.fallow.toml` via `toml_edit` (used by cargo itself).
* Merge semantics: append-only, dedupe on exact `file` match, no reorder,
  no replace. Absolute existing entries dedupe against the writer's
  relative emissions when they resolve under the config dir.
* Workspace path re-anchoring: when the config lives in a workspace subdir
  (e.g. `packages/ui/.fallowrc.json`), emitted paths are relative to that
  config's directory.
* CRLF / LF round-trip: detect the existing file's line endings and
  preserve them on emit (normalize-then-emit so the CST library's own
  CRLF preservation is not doubled into `\r\r\n`).
* UTF-8 BOM round-trip: strip a leading BOM before parse (jsonc-parser
  rejects it; toml_edit silently drops it on emit) and re-prepend on
  emit. Also stripped in the main config loader so Windows-authored
  configs parse cleanly.
* Symlink-safe writes: `atomic_write` canonicalizes the target before
  persisting so configs mounted as symlinks (common in Docker setups)
  get their target updated rather than being replaced with a regular
  file.
* All `json_comments::StripComments` parse sites replaced with
  `jsonc-parser` so reads and writes share the same JSONC dialect.

JSON contract

* `AddToConfigAction.auto_fixable` widens from `const false` to
  `boolean`.
* TypeScript contract regenerated for both
  `editors/vscode/src/generated/output-contract.d.ts` and
  `npm/fallow/types/output-contract.d.ts`.

Tests

* Unit coverage for: JSON / JSONC / TOML / .fallow.toml shape; comment
  preservation; existing-array merging; workspace-subdir re-anchoring;
  CRLF round-trip (with `!output.contains("\r\r")` assertion);
  TOML+CRLF round-trip; BOM round-trip on JSON and TOML;
  absolute-vs-relative path dedupe on JSON and TOML.
* Integration coverage for: end-to-end fix + post-fix dead-code
  round-trip; BOM round-trip through the full binary; symlink
  write-through (Unix).
@BartWaardenburg BartWaardenburg force-pushed the feat/issue-330-config-writer branch from dbe4bd6 to 1907b2f Compare May 13, 2026 10:10
@BartWaardenburg BartWaardenburg merged commit fb89904 into main May 13, 2026
21 checks passed
@BartWaardenburg BartWaardenburg deleted the feat/issue-330-config-writer branch May 13, 2026 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant