@tscircuit/parts-engine 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/jlc-parts-engine.ts +54 -16
- package/package.json +1 -1
- package/tests/jlc-parts-engine.test.ts +40 -1
package/lib/jlc-parts-engine.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PartsEngine, SupplierPartNumbers } from "@tscircuit/props"
|
|
2
2
|
import { getJlcpcbPackageName } from "./footprint-translators/index"
|
|
3
3
|
|
|
4
|
-
const cache = new Map<string, any>()
|
|
4
|
+
export const cache = new Map<string, any>()
|
|
5
5
|
|
|
6
6
|
const getJlcPartsCached = async (name: any, params: any) => {
|
|
7
7
|
const paramString = new URLSearchParams({
|
|
@@ -19,6 +19,13 @@ const getJlcPartsCached = async (name: any, params: any) => {
|
|
|
19
19
|
return responseJson
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
const withBasicPartPreference = (parts: any[] | undefined) => {
|
|
23
|
+
if (!parts) return []
|
|
24
|
+
return [...parts].sort(
|
|
25
|
+
(a, b) => Number(b.is_basic ?? false) - Number(a.is_basic ?? false),
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
export const jlcPartsEngine: PartsEngine = {
|
|
23
30
|
findPart: async ({
|
|
24
31
|
sourceComponent,
|
|
@@ -36,7 +43,9 @@ export const jlcPartsEngine: PartsEngine = {
|
|
|
36
43
|
})
|
|
37
44
|
|
|
38
45
|
return {
|
|
39
|
-
jlcpcb: (resistors
|
|
46
|
+
jlcpcb: withBasicPartPreference(resistors)
|
|
47
|
+
.map((r: any) => `C${r.lcsc}`)
|
|
48
|
+
.slice(0, 3),
|
|
40
49
|
}
|
|
41
50
|
} else if (
|
|
42
51
|
sourceComponent.type === "source_component" &&
|
|
@@ -48,7 +57,9 @@ export const jlcPartsEngine: PartsEngine = {
|
|
|
48
57
|
})
|
|
49
58
|
|
|
50
59
|
return {
|
|
51
|
-
jlcpcb: (capacitors
|
|
60
|
+
jlcpcb: withBasicPartPreference(capacitors)
|
|
61
|
+
.map((c: any) => `C${c.lcsc}`)
|
|
62
|
+
.slice(0, 3),
|
|
52
63
|
}
|
|
53
64
|
} else if (
|
|
54
65
|
sourceComponent.type === "source_component" &&
|
|
@@ -72,7 +83,9 @@ export const jlcPartsEngine: PartsEngine = {
|
|
|
72
83
|
},
|
|
73
84
|
)
|
|
74
85
|
return {
|
|
75
|
-
jlcpcb: (headers
|
|
86
|
+
jlcpcb: withBasicPartPreference(headers)
|
|
87
|
+
.map((h: any) => `C${h.lcsc}`)
|
|
88
|
+
.slice(0, 3),
|
|
76
89
|
}
|
|
77
90
|
} else if (
|
|
78
91
|
sourceComponent.type === "source_component" &&
|
|
@@ -83,7 +96,7 @@ export const jlcPartsEngine: PartsEngine = {
|
|
|
83
96
|
package: jlcpcbPackage,
|
|
84
97
|
})
|
|
85
98
|
return {
|
|
86
|
-
jlcpcb: (potentiometers
|
|
99
|
+
jlcpcb: withBasicPartPreference(potentiometers)
|
|
87
100
|
.map((p: any) => `C${p.lcsc}`)
|
|
88
101
|
.slice(0, 3),
|
|
89
102
|
}
|
|
@@ -95,17 +108,24 @@ export const jlcPartsEngine: PartsEngine = {
|
|
|
95
108
|
package: jlcpcbPackage,
|
|
96
109
|
})
|
|
97
110
|
return {
|
|
98
|
-
jlcpcb: (diodes
|
|
111
|
+
jlcpcb: withBasicPartPreference(diodes)
|
|
112
|
+
.map((d: any) => `C${d.lcsc}`)
|
|
113
|
+
.slice(0, 3),
|
|
99
114
|
}
|
|
100
115
|
} else if (
|
|
101
116
|
sourceComponent.type === "source_component" &&
|
|
102
117
|
sourceComponent.ftype === "simple_chip"
|
|
103
118
|
) {
|
|
119
|
+
if (!jlcpcbPackage || !footprinterString) {
|
|
120
|
+
return {}
|
|
121
|
+
}
|
|
104
122
|
const { chips } = await getJlcPartsCached("chips", {
|
|
105
123
|
package: jlcpcbPackage,
|
|
106
124
|
})
|
|
107
125
|
return {
|
|
108
|
-
jlcpcb: (chips
|
|
126
|
+
jlcpcb: withBasicPartPreference(chips)
|
|
127
|
+
.map((c: any) => `C${c.lcsc}`)
|
|
128
|
+
.slice(0, 3),
|
|
109
129
|
}
|
|
110
130
|
} else if (
|
|
111
131
|
sourceComponent.type === "source_component" &&
|
|
@@ -116,7 +136,9 @@ export const jlcPartsEngine: PartsEngine = {
|
|
|
116
136
|
transistor_type: sourceComponent.transistor_type,
|
|
117
137
|
})
|
|
118
138
|
return {
|
|
119
|
-
jlcpcb: (transistors
|
|
139
|
+
jlcpcb: withBasicPartPreference(transistors)
|
|
140
|
+
.map((t: any) => `C${t.lcsc}`)
|
|
141
|
+
.slice(0, 3),
|
|
120
142
|
}
|
|
121
143
|
} else if (
|
|
122
144
|
sourceComponent.type === "source_component" &&
|
|
@@ -127,7 +149,9 @@ export const jlcPartsEngine: PartsEngine = {
|
|
|
127
149
|
package: jlcpcbPackage,
|
|
128
150
|
})
|
|
129
151
|
return {
|
|
130
|
-
jlcpcb: (power_sources
|
|
152
|
+
jlcpcb: withBasicPartPreference(power_sources)
|
|
153
|
+
.map((p: any) => `C${p.lcsc}`)
|
|
154
|
+
.slice(0, 3),
|
|
131
155
|
}
|
|
132
156
|
} else if (
|
|
133
157
|
sourceComponent.type === "source_component" &&
|
|
@@ -138,7 +162,9 @@ export const jlcPartsEngine: PartsEngine = {
|
|
|
138
162
|
package: jlcpcbPackage,
|
|
139
163
|
})
|
|
140
164
|
return {
|
|
141
|
-
jlcpcb: (inductors
|
|
165
|
+
jlcpcb: withBasicPartPreference(inductors)
|
|
166
|
+
.map((i: any) => `C${i.lcsc}`)
|
|
167
|
+
.slice(0, 3),
|
|
142
168
|
}
|
|
143
169
|
} else if (
|
|
144
170
|
sourceComponent.type === "source_component" &&
|
|
@@ -150,7 +176,9 @@ export const jlcPartsEngine: PartsEngine = {
|
|
|
150
176
|
package: jlcpcbPackage,
|
|
151
177
|
})
|
|
152
178
|
return {
|
|
153
|
-
jlcpcb: (crystals
|
|
179
|
+
jlcpcb: withBasicPartPreference(crystals)
|
|
180
|
+
.map((c: any) => `C${c.lcsc}`)
|
|
181
|
+
.slice(0, 3),
|
|
154
182
|
}
|
|
155
183
|
} else if (
|
|
156
184
|
sourceComponent.type === "source_component" &&
|
|
@@ -162,7 +190,9 @@ export const jlcPartsEngine: PartsEngine = {
|
|
|
162
190
|
channel_type: sourceComponent.channel_type,
|
|
163
191
|
})
|
|
164
192
|
return {
|
|
165
|
-
jlcpcb: (mosfets
|
|
193
|
+
jlcpcb: withBasicPartPreference(mosfets)
|
|
194
|
+
.map((m: any) => `C${m.lcsc}`)
|
|
195
|
+
.slice(0, 3),
|
|
166
196
|
}
|
|
167
197
|
} else if (
|
|
168
198
|
sourceComponent.type === "source_component" &&
|
|
@@ -173,7 +203,9 @@ export const jlcPartsEngine: PartsEngine = {
|
|
|
173
203
|
package: jlcpcbPackage,
|
|
174
204
|
})
|
|
175
205
|
return {
|
|
176
|
-
jlcpcb: (resonators
|
|
206
|
+
jlcpcb: withBasicPartPreference(resonators)
|
|
207
|
+
.map((r: any) => `C${r.lcsc}`)
|
|
208
|
+
.slice(0, 3),
|
|
177
209
|
}
|
|
178
210
|
} else if (
|
|
179
211
|
sourceComponent.type === "source_component" &&
|
|
@@ -184,7 +216,9 @@ export const jlcPartsEngine: PartsEngine = {
|
|
|
184
216
|
package: jlcpcbPackage,
|
|
185
217
|
})
|
|
186
218
|
return {
|
|
187
|
-
jlcpcb: (switches
|
|
219
|
+
jlcpcb: withBasicPartPreference(switches)
|
|
220
|
+
.map((s: any) => `C${s.lcsc}`)
|
|
221
|
+
.slice(0, 3),
|
|
188
222
|
}
|
|
189
223
|
} else if (
|
|
190
224
|
sourceComponent.type === "source_component" &&
|
|
@@ -194,7 +228,9 @@ export const jlcPartsEngine: PartsEngine = {
|
|
|
194
228
|
package: jlcpcbPackage,
|
|
195
229
|
})
|
|
196
230
|
return {
|
|
197
|
-
jlcpcb: (leds
|
|
231
|
+
jlcpcb: withBasicPartPreference(leds)
|
|
232
|
+
.map((l: any) => `C${l.lcsc}`)
|
|
233
|
+
.slice(0, 3),
|
|
198
234
|
}
|
|
199
235
|
} else if (
|
|
200
236
|
sourceComponent.type === "source_component" &&
|
|
@@ -204,7 +240,9 @@ export const jlcPartsEngine: PartsEngine = {
|
|
|
204
240
|
package: jlcpcbPackage,
|
|
205
241
|
})
|
|
206
242
|
return {
|
|
207
|
-
jlcpcb: (fuses
|
|
243
|
+
jlcpcb: withBasicPartPreference(fuses)
|
|
244
|
+
.map((l: any) => `C${l.lcsc}`)
|
|
245
|
+
.slice(0, 3),
|
|
208
246
|
}
|
|
209
247
|
}
|
|
210
248
|
return {}
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { describe, test, expect, beforeEach } from "bun:test"
|
|
2
|
-
import { jlcPartsEngine } from "../lib/jlc-parts-engine"
|
|
2
|
+
import { jlcPartsEngine, cache } from "../lib/jlc-parts-engine"
|
|
3
3
|
import type { AnySourceComponent } from "circuit-json"
|
|
4
4
|
describe("jlcPartsEngine", () => {
|
|
5
5
|
beforeEach(() => {
|
|
6
|
+
cache.clear()
|
|
6
7
|
// Reset fetch fake between tests
|
|
7
8
|
globalThis.fetch = (async (url: string) => {
|
|
8
9
|
if (url.includes("/resistors/")) {
|
|
@@ -431,4 +432,42 @@ describe("jlcPartsEngine", () => {
|
|
|
431
432
|
|
|
432
433
|
expect(result).toEqual({ jlcpcb: [] })
|
|
433
434
|
})
|
|
435
|
+
|
|
436
|
+
test("should prefer basic parts when available", async () => {
|
|
437
|
+
// Override the global fetch mock for this specific test
|
|
438
|
+
globalThis.fetch = (async (url: string) => {
|
|
439
|
+
if (url.includes("/resistors/")) {
|
|
440
|
+
return {
|
|
441
|
+
json: async () => ({
|
|
442
|
+
resistors: [
|
|
443
|
+
{ lcsc: "1111" }, // is_basic is undefined, treated as false
|
|
444
|
+
{ lcsc: "2222", is_basic: true },
|
|
445
|
+
{ lcsc: "3333", is_basic: false },
|
|
446
|
+
{ lcsc: "4444", is_basic: true },
|
|
447
|
+
{ lcsc: "5555" },
|
|
448
|
+
],
|
|
449
|
+
}),
|
|
450
|
+
} as Response
|
|
451
|
+
}
|
|
452
|
+
return {} as Response
|
|
453
|
+
}) as unknown as typeof fetch
|
|
454
|
+
|
|
455
|
+
const resistor: AnySourceComponent = {
|
|
456
|
+
type: "source_component",
|
|
457
|
+
ftype: "simple_resistor",
|
|
458
|
+
resistance: 10000,
|
|
459
|
+
source_component_id: "source_component_0",
|
|
460
|
+
name: "R1",
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const result = await jlcPartsEngine.findPart({
|
|
464
|
+
sourceComponent: resistor,
|
|
465
|
+
footprinterString: "0603",
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
// Expecting basic parts to be prioritized
|
|
469
|
+
expect(result).toEqual({
|
|
470
|
+
jlcpcb: ["C2222", "C4444", "C1111"],
|
|
471
|
+
})
|
|
472
|
+
})
|
|
434
473
|
})
|