Skip to content

Fix Node#rangeBy() ignoring index 0#2091

Merged
ai merged 1 commit into
postcss:mainfrom
sarathfrancis90:fix-rangeby-index-zero
Jun 27, 2026
Merged

Fix Node#rangeBy() ignoring index 0#2091
ai merged 1 commit into
postcss:mainfrom
sarathfrancis90:fix-rangeby-index-zero

Conversation

@sarathfrancis90

Copy link
Copy Markdown
Contributor

What

Node#rangeBy() ignored an index of 0, returning a range that spanned the entire node instead of a single character at offset 0. This also affected Node#error() and Node#warn(), which build their positions through rangeBy().

const root = postcss.parse('a b{}')

// before this PR
root.first.rangeBy({ index: 0 })
// => { start: { column: 1, ... }, end: { column: 6, offset: 5 } }  // whole "a b{}"

// any other index works correctly
root.first.rangeBy({ index: 1 })
// => { start: { column: 2, ... }, end: { column: 3, ... } }  // single character

Root cause

In lib/node.js, the end-of-range branch used a truthy check on opts.index:

} else if (opts.index) {
  end = this.positionInside(opts.index + 1)
}

With index === 0 the condition is falsy, so the branch is skipped and end keeps the node's end position, producing a whole-node range. The start branch had the same truthy check, though for the start position positionInside(0) happens to equal the node start, so only the end was observably wrong.

This is the same off-by-zero that was previously fixed for endIndex in #1932 (else if (opts.endIndex)typeof opts.endIndex === 'number'); the two opts.index branches were left unchanged at that time.

Fix

Switch both opts.index checks to a numeric type check, matching the existing endIndex handling:

} else if (typeof opts.index === 'number') {

index: 0 now yields a single-character range, consistent with every other index value.

Tests

Added regression tests (both confirmed failing before the fix, passing after):

  • test/node.test.tsrangeBy() returns a single-character range for index: 0.
  • test/warning.test.ts — a warning created with index: 0 reports endColumn one past the start rather than the node end.

Full suite locally:

  • pnpm unit — 654/654 passing
  • pnpm test:lint — clean
  • pnpm test:types — clean

When calling Node#rangeBy() (and therefore Node#error() and
Node#warn()) with index 0, the resulting range covered the whole node
instead of a single character at offset 0.

The end-of-range branch used a truthy check on opts.index, so an index
of 0 fell through and left the end position at the node's end. This is
the same off-by-zero issue that was previously fixed for endIndex; the
index branches were missed. Switch both index checks to a numeric type
check, matching the existing endIndex handling.

index 0 now produces a single-character range, consistent with every
other index value.
@ai ai merged commit d1e80b8 into postcss:main Jun 27, 2026
10 checks passed
@ai

ai commented Jun 27, 2026

Copy link
Copy Markdown
Member

Thanks!

@ai

ai commented Jun 28, 2026

Copy link
Copy Markdown
Member

Released in 8.5.16.

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.

2 participants