From 43ba552c6cbc52e3b6548973d184ab2153971c68 Mon Sep 17 00:00:00 2001 From: Brian Nuckols Date: Sun, 12 Jul 2026 23:39:20 -0400 Subject: [PATCH] fix(schema): tenthmil no longer emits "NaNmil" for missing pad fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tenthmil (used by PadSchema, RectSchema, and TextSchema for coordinates and sizes) is optional, but its transform ran `${Number.parseFloat(n) * 10}mil` on every non-unit value. When a field is undefined — e.g. a short EasyEDA pad string that omits holeRadius — Number.parseFloat(undefined) is NaN, producing the nonsensical unit string "NaNmil". Downstream in convert-easyeda-json-to-tscircuit-soup-json.ts, a "NaNmil" holeRadius makes mil2mm(pad.holeRadius) !== 0 true, so the pad is treated as a plated hole and emits NaN geometry — surfacing as "NaNmm" in the generated component, the string the convert-to-ts tests guard against. Guard the parse: fall back to 0 when Number.parseFloat yields NaN, matching the safeNumber(0) convention already used in this file. A missing holeRadius now reads as 0 (an SMT pad). Adds a regression test. --- lib/schemas/package-detail-shape-schema.ts | 13 ++++--- tests/parse-tests/pad-tenthmil-nan.test.ts | 42 ++++++++++++++++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 tests/parse-tests/pad-tenthmil-nan.test.ts diff --git a/lib/schemas/package-detail-shape-schema.ts b/lib/schemas/package-detail-shape-schema.ts index 852f89b6..8e0e7134 100644 --- a/lib/schemas/package-detail-shape-schema.ts +++ b/lib/schemas/package-detail-shape-schema.ts @@ -13,11 +13,14 @@ const safeNumber = (defaultValue = 0) => const tenthmil = z .union([z.number(), z.string()]) .optional() - .transform((n) => - typeof n === "string" && n.endsWith("mil") - ? n - : `${Number.parseFloat(n as string) * 10}mil`, - ) + .transform((n) => { + if (typeof n === "string" && n.endsWith("mil")) return n + // parseFloat(undefined)/non-numeric input is NaN, which previously produced + // the nonsensical unit string "NaNmil" (and later "NaNmm" in output). Fall + // back to 0, matching the safeNumber(0) convention above. + const parsed = Number.parseFloat(n as string) + return `${(Number.isNaN(parsed) ? 0 : parsed) * 10}mil` + }) .pipe(z.string()) export const PointSchema = z diff --git a/tests/parse-tests/pad-tenthmil-nan.test.ts b/tests/parse-tests/pad-tenthmil-nan.test.ts new file mode 100644 index 00000000..6dfc3540 --- /dev/null +++ b/tests/parse-tests/pad-tenthmil-nan.test.ts @@ -0,0 +1,42 @@ +import { it, expect } from "bun:test" +import { PadSchema } from "lib/schemas/package-detail-shape-schema" + +const basePad = { + type: "PAD" as const, + shape: "ELLIPSE" as const, + center: { x: 100, y: 200 }, + width: 60, + height: 60, + layermask: 1, + number: 1, + plated: false, +} + +it("does not emit NaNmil when an optional tenthmil field is missing", () => { + // A short/variant EasyEDA pad string can leave holeRadius (or a later field) + // undefined. tenthmil must not turn that into the nonsensical unit "NaNmil", + // which otherwise surfaces as "NaNmm" in the generated component. + const pad = PadSchema.parse({ ...basePad }) // holeRadius omitted -> undefined + expect(pad.holeRadius).toBe("0mil") + for (const v of [ + pad.center.x, + pad.center.y, + pad.width, + pad.height, + pad.holeRadius, + ]) { + expect(String(v)).not.toContain("NaN") + } +}) + +it("still converts present tenthmil values (bare number => mil*10, unit string passes through)", () => { + const pad = PadSchema.parse({ + ...basePad, + width: 60, + height: "40", + holeRadius: "5mil", + }) + expect(pad.width).toBe("600mil") // 60 * 10 + expect(pad.height).toBe("400mil") // "40" -> 40 * 10 + expect(pad.holeRadius).toBe("5mil") // already has a unit, passed through +})