From dec7990c9cbbbddf0ab0a7f33afea6c4e0a5422a Mon Sep 17 00:00:00 2001 From: ducanh3005 Date: Fri, 23 Oct 2026 23:26:24 +0700 Subject: [PATCH] fix(via): initialize layer ports after board attachment so inner layers are exposed (#4530) --- lib/components/primitive-components/Via.ts | 15 ++--- .../repro-via-inner2-ports.snap.svg | 6 +- .../repro4530-via-inner-layer-ports.test.tsx | 61 +++++++++++++++++++ 3 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 tests/repros/repro4530-via-inner-layer-ports.test.tsx diff --git a/lib/components/primitive-components/Via.ts b/lib/components/primitive-components/Via.ts index 1b0c115fc6..6bb76070c8 100644 --- a/lib/components/primitive-components/Via.ts +++ b/lib/components/primitive-components/Via.ts @@ -4,7 +4,6 @@ import { createNetsFromProps } from "lib/utils/components/createNetsFromProps" import { getViaSpanLayers } from "lib/utils/getViaSpanLayers" import { getViaDiameterDefaultsWithOverrides } from "lib/utils/pcbStyle/getViaDiameterDefaults" import { getViaTenting } from "lib/utils/getViaTenting" -import { z } from "zod" import { PrimitiveComponent } from "../base-components/PrimitiveComponent" import { Net } from "./Net" import { Port } from "./Port" @@ -19,12 +18,6 @@ export class Via extends PrimitiveComponent { source_trace_id: string | null = null subcircuit_connectivity_map_key: string | null = null - constructor(props: z.input) { - super(props) - const layers = this._getLayers() - ;(this._parsedProps as any).layers = layers - this.initPorts() - } get config() { return { componentName: "Via", @@ -86,16 +79,16 @@ export class Via extends PrimitiveComponent { } _getLayers(): LayerRef[] { const { fromLayer = "top", toLayer = "bottom" } = this._parsedProps - // Before the via is attached to the tree (constructor/port init) the board - // layer count is unknown — fall back to 2, which yields [fromLayer, toLayer]. + // Unattached layer queries do not yet know the board's layer count. const layerCount = this.parent ? this.getSubcircuit()._getSubcircuitLayerCount() : 2 return getViaSpanLayers({ fromLayer, toLayer, layerCount }) } - initPorts() { - const layers = (this._parsedProps as any).layers as LayerRef[] + doInitialInitializePortsFromChildren(): void { + const layers = this._getLayers() + ;(this._parsedProps as any).layers = layers for (const layer of layers) { const port = new Port({ name: layer, layer }) port.registerMatch(this) diff --git a/tests/repros/__snapshots__/repro-via-inner2-ports.snap.svg b/tests/repros/__snapshots__/repro-via-inner2-ports.snap.svg index 1816e3a291..36e36f3861 100644 --- a/tests/repros/__snapshots__/repro-via-inner2-ports.snap.svg +++ b/tests/repros/__snapshots__/repro-via-inner2-ports.snap.svg @@ -2,12 +2,12 @@ - Inner2 connection: FAILING + Inner2 connection: PASSING Expected: a copper trace connecting VA.inner2 to VB.inner2. - Actual: 0 inner2 traces; vias disconnected. + Actual: 1 inner2 traces; vias connected. - VA.inner2VB.inner2 + VA.inner2VB.inner2 \ No newline at end of file diff --git a/tests/repros/repro4530-via-inner-layer-ports.test.tsx b/tests/repros/repro4530-via-inner-layer-ports.test.tsx new file mode 100644 index 0000000000..31e3cd8fae --- /dev/null +++ b/tests/repros/repro4530-via-inner-layer-ports.test.tsx @@ -0,0 +1,61 @@ +import { expect, test } from "bun:test" +import { getTestFixture } from "tests/fixtures/get-test-fixture" + +test("through-hole via components expose inner-layer source and PCB ports on multilayer boards", async () => { + const { circuit } = getTestFixture() + circuit.schematicDisabled = true + + circuit.add( + + + + + , + ) + + await circuit.renderUntilSettled() + + const pcbVias = circuit.db.pcb_via.list() + expect(pcbVias).toHaveLength(2) + for (const via of pcbVias) { + expect(via.layers).toEqual(["top", "inner1", "inner2", "bottom"]) + expect((via as any).pcb_port_ids).toHaveLength(4) + } + + expect(circuit.db.source_port.list()).toHaveLength(8) + expect(circuit.db.pcb_port.list()).toHaveLength(8) + expect(circuit.db.source_trace_not_connected_error.list()).toHaveLength(0) + + const traces = circuit.db.pcb_trace.list() + expect(traces).toHaveLength(1) + const wirePoints = traces[0]!.route.filter((pt) => pt.route_type === "wire") + expect(wirePoints.length).toBeGreaterThanOrEqual(2) + for (const pt of wirePoints) { + if (pt.route_type === "wire") { + expect(pt.layer).toBe("inner2") + } + } +})