Skip to content

feat(enforce-consistent-line-wrapping): wrap a JSX attribute string in place (#216) - #224

Merged
sergioazoc merged 1 commit into
mainfrom
feat/line-wrapping-jsx-strings
Oct 9, 2026
Merged

sergioazoc merged 1 commit into
mainfrom
feat/line-wrapping-jsx-strings

Conversation

@sergioazoc

Copy link
Copy Markdown
Owner

Closes #216.

Summary

The rule never fixed a string literal: a JS string can't hold a raw newline, and turning it into a template is a judgment call. A JSX attribute's own string is different. className="…" may span lines as written, because its newlines are plain whitespace in a class attribute, so nothing has to be converted. It is also the layout eslint-plugin-better-tailwindcss writes: a migrated codebase's multiline className strings already lint clean, but new ones were only reported, not fixed.

  • New option wrapStrings: 'never' | 'jsx' (default 'never'). With 'jsx', both fixers (wrapLines and classesPerLine) also handle a Literal whose parent is a JSXAttribute, and write the template block between the quotes. The closing quote lands at the attribute line's indentation.
  • JS strings still only report: className={"…"}, call arguments, variables, ternaries and object values.
  • CRLF: a JSX string keeps the file's \r\n. It is measured and wrapped in LF, then written back with the file's line ending.
  • oxfmt: sortTailwindcss collapses the block unless preserveWhitespace: true. This is pinned in oxfmt-interop.test.ts and noted on the rule page and on /interop (EN/ES).

Test plan

  • Rule cases: overWidth, all, classesPerLine, single quotes, nested JSX and tabs, CRLF, configured attributes, and every JS-string shape still warn-only.
  • whitespace-line-wrap-cycle: steps for the JSX block (the no-unnecessary-whitespace and enforce-consistent-line-wrapping contradict each other #14 cycle).
  • multiline-preservation now runs every fixer over both the template shape and the JSX-string shape (34 cases).
  • e2e oxfmt-interop: with real oxfmt, the block collapses by default and survives with preserveWhitespace: true.
  • Mutation checks: a looser predicate and removing the CRLF handling each fail tests.
  • Docs (EN/ES): the wrapStrings section, corrected string-literal passages, new ✗/✓ examples run by doc-examples, the defaults row and the migration-table note.
  • Lint, lint:types, format:check, typecheck, build and pnpm test (3749 + 312); bench npm test. The corpus and synthetic runs are identical to the baseline (the default is unchanged). interop.mjs passes.

🤖 Generated with Claude Code

https://claude.ai/code/session_019vTh6S5A41M1HLkmk3FaM1

…n place (#216)

The rule never fixed a string literal: a JS string can't hold a raw newline,
and turning it into a template is a judgment call. A JSX attribute's own
string is different — `className="…"` may span lines as written, its newlines
are plain whitespace in a class attribute — so nothing is converted, and it's
the layout eslint-plugin-better-tailwindcss writes. A migrated codebase's
multiline className strings linted clean but new ones were only reported.

- New option `wrapStrings: 'never' | 'jsx'` (default 'never'). With 'jsx',
  both fixers (wrapLines and classesPerLine) take a Literal whose parent is a
  JSXAttribute — never `className={"…"}`, a call argument, a variable, a
  ternary or an object value — and write the template block between the
  quotes, the closing quote at the attribute line's indentation.
- CRLF: a JSX string keeps the file's `\r\n` (a template's value is LF), so
  it is measured and wrapped in LF and written back with the file's ending.
- oxfmt's sortTailwindcss collapses the block unless `preserveWhitespace:
  true`: pinned in oxfmt-interop.test.ts and said on the rule page and /interop.

Tests: rule cases (overWidth, all, classesPerLine, single quotes, nested JSX
and tabs, CRLF, configured attributes, every JS-string shape still warn-only);
the #14 cycle steps for the JSX block; multiline-preservation now runs every
fixer over both the template and the JSX-string shape. Mutations of the
predicate and the CRLF handling each fail a test. The bench corpus and
synthetic are identical (the default is unchanged).

Claude-Session: https://claude.ai/code/session_019vTh6S5A41M1HLkmk3FaM1
@sergioazoc
sergioazoc merged commit d11c5d2 into main Oct 9, 2026
11 checks passed
@sergioazoc
sergioazoc deleted the feat/line-wrapping-jsx-strings branch October 9, 2026 01:27
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.

enforce-consistent-line-wrapping: opt-in fix for JSX attribute strings

1 participant