Skip to content

fix: correct parseInt radix parameters and use parseFloat for dimensions#576

Open
claw-explorer wants to merge 1 commit intotscircuit:mainfrom
claw-explorer:fix/parseint-radix-bug
Open

fix: correct parseInt radix parameters and use parseFloat for dimensions#576
claw-explorer wants to merge 1 commit intotscircuit:mainfrom
claw-explorer:fix/parseint-radix-bug

Conversation

@claw-explorer
Copy link
Copy Markdown

Summary

Fixes latent bugs in three footprint generators where Number.parseInt() was called with the default pin count as the radix instead of base 10, causing string-based pin count parsing to silently return NaN.

The Bug

// Before (broken): radix 3 only accepts digits 0-2
const numPins = match ? Number.parseInt(match[1]!, 3) : 3  // parseInt('3', 3) → NaN

// After (fixed): radix 10 correctly parses decimal numbers
const numPins = match ? Number.parseInt(match[1]!, 10) : 3  // parseInt('3', 10) → 3

parseInt Radix Fixes

File Before After Impact
sot323.ts parseInt(match[1]!, 3) parseInt(match[1]!, 10) Radix 3 rejects digit '3', so any pin count from string returned NaN
sot23w.ts parseInt(match[1]!, 3) parseInt(match[1]!, 10) Same base-3 issue
sot343.ts parseInt(match[1]!, 4) parseInt(match[1]!, 10) Radix 4 rejects digit '4'+, same issue
to220.ts parseInt(...) parseInt(..., 10) Added explicit radix for best practices
to220f.ts parseInt(...) parseInt(..., 10) Added explicit radix for best practices
bga.ts parseInt(m[2]!) parseInt(m[2]!, 10) Added explicit radix for best practices

parseInt → parseFloat Fixes

File Before After Impact
sot23.ts parseInt(parameters.h) parseFloat(parameters.h) parseInt('2.50mm') → 2 (truncated); parseFloat → 2.5 (correct)
sot23w.ts parseInt(parameters.h) parseFloat(parameters.h) Same truncation issue for silkscreen ref Y position
sot323.ts parseInt(parameters.h) parseFloat(parameters.h) Same truncation issue for silkscreen ref Y position

Test Plan

  • All 389 tests pass (385 existing + 4 new regression tests)
  • New tests/parseint-radix.test.ts validates:
    • sot323 string parsing correctly reads num_pins
    • sot343 produces exactly 4 pads
    • sot23w generates 3 pads by default
    • sot23 silkscreen ref uses full decimal height value
  • Updated SVG snapshots reflect corrected silkscreen positioning
  • bun run format — no formatting changes needed

Three footprint generators used the wrong radix in Number.parseInt(),
causing string-based pin count parsing to silently return NaN:

- sot323.ts: parseInt(match[1]!, 3) → parseInt(match[1]!, 10)
  Radix 3 only accepts digits 0-2, so '3' returns NaN
- sot23w.ts: parseInt(match[1]!, 3) → parseInt(match[1]!, 10)
  Same base-3 issue
- sot343.ts: parseInt(match[1]!, 4) → parseInt(match[1]!, 10)
  Radix 4 only accepts digits 0-3, so '4' returns NaN

The second argument to parseInt() was accidentally set to the default
pin count instead of the radix (10). This meant parsing any pin count
from the string always failed, falling back to defaults.

Also fixes parseInt → parseFloat for silkscreen ref text positioning
in sot23, sot23w, and sot323, where parseInt('2.50mm') truncated to
2 instead of using the correct 2.5mm value.

Adds explicit radix 10 to parseInt calls in to220, to220f, and bga
for consistency and to follow best practices.

All 389 tests pass (4 new regression tests added).
@claw-explorer claw-explorer requested a review from seveibar as a code owner April 4, 2026 19:21
Comment on lines +1 to +41
import { test, expect } from "bun:test"
import { fp } from "src/footprinter"

test("sot323 string parsing correctly reads num_pins", () => {
// Before fix: parseInt(match[1]!, 3) used base-3 radix,
// so digit "3" and above returned NaN
const params = fp.string("sot323").params()
expect(params.fn).toBe("sot323")
})

test("sot343 string parsing correctly reads num_pins", () => {
// Before fix: parseInt(match[1]!, 4) used base-4 radix,
// so digit "4" and above returned NaN
const params = fp.string("sot343").params()
expect(params.fn).toBe("sot343")
const circuitJson = fp.string("sot343").circuitJson()
const smtpads = circuitJson.filter((e) => e.type === "pcb_smtpad")
expect(smtpads.length).toBe(4)
})

test("sot23w generates 3 pads by default", () => {
const circuitJson = fp.string("sot23w").circuitJson()
const smtpads = circuitJson.filter((e) => e.type === "pcb_smtpad")
expect(smtpads.length).toBe(3)
})

test("sot23 silkscreen ref uses parseFloat for height positioning", () => {
// Before fix: parseInt("2.50mm") returned 2, truncating the decimal
// After fix: parseFloat("2.50mm") returns 2.5
const circuitJson = fp.string("sot23").circuitJson()
const silkscreenText = circuitJson.find(
(e) => e.type === "pcb_silkscreen_text",
)
expect(silkscreenText).toBeDefined()
// With parseFloat, the Y position should use the full decimal value
// not just the integer part
if (silkscreenText && "anchor_position" in silkscreenText) {
// Y should be based on parseFloat("2.50mm") = 2.5, not parseInt("2.50mm") = 2
expect(silkscreenText.anchor_position.y).not.toBe(2)
}
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test file contains 4 test() functions, which violates the rule that a *.test.ts file may have AT MOST one test(...). After that, the user should split into multiple, numbered files. The file should be split into separate files like parseint-radix1.test.ts, parseint-radix2.test.ts, parseint-radix3.test.ts, and parseint-radix4.test.ts, with each file containing only one test function.

Spotted by Graphite (based on custom rule: Custom rule)

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

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