easyeda 0.0.2
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/README.md +59 -0
- package/ai-assist-docs/soup-reference.md +1325 -0
- package/bun.lockb +0 -0
- package/cli/main.ts +67 -0
- package/dist/cli/main.cjs +29264 -0
- package/dist/cli/main.cjs.map +1 -0
- package/dist/cli/main.d.cts +1 -0
- package/dist/index.cjs +29196 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1553 -0
- package/dist/lib/index.cjs +29196 -0
- package/dist/lib/index.cjs.map +1 -0
- package/dist/lib/index.d.cts +1553 -0
- package/lib/convert-easyeda-json-to-tscircuit-soup-json.ts +204 -0
- package/lib/fetch-easyeda-json.ts +66 -0
- package/lib/index.ts +2 -0
- package/lib/math/arc-utils.ts +170 -0
- package/lib/schemas/easy-eda-json-schema.ts +158 -0
- package/lib/schemas/package-detail-shape-schema.ts +251 -0
- package/lib/schemas/single-letter-shape-schema.ts +201 -0
- package/package.json +31 -0
- package/renovate.json +6 -0
- package/scripts/get-easyeda-json.ts +12 -0
- package/tests/assets/a555-timer-dip.raweasy.json +244 -0
- package/tests/assets/a555-timer-smd.raweasy.json +226 -0
- package/tests/assets/esp32.raweasy.json +333 -0
- package/tests/assets/usb-c.raweasy.json +263 -0
- package/tests/convert-to-soup-tests/a555timer-smd.test.ts +18 -0
- package/tests/convert-to-soup-tests/convert-easyeda-to-tscircuit-soup.test.ts +49 -0
- package/tests/convert-to-soup-tests/convert-usb-c-to-soup.test.ts +18 -0
- package/tests/convert-to-soup-tests/esp32-to-soup.test.ts +13 -0
- package/tests/parse-tests/parse-555-timer-json.test.ts +9 -0
- package/tests/parse-tests/parse-esp32.test.ts +9 -0
- package/tests/parse-tests/parse-usb-c.test.ts +9 -0
- package/tests/parse-tests/single-letter-shape-schema.test.ts +30 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { z } from "zod"
|
|
2
|
+
|
|
3
|
+
export const PointSchema = z
|
|
4
|
+
.any()
|
|
5
|
+
.transform((p) => {
|
|
6
|
+
if (Array.isArray(p)) {
|
|
7
|
+
const [x, y] = p
|
|
8
|
+
return { x, y }
|
|
9
|
+
} else if (typeof p === "object") {
|
|
10
|
+
return p
|
|
11
|
+
}
|
|
12
|
+
throw new Error(`Invalid point: ${p}`)
|
|
13
|
+
})
|
|
14
|
+
.pipe(
|
|
15
|
+
z.object({
|
|
16
|
+
x: z.number(),
|
|
17
|
+
y: z.number(),
|
|
18
|
+
})
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
export const BaseShapeSchema = z.object({
|
|
22
|
+
type: z.string(),
|
|
23
|
+
id: z.string().optional(),
|
|
24
|
+
layer: z.coerce.number().optional(),
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
export const TrackSchema = BaseShapeSchema.extend({
|
|
28
|
+
type: z.literal("TRACK"),
|
|
29
|
+
width: z.coerce.number(),
|
|
30
|
+
points: z.array(PointSchema),
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
export const PadSchema = BaseShapeSchema.extend({
|
|
34
|
+
type: z.literal("PAD"),
|
|
35
|
+
shape: z.enum(["RECT", "ELLIPSE", "OVAL"]),
|
|
36
|
+
center: z.object({
|
|
37
|
+
x: z.number(),
|
|
38
|
+
y: z.number(),
|
|
39
|
+
}),
|
|
40
|
+
width: z.number(),
|
|
41
|
+
height: z.number(),
|
|
42
|
+
layermask: z.number(),
|
|
43
|
+
net: z.union([z.string(), z.number()]).optional(),
|
|
44
|
+
number: z.number(),
|
|
45
|
+
holeRadius: z.number(),
|
|
46
|
+
points: z.array(PointSchema).optional(),
|
|
47
|
+
rotation: z.number().optional(),
|
|
48
|
+
plated: z.boolean(),
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// TODO this should be in "arc sweep" format with the following fields:
|
|
52
|
+
// start, end, radius, largeArc, sweepDirection
|
|
53
|
+
export const ArcSchema = BaseShapeSchema.extend({
|
|
54
|
+
type: z.literal("ARC"),
|
|
55
|
+
width: z.number(),
|
|
56
|
+
start: PointSchema,
|
|
57
|
+
end: PointSchema,
|
|
58
|
+
radiusX: z.number(),
|
|
59
|
+
radiusY: z.number(),
|
|
60
|
+
largeArc: z.boolean(),
|
|
61
|
+
sweepDirection: z.enum(["CW", "CCW"]),
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
export const CircleSchema = BaseShapeSchema.extend({
|
|
65
|
+
type: z.literal("CIRCLE"),
|
|
66
|
+
center: PointSchema,
|
|
67
|
+
radius: z.number(),
|
|
68
|
+
width: z.number(),
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
export const SolidRegionSchema = BaseShapeSchema.extend({
|
|
72
|
+
type: z.literal("SOLIDREGION"),
|
|
73
|
+
layermask: z.number(),
|
|
74
|
+
points: z.array(PointSchema),
|
|
75
|
+
fillStyle: z.string(),
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
export const SVGNodeSchema = BaseShapeSchema.extend({
|
|
79
|
+
type: z.literal("SVGNODE"),
|
|
80
|
+
svgData: z.object({
|
|
81
|
+
gId: z.string(),
|
|
82
|
+
nodeName: z.string(),
|
|
83
|
+
nodeType: z.number(),
|
|
84
|
+
layerid: z.string(),
|
|
85
|
+
attrs: z.record(z.string(), z.string()),
|
|
86
|
+
childNodes: z.array(z.unknown()),
|
|
87
|
+
}),
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// e.g. "HOLE~3999.449~3004.213~1.378~gge152~0"
|
|
91
|
+
export const HoleSchema = BaseShapeSchema.extend({
|
|
92
|
+
type: z.literal("HOLE"),
|
|
93
|
+
center: PointSchema,
|
|
94
|
+
radius: z.number(),
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
export const PackageDetailShapeSchema = z.discriminatedUnion("type", [
|
|
98
|
+
TrackSchema,
|
|
99
|
+
PadSchema,
|
|
100
|
+
ArcSchema,
|
|
101
|
+
CircleSchema,
|
|
102
|
+
SolidRegionSchema,
|
|
103
|
+
SVGNodeSchema,
|
|
104
|
+
HoleSchema,
|
|
105
|
+
])
|
|
106
|
+
|
|
107
|
+
const pairs = <T>(arr: T[]): [T, T][] => {
|
|
108
|
+
const pairs: [T, T][] = []
|
|
109
|
+
for (let i = 0; i < arr.length; i += 2) {
|
|
110
|
+
pairs.push([arr[i], arr[i + 1]])
|
|
111
|
+
}
|
|
112
|
+
return pairs
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const parsePoints = (pointsStr: string): number[][] =>
|
|
116
|
+
pairs(
|
|
117
|
+
pointsStr
|
|
118
|
+
.trim()
|
|
119
|
+
.split(" ")
|
|
120
|
+
.map((n) => Number(n))
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
export const ShapeItemSchema = z
|
|
124
|
+
.object({
|
|
125
|
+
type: z.string(),
|
|
126
|
+
data: z.string(),
|
|
127
|
+
})
|
|
128
|
+
.transform((shape) => {
|
|
129
|
+
const [firstParam, ...restParams] = shape.data.split("~")
|
|
130
|
+
const lastParam = restParams.pop()
|
|
131
|
+
|
|
132
|
+
switch (shape.type) {
|
|
133
|
+
case "TRACK": {
|
|
134
|
+
const [width, layer, _, pointsStr, id, _n] = shape.data.split("~")
|
|
135
|
+
const points = parsePoints(pointsStr)
|
|
136
|
+
return TrackSchema.parse({ type: "TRACK", width, layer, points, id })
|
|
137
|
+
}
|
|
138
|
+
case "PAD": {
|
|
139
|
+
const [padShape, ...params] = shape.data.split("~")
|
|
140
|
+
const [
|
|
141
|
+
centerX,
|
|
142
|
+
centerY,
|
|
143
|
+
width,
|
|
144
|
+
height,
|
|
145
|
+
layermask,
|
|
146
|
+
net,
|
|
147
|
+
number,
|
|
148
|
+
holeRadius,
|
|
149
|
+
...rest
|
|
150
|
+
] = params.map((p) => (isNaN(Number(p)) ? p : Number(p)))
|
|
151
|
+
const center = { x: centerX, y: centerY }
|
|
152
|
+
let points, rotation
|
|
153
|
+
if (padShape === "RECT") {
|
|
154
|
+
points = parsePoints(rest[0] as any)
|
|
155
|
+
rotation = Number(rest[1])
|
|
156
|
+
}
|
|
157
|
+
return PadSchema.parse({
|
|
158
|
+
type: "PAD",
|
|
159
|
+
shape: padShape,
|
|
160
|
+
center,
|
|
161
|
+
width,
|
|
162
|
+
height,
|
|
163
|
+
layermask,
|
|
164
|
+
net,
|
|
165
|
+
number,
|
|
166
|
+
holeRadius,
|
|
167
|
+
points,
|
|
168
|
+
rotation,
|
|
169
|
+
plated: rest.includes("Y"),
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
case "ARC": {
|
|
173
|
+
const [width, layer, , arcData] = shape.data.split("~")
|
|
174
|
+
// A rx ry x-axis-rotation large-arc-flag sweep-flag x y
|
|
175
|
+
// A rx ry x-axis-rotation large-arc-flag sweep-flag dx dy
|
|
176
|
+
const [
|
|
177
|
+
,
|
|
178
|
+
startX,
|
|
179
|
+
startY,
|
|
180
|
+
radiusX,
|
|
181
|
+
radiusY,
|
|
182
|
+
xAxisRotation,
|
|
183
|
+
largeArcFlag,
|
|
184
|
+
sweepFlag,
|
|
185
|
+
endX,
|
|
186
|
+
endY,
|
|
187
|
+
] = arcData.match(
|
|
188
|
+
/M ([\d.]+) ([\d.]+) A ([\d.]+) ([\d.]+) ([\d.]+) ([\d.]+) ([\d.]+) ([\d.]+) ([\d.]+)/
|
|
189
|
+
)!
|
|
190
|
+
const start: [number, number] = [Number(startX), Number(startY)]
|
|
191
|
+
const end: [number, number] = [Number(endX), Number(endY)]
|
|
192
|
+
return ArcSchema.parse({
|
|
193
|
+
type: "ARC",
|
|
194
|
+
width: Number(width),
|
|
195
|
+
layer: Number(layer),
|
|
196
|
+
start,
|
|
197
|
+
end,
|
|
198
|
+
radiusX: Number(radiusX),
|
|
199
|
+
radiusY: Number(radiusY),
|
|
200
|
+
largeArc: largeArcFlag === "1",
|
|
201
|
+
sweepDirection: sweepFlag === "1" ? "CW" : "CCW",
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
case "CIRCLE": {
|
|
205
|
+
const [centerX, centerY, radius, width, layer, id] =
|
|
206
|
+
shape.data.split("~")
|
|
207
|
+
const center: [number, number] = [Number(centerX), Number(centerY)]
|
|
208
|
+
return CircleSchema.parse({
|
|
209
|
+
type: "CIRCLE",
|
|
210
|
+
center,
|
|
211
|
+
radius: Number(radius),
|
|
212
|
+
width: Number(width),
|
|
213
|
+
layer: Number(layer),
|
|
214
|
+
id,
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
case "HOLE": {
|
|
218
|
+
const [centerX, centerY, radius, id] = shape.data.split("~")
|
|
219
|
+
const center: [number, number] = [Number(centerX), Number(centerY)]
|
|
220
|
+
return HoleSchema.parse({
|
|
221
|
+
type: "HOLE",
|
|
222
|
+
center,
|
|
223
|
+
radius: Number(radius),
|
|
224
|
+
id,
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
case "SOLIDREGION": {
|
|
228
|
+
const [layermask, , pathData, fillStyle, id] = shape.data.split("~")
|
|
229
|
+
const points = pathData
|
|
230
|
+
.match(/[ML] ?([\d.]+[ ,][\d.]+)/g)!
|
|
231
|
+
.map((point) => point.slice(2).split(/[ ,]/).map(Number))
|
|
232
|
+
return SolidRegionSchema.parse({
|
|
233
|
+
type: "SOLIDREGION",
|
|
234
|
+
layermask: Number(layermask),
|
|
235
|
+
points,
|
|
236
|
+
fillStyle,
|
|
237
|
+
id,
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
case "SVGNODE": {
|
|
241
|
+
const svgData = JSON.parse(shape.data)
|
|
242
|
+
return SVGNodeSchema.parse({ type: "SVGNODE", svgData })
|
|
243
|
+
}
|
|
244
|
+
default:
|
|
245
|
+
throw new Error(`Unknown shape type: ${shape.type}`)
|
|
246
|
+
return BaseShapeSchema.parse({ type: shape.type })
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
.pipe(PackageDetailShapeSchema)
|
|
250
|
+
|
|
251
|
+
export const ShapesArraySchema = z.array(ShapeItemSchema)
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { z } from "zod"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
I'll break down the elements in the `dataStr.head.shape` array and explain what they represent. This array contains instructions for drawing the schematic symbol of the component.
|
|
5
|
+
1. `"R~365~275~2~2~70~50~#880000~1~0~none~gge1~0~"`
|
|
6
|
+
This represents a rectangle:
|
|
7
|
+
- Starting position: (365, 275)
|
|
8
|
+
- Width: 70
|
|
9
|
+
- Height: 50
|
|
10
|
+
- Color: #880000 (dark red)
|
|
11
|
+
- Line width: 1
|
|
12
|
+
- Other parameters are for internal use
|
|
13
|
+
2. `"E~370~280~1.5~1.5~#880000~1~0~#880000~gge2~0"`
|
|
14
|
+
This draws an ellipse:
|
|
15
|
+
- Center: (370, 280)
|
|
16
|
+
- Radius X: 1.5
|
|
17
|
+
- Radius Y: 1.5
|
|
18
|
+
- Color: #880000 (dark red)
|
|
19
|
+
- Line width: 1
|
|
20
|
+
3-10. `"P~show~0~1~355~285~180~gge5~0^^355~285^^M355,285h10~#000000^^1~368.7~289~0~GND~start~~~#000000^^1~364.5~284~0~1~end~~~#000000^^0~362~285^^0~M 365 288 L 368 285 L 365 282"`
|
|
21
|
+
These represent pins of the component. Each pin has:
|
|
22
|
+
- Pin number
|
|
23
|
+
- Position
|
|
24
|
+
- Orientation
|
|
25
|
+
- Label (e.g., GND, TRIG, OUT)
|
|
26
|
+
- Color
|
|
27
|
+
- Drawing instructions for the pin shape
|
|
28
|
+
To parse this data:
|
|
29
|
+
1. Split the string by `~` to get individual parameters.
|
|
30
|
+
2. For rectangles (R), use parameters 2-7 for position, size, and color.
|
|
31
|
+
3. For ellipses (E), use parameters 2-5 for position and size, and 6 for color.
|
|
32
|
+
4. For pins (P), split further by `^^` to get different sections:
|
|
33
|
+
- First section: general pin properties
|
|
34
|
+
- Second section: pin position
|
|
35
|
+
- Third section: line drawing instruction
|
|
36
|
+
- Fourth and Fifth sections: label position and text
|
|
37
|
+
- Sixth section: additional drawing instruction for pin shape
|
|
38
|
+
This data structure allows for a compact representation of the schematic symbol, which can be rendered by CAD software to display the component in circuit diagrams.
|
|
39
|
+
*/
|
|
40
|
+
// Examples
|
|
41
|
+
// "R~365~275~2~2~70~50~#880000~1~0~none~gge1~0~",
|
|
42
|
+
// "E~370~280~1.5~1.5~#880000~1~0~#880000~gge2~0",
|
|
43
|
+
// "P~show~0~1~355~285~180~gge5~0^^355~285^^M355,285h10~#000000^^1~368.7~289~0~GND~start~~~#000000^^1~364.5~284~0~1~end~~~#000000^^0~362~285^^0~M 365 288 L 368 285 L 365 282",
|
|
44
|
+
// "P~show~0~2~355~295~180~gge6~0^^355~295^^M355,295h10~#880000^^1~368.7~299~0~TRIG~start~~~#0000FF^^1~364.5~294~0~2~end~~~#0000FF^^0~362~295^^0~M 365 298 L 368 295 L 365 292",
|
|
45
|
+
// "P~show~0~3~355~305~180~gge7~0^^355~305^^M355,305h10~#880000^^1~368.7~309~0~OUT~start~~~#0000FF^^1~364.5~304~0~3~end~~~#0000FF^^0~362~305^^0~M 365 308 L 368 305 L 365 302",
|
|
46
|
+
// "P~show~0~4~355~315~180~gge8~0^^355~315^^M355,315h10~#880000^^1~368.7~319~0~RESET~start~~~#0000FF^^1~364.5~314~0~4~end~~~#0000FF^^0~362~315^^0~M 365 318 L 368 315 L 365 312",
|
|
47
|
+
// "P~show~0~5~445~315~0~gge9~0^^445~315^^M445,315h-10~#880000^^1~431.3~319~0~CONT~end~~~#0000FF^^1~435.5~314~0~5~start~~~#0000FF^^0~438~315^^0~M 435 312 L 432 315 L 435 318",
|
|
48
|
+
// "P~show~0~6~445~305~0~gge10~0^^445~305^^M445,305h-10~#880000^^1~431.3~309~0~THRES~end~~~#0000FF^^1~435.5~304~0~6~start~~~#0000FF^^0~438~305^^0~M 435 302 L 432 305 L 435 308",
|
|
49
|
+
// "P~show~0~7~445~295~0~gge11~0^^445~295^^M445,295h-10~#880000^^1~431.3~299~0~DISCH~end~~~#0000FF^^1~435.5~294~0~7~start~~~#0000FF^^0~438~295^^0~M 435 292 L 432 295 L 435 298",
|
|
50
|
+
// "P~show~0~8~445~285~0~gge12~0^^445~285^^M445,285h-10~#FF0000^^1~431.3~289~0~VCC~end~~~#FF0000^^1~435.5~284~0~8~start~~~#FF0000^^0~438~285^^0~M 435 282 L 432 285 L 435 288",
|
|
51
|
+
|
|
52
|
+
const PointSchema = z.object({
|
|
53
|
+
x: z.number(),
|
|
54
|
+
y: z.number(),
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const RectangleShapeOutputSchema = z.object({
|
|
58
|
+
type: z.literal("RECTANGLE"),
|
|
59
|
+
position: PointSchema,
|
|
60
|
+
width: z.number(),
|
|
61
|
+
height: z.number(),
|
|
62
|
+
color: z.string(),
|
|
63
|
+
lineWidth: z.number(),
|
|
64
|
+
id: z.string(),
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
export const RectangleShapeSchema = z
|
|
68
|
+
.string()
|
|
69
|
+
.startsWith("R~")
|
|
70
|
+
.transform((str): z.infer<typeof RectangleShapeOutputSchema> => {
|
|
71
|
+
const [, x, y, , , width, height, color, lineWidth, , , id] = str.split("~")
|
|
72
|
+
return {
|
|
73
|
+
type: "RECTANGLE",
|
|
74
|
+
position: { x: Number(x), y: Number(y) },
|
|
75
|
+
width: Number(width),
|
|
76
|
+
height: Number(height),
|
|
77
|
+
color,
|
|
78
|
+
lineWidth: Number(lineWidth),
|
|
79
|
+
id,
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
.pipe(RectangleShapeOutputSchema)
|
|
83
|
+
|
|
84
|
+
const EllipseShapeOutputSchema = z.object({
|
|
85
|
+
type: z.literal("ELLIPSE"),
|
|
86
|
+
center: PointSchema,
|
|
87
|
+
radiusX: z.number(),
|
|
88
|
+
radiusY: z.number(),
|
|
89
|
+
color: z.string(),
|
|
90
|
+
lineWidth: z.number(),
|
|
91
|
+
id: z.string(),
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
export const EllipseShapeSchema = z
|
|
95
|
+
.string()
|
|
96
|
+
.startsWith("E~")
|
|
97
|
+
.transform((str): z.infer<typeof EllipseShapeOutputSchema> => {
|
|
98
|
+
const [, x, y, radiusX, radiusY, color, lineWidth, , , id] = str.split("~")
|
|
99
|
+
return {
|
|
100
|
+
type: "ELLIPSE",
|
|
101
|
+
center: { x: Number(x), y: Number(y) },
|
|
102
|
+
radiusX: Number(radiusX),
|
|
103
|
+
radiusY: Number(radiusY),
|
|
104
|
+
color,
|
|
105
|
+
lineWidth: Number(lineWidth),
|
|
106
|
+
id,
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
.pipe(EllipseShapeOutputSchema)
|
|
110
|
+
|
|
111
|
+
// type PinDirection = 'left' | 'right';
|
|
112
|
+
|
|
113
|
+
// interface Pin {
|
|
114
|
+
// type: 'P';
|
|
115
|
+
// visibility: string;
|
|
116
|
+
// id: string;
|
|
117
|
+
// pin: number;
|
|
118
|
+
// x: number;
|
|
119
|
+
// y: number;
|
|
120
|
+
// rotation: number;
|
|
121
|
+
// name: string;
|
|
122
|
+
// color: string;
|
|
123
|
+
// path: string;
|
|
124
|
+
// arrow: string;
|
|
125
|
+
// direction: PinDirection;
|
|
126
|
+
// }
|
|
127
|
+
const PinShapeOutputSchema = z.object({
|
|
128
|
+
type: z.literal("PIN"),
|
|
129
|
+
visibility: z.enum(["show", "hide"]),
|
|
130
|
+
pinNumber: z.number(),
|
|
131
|
+
x: z.number(),
|
|
132
|
+
y: z.number(),
|
|
133
|
+
rotation: z.number(),
|
|
134
|
+
id: z.string(),
|
|
135
|
+
label: z.string(),
|
|
136
|
+
labelColor: z.string(),
|
|
137
|
+
path: z.string(),
|
|
138
|
+
arrow: z.string(),
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
function parsePin(pinString: string): z.infer<typeof PinShapeOutputSchema> {
|
|
142
|
+
const parts = pinString.split("~")
|
|
143
|
+
const [_, visibility, __, pinNumber, x, y, rotation, id] = parts
|
|
144
|
+
|
|
145
|
+
const nameMatch = pinString.match(/~(\w+)~start~/)
|
|
146
|
+
const label = nameMatch ? nameMatch[1] : ""
|
|
147
|
+
|
|
148
|
+
// TODO: make sure colors are associated properly
|
|
149
|
+
const colorMatch = pinString.match(/#[0-9A-F]{6}/)
|
|
150
|
+
const labelColor = colorMatch ? colorMatch[0] : ""
|
|
151
|
+
|
|
152
|
+
const pathMatch = pinString.match(/\^\^([^~]+)/)
|
|
153
|
+
const path = pathMatch ? pathMatch[1] : ""
|
|
154
|
+
|
|
155
|
+
const arrowMatch = pinString.match(/\^\^0~(.+)$/)
|
|
156
|
+
const arrow = arrowMatch ? arrowMatch[1] : ""
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
type: "PIN",
|
|
160
|
+
visibility: visibility as "show" | "hide",
|
|
161
|
+
id,
|
|
162
|
+
pinNumber: parseInt(pinNumber),
|
|
163
|
+
x: parseFloat(x),
|
|
164
|
+
y: parseFloat(y),
|
|
165
|
+
rotation: parseFloat(rotation),
|
|
166
|
+
label,
|
|
167
|
+
labelColor,
|
|
168
|
+
path,
|
|
169
|
+
arrow,
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export const PinShapeSchema = z
|
|
174
|
+
.string()
|
|
175
|
+
.startsWith("P~")
|
|
176
|
+
.transform((str): z.infer<typeof PinShapeOutputSchema> => {
|
|
177
|
+
return parsePin(str)
|
|
178
|
+
})
|
|
179
|
+
.pipe(PinShapeOutputSchema)
|
|
180
|
+
|
|
181
|
+
export const SingleLetterShapeSchema = z
|
|
182
|
+
.string()
|
|
183
|
+
.transform((x) => {
|
|
184
|
+
// We do some discrimination here for better errors
|
|
185
|
+
if (x.startsWith("R~")) {
|
|
186
|
+
return RectangleShapeSchema.parse(x)
|
|
187
|
+
} else if (x.startsWith("E~")) {
|
|
188
|
+
return EllipseShapeSchema.parse(x)
|
|
189
|
+
} else if (x.startsWith("P~")) {
|
|
190
|
+
return PinShapeSchema.parse(x)
|
|
191
|
+
} else {
|
|
192
|
+
throw new Error("Invalid shape type: " + x)
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
.pipe(
|
|
196
|
+
z.union([
|
|
197
|
+
RectangleShapeOutputSchema,
|
|
198
|
+
EllipseShapeOutputSchema,
|
|
199
|
+
PinShapeOutputSchema,
|
|
200
|
+
])
|
|
201
|
+
)
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "easyeda",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.2",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"test": "bun test",
|
|
7
|
+
"build": "tsup lib/index.ts cli/main.ts --dts --sourcemap",
|
|
8
|
+
"aider": "aider"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/lib/index.cjs",
|
|
11
|
+
"bin": {
|
|
12
|
+
"easyeda-converter": "./dist/lib/cli/main.cjs",
|
|
13
|
+
"easyeda": "./dist/lib/cli/main.cjs"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@tscircuit/builder": "^1.5.131",
|
|
17
|
+
"@tscircuit/log-soup": "1.0.2",
|
|
18
|
+
"@tscircuit/soup": "^0.0.37",
|
|
19
|
+
"@tscircuit/soup-util": "^0.0.11",
|
|
20
|
+
"@types/bun": "latest",
|
|
21
|
+
"tsup": "^8.1.0"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"typescript": "^5.5.2"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"commander": "^12.1.0",
|
|
28
|
+
"transformation-matrix": "^2.16.1",
|
|
29
|
+
"zod": "^3.23.8"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/renovate.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { fetchEasyEDAComponent } from "../lib/fetch-easyeda-json"
|
|
2
|
+
import * as fs from "node:fs"
|
|
3
|
+
|
|
4
|
+
// "C46749" // NE555P - DIP8
|
|
5
|
+
// const partNumber = "C5125085" - SSOP8
|
|
6
|
+
const partNumber = "C95209" // esp32
|
|
7
|
+
|
|
8
|
+
const outputFile = "./test.json"
|
|
9
|
+
|
|
10
|
+
const easyEdaJson = await fetchEasyEDAComponent(partNumber)
|
|
11
|
+
|
|
12
|
+
fs.writeFileSync(outputFile, JSON.stringify(easyEdaJson, null, " "))
|