circuit-json-to-kicad 0.0.32 → 0.0.33
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/dist/index.d.ts +80 -1
- package/dist/index.js +466 -21
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -20,14 +20,25 @@ interface PcbNetInfo {
|
|
|
20
20
|
interface SymbolEntry {
|
|
21
21
|
symbolName: string;
|
|
22
22
|
symbol: SchematicSymbol;
|
|
23
|
+
/**
|
|
24
|
+
* Whether this symbol is from a standard library footprint (has footprinter_string).
|
|
25
|
+
* If true, this is a builtin/standard symbol. If false, it's a custom/inline symbol.
|
|
26
|
+
*/
|
|
27
|
+
isBuiltin?: boolean;
|
|
23
28
|
}
|
|
24
29
|
interface FootprintEntry {
|
|
25
30
|
footprintName: string;
|
|
26
31
|
kicadModString: string;
|
|
27
32
|
model3dSourcePaths: string[];
|
|
33
|
+
/**
|
|
34
|
+
* Whether this footprint is from a standard library footprint (has footprinter_string).
|
|
35
|
+
* If true, this is a builtin/standard footprint. If false, it's a custom/inline footprint.
|
|
36
|
+
*/
|
|
37
|
+
isBuiltin?: boolean;
|
|
28
38
|
}
|
|
29
39
|
interface KicadLibraryOutput {
|
|
30
40
|
kicadSymString: string;
|
|
41
|
+
symbols: SymbolEntry[];
|
|
31
42
|
footprints: FootprintEntry[];
|
|
32
43
|
fpLibTableString: string;
|
|
33
44
|
symLibTableString: string;
|
|
@@ -201,4 +212,72 @@ declare class CircuitJsonToKicadLibraryConverter {
|
|
|
201
212
|
getModel3dSourcePaths(): string[];
|
|
202
213
|
}
|
|
203
214
|
|
|
204
|
-
|
|
215
|
+
interface KicadLibraryConverterOptions {
|
|
216
|
+
/**
|
|
217
|
+
* Name for the generated KiCad library (e.g., "my-library").
|
|
218
|
+
* This will be used for the user library files.
|
|
219
|
+
*/
|
|
220
|
+
kicadLibraryName?: string;
|
|
221
|
+
/**
|
|
222
|
+
* The main entry point file for the library (e.g., "lib/my-library.ts").
|
|
223
|
+
* This file's exports define the public API of the library.
|
|
224
|
+
*/
|
|
225
|
+
entrypoint: string;
|
|
226
|
+
/**
|
|
227
|
+
* Callback to build circuit JSON from a file path and export name.
|
|
228
|
+
* Should handle both board components and symbol components:
|
|
229
|
+
* - For board components: render inside a <board> element
|
|
230
|
+
* - For symbol components: render inside a <chip> with the symbol prop
|
|
231
|
+
* (Note: tscircuit symbols cannot render standalone - they must be
|
|
232
|
+
* used as a prop on a <chip> component)
|
|
233
|
+
* Return null if the export cannot be rendered.
|
|
234
|
+
*/
|
|
235
|
+
buildFileToCircuitJson: (filePath: string, componentName: string) => Promise<CircuitJson | null>;
|
|
236
|
+
/**
|
|
237
|
+
* Callback to get all exports from a TSX/TS file.
|
|
238
|
+
* Must evaluate the file (not just parse) to handle `export * from` patterns.
|
|
239
|
+
*/
|
|
240
|
+
getExportsFromTsxFile: (filePath: string) => Promise<string[]>;
|
|
241
|
+
/**
|
|
242
|
+
* Callback to resolve an export name to its file path.
|
|
243
|
+
* Returns the file path where the component is defined, or null if not resolvable.
|
|
244
|
+
*/
|
|
245
|
+
resolveExportPath?: (entrypoint: string, exportName: string) => Promise<string | null>;
|
|
246
|
+
/**
|
|
247
|
+
* Whether to include builtin footprints/symbols (like 0402, soic8).
|
|
248
|
+
* Default: true
|
|
249
|
+
*/
|
|
250
|
+
includeBuiltins?: boolean;
|
|
251
|
+
}
|
|
252
|
+
interface KicadLibraryConverterOutput {
|
|
253
|
+
/**
|
|
254
|
+
* Map of file paths to file contents for the generated KiCad library.
|
|
255
|
+
*/
|
|
256
|
+
kicadProjectFsMap: Record<string, string | Buffer>;
|
|
257
|
+
/**
|
|
258
|
+
* Source paths to 3D model files that need to be copied.
|
|
259
|
+
*/
|
|
260
|
+
model3dSourcePaths: string[];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Converts tscircuit component files to a KiCad library.
|
|
265
|
+
*/
|
|
266
|
+
declare class KicadLibraryConverter {
|
|
267
|
+
private options;
|
|
268
|
+
private output;
|
|
269
|
+
private ctx;
|
|
270
|
+
constructor(options: KicadLibraryConverterOptions);
|
|
271
|
+
run(): Promise<void>;
|
|
272
|
+
/**
|
|
273
|
+
* Builds tscircuit components to circuit-json.
|
|
274
|
+
*/
|
|
275
|
+
private buildTscircuitComponents;
|
|
276
|
+
/**
|
|
277
|
+
* Extracts KiCad footprints and symbols from built tscircuit components.
|
|
278
|
+
*/
|
|
279
|
+
private extractKicadComponents;
|
|
280
|
+
getOutput(): KicadLibraryConverterOutput;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export { CircuitJsonToKicadLibraryConverter, CircuitJsonToKicadPcbConverter, CircuitJsonToKicadProConverter, CircuitJsonToKicadSchConverter, type FootprintEntry, KicadLibraryConverter, type KicadLibraryConverterOptions, type KicadLibraryConverterOutput, type KicadLibraryOutput, type SymbolEntry };
|
package/dist/index.js
CHANGED
|
@@ -73,7 +73,7 @@ import {
|
|
|
73
73
|
TextEffectsFont,
|
|
74
74
|
Xy
|
|
75
75
|
} from "kicadts";
|
|
76
|
-
import { symbols } from "schematic-symbols";
|
|
76
|
+
import { symbols as symbols2 } from "schematic-symbols";
|
|
77
77
|
|
|
78
78
|
// lib/utils/getKicadCompatibleComponentName.ts
|
|
79
79
|
function getKicadCompatibleComponentName(sourceComponent, cadComponent) {
|
|
@@ -103,14 +103,24 @@ function extractReferencePrefix(name) {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
// lib/schematic/getLibraryId.ts
|
|
106
|
+
import { symbols } from "schematic-symbols";
|
|
107
|
+
function isBuiltinSymbol(symbolName) {
|
|
108
|
+
return symbolName in symbols;
|
|
109
|
+
}
|
|
106
110
|
function getLibraryId(sourceComp, schematicComp, cadComponent) {
|
|
107
111
|
if (sourceComp.type !== "source_component") {
|
|
108
112
|
if (schematicComp.symbol_name) {
|
|
113
|
+
if (isBuiltinSymbol(schematicComp.symbol_name)) {
|
|
114
|
+
return `Device:${schematicComp.symbol_name}`;
|
|
115
|
+
}
|
|
109
116
|
return `Custom:${schematicComp.symbol_name}`;
|
|
110
117
|
}
|
|
111
118
|
return "Device:Component";
|
|
112
119
|
}
|
|
113
120
|
if (schematicComp.symbol_name) {
|
|
121
|
+
if (isBuiltinSymbol(schematicComp.symbol_name)) {
|
|
122
|
+
return `Device:${schematicComp.symbol_name}`;
|
|
123
|
+
}
|
|
114
124
|
return `Custom:${schematicComp.symbol_name}`;
|
|
115
125
|
}
|
|
116
126
|
const ergonomicName = getKicadCompatibleComponentName(
|
|
@@ -200,7 +210,7 @@ var AddLibrarySymbolsStage = class extends ConverterStage {
|
|
|
200
210
|
}) {
|
|
201
211
|
const symbolName = netLabel.symbol_name;
|
|
202
212
|
if (!symbolName) return null;
|
|
203
|
-
const symbolData =
|
|
213
|
+
const symbolData = symbols2[symbolName];
|
|
204
214
|
if (!symbolData) return null;
|
|
205
215
|
const libId = `Custom:${symbolName}`;
|
|
206
216
|
return this.createLibrarySymbol({
|
|
@@ -220,7 +230,7 @@ var AddLibrarySymbolsStage = class extends ConverterStage {
|
|
|
220
230
|
if (symbolName.startsWith("generic_chip_")) {
|
|
221
231
|
return this.createGenericChipSymbolData(schematicComponent, this.ctx.db);
|
|
222
232
|
}
|
|
223
|
-
return
|
|
233
|
+
return symbols2[symbolName] || null;
|
|
224
234
|
}
|
|
225
235
|
/**
|
|
226
236
|
* Create generic chip symbol data for chips without a symbol_name
|
|
@@ -620,7 +630,7 @@ import {
|
|
|
620
630
|
TextEffectsJustify
|
|
621
631
|
} from "kicadts";
|
|
622
632
|
import { applyToPoint as applyToPoint2 } from "transformation-matrix";
|
|
623
|
-
import { symbols as
|
|
633
|
+
import { symbols as symbols3 } from "schematic-symbols";
|
|
624
634
|
var AddSchematicSymbolsStage = class extends ConverterStage {
|
|
625
635
|
_step() {
|
|
626
636
|
const { kicadSch, db } = this.ctx;
|
|
@@ -629,7 +639,7 @@ var AddSchematicSymbolsStage = class extends ConverterStage {
|
|
|
629
639
|
this.finished = true;
|
|
630
640
|
return;
|
|
631
641
|
}
|
|
632
|
-
const
|
|
642
|
+
const symbols4 = [];
|
|
633
643
|
for (const schematicComponent of schematicComponents) {
|
|
634
644
|
const sourceComponent = schematicComponent.source_component_id ? db.source_component.get(schematicComponent.source_component_id) : null;
|
|
635
645
|
if (!sourceComponent) continue;
|
|
@@ -724,10 +734,10 @@ var AddSchematicSymbolsStage = class extends ConverterStage {
|
|
|
724
734
|
project.paths.push(path);
|
|
725
735
|
instances.projects.push(project);
|
|
726
736
|
symbol._sxInstances = instances;
|
|
727
|
-
|
|
737
|
+
symbols4.push(symbol);
|
|
728
738
|
}
|
|
729
739
|
if (kicadSch) {
|
|
730
|
-
kicadSch.symbols =
|
|
740
|
+
kicadSch.symbols = symbols4;
|
|
731
741
|
}
|
|
732
742
|
this.finished = true;
|
|
733
743
|
}
|
|
@@ -748,7 +758,7 @@ var AddSchematicSymbolsStage = class extends ConverterStage {
|
|
|
748
758
|
return { refTextPos: refTextPos2, valTextPos: valTextPos2 };
|
|
749
759
|
}
|
|
750
760
|
const symbolName = schematicComponent.symbol_name;
|
|
751
|
-
const symbol =
|
|
761
|
+
const symbol = symbols3[symbolName];
|
|
752
762
|
if (!symbol) {
|
|
753
763
|
return {
|
|
754
764
|
refTextPos: { x: symbolKicadPos.x, y: symbolKicadPos.y - 6 },
|
|
@@ -874,7 +884,7 @@ var AddSchematicNetLabelsStage = class extends ConverterStage {
|
|
|
874
884
|
this.finished = true;
|
|
875
885
|
return;
|
|
876
886
|
}
|
|
877
|
-
const
|
|
887
|
+
const symbols4 = [];
|
|
878
888
|
const globalLabels = [];
|
|
879
889
|
for (const netLabel of netLabels) {
|
|
880
890
|
const labelText = netLabel.text || "";
|
|
@@ -886,7 +896,7 @@ var AddSchematicNetLabelsStage = class extends ConverterStage {
|
|
|
886
896
|
symbolName
|
|
887
897
|
);
|
|
888
898
|
if (symbol) {
|
|
889
|
-
|
|
899
|
+
symbols4.push(symbol);
|
|
890
900
|
}
|
|
891
901
|
} else {
|
|
892
902
|
const label = this.createGlobalLabel(netLabel, labelText);
|
|
@@ -895,9 +905,9 @@ var AddSchematicNetLabelsStage = class extends ConverterStage {
|
|
|
895
905
|
}
|
|
896
906
|
}
|
|
897
907
|
}
|
|
898
|
-
if (kicadSch &&
|
|
908
|
+
if (kicadSch && symbols4.length > 0) {
|
|
899
909
|
const existingSymbols = kicadSch.symbols || [];
|
|
900
|
-
kicadSch.symbols = [...existingSymbols, ...
|
|
910
|
+
kicadSch.symbols = [...existingSymbols, ...symbols4];
|
|
901
911
|
}
|
|
902
912
|
if (kicadSch && globalLabels.length > 0) {
|
|
903
913
|
kicadSch.globalLabels = [
|
|
@@ -2490,6 +2500,13 @@ var GenerateKicadSchAndPcbStage = class extends ConverterStage {
|
|
|
2490
2500
|
// lib/kicad-library/stages/ExtractSymbolsStage.ts
|
|
2491
2501
|
import { parseKicadSexpr, KicadSch as KicadSch2 } from "kicadts";
|
|
2492
2502
|
var ExtractSymbolsStage = class extends ConverterStage {
|
|
2503
|
+
/**
|
|
2504
|
+
* Checks if a symbol is custom (user-specified symbol={<symbol>...</symbol>}).
|
|
2505
|
+
* Custom symbols have libraryId starting with "Custom:".
|
|
2506
|
+
*/
|
|
2507
|
+
isCustomSymbol(libraryId) {
|
|
2508
|
+
return libraryId?.startsWith("Custom:") ?? false;
|
|
2509
|
+
}
|
|
2493
2510
|
_step() {
|
|
2494
2511
|
const schContent = this.ctx.kicadSchString;
|
|
2495
2512
|
const fpLibraryName = this.ctx.fpLibraryName ?? "tscircuit";
|
|
@@ -2515,15 +2532,17 @@ var ExtractSymbolsStage = class extends ConverterStage {
|
|
|
2515
2532
|
this.finished = true;
|
|
2516
2533
|
return;
|
|
2517
2534
|
}
|
|
2518
|
-
const
|
|
2519
|
-
for (const symbol of
|
|
2535
|
+
const symbols4 = libSymbols.symbols ?? [];
|
|
2536
|
+
for (const symbol of symbols4) {
|
|
2537
|
+
const isCustom = this.isCustomSymbol(symbol.libraryId);
|
|
2520
2538
|
const symbolName = this.sanitizeSymbolName(symbol.libraryId);
|
|
2521
2539
|
if (!uniqueSymbols.has(symbolName)) {
|
|
2522
2540
|
symbol.libraryId = symbolName;
|
|
2523
2541
|
this.updateFootprintProperty(symbol, fpLibraryName);
|
|
2524
2542
|
uniqueSymbols.set(symbolName, {
|
|
2525
2543
|
symbolName,
|
|
2526
|
-
symbol
|
|
2544
|
+
symbol,
|
|
2545
|
+
isBuiltin: !isCustom
|
|
2527
2546
|
});
|
|
2528
2547
|
}
|
|
2529
2548
|
}
|
|
@@ -2573,6 +2592,28 @@ function getBasename(filePath) {
|
|
|
2573
2592
|
return parts[parts.length - 1] || filePath;
|
|
2574
2593
|
}
|
|
2575
2594
|
var ExtractFootprintsStage = class extends ConverterStage {
|
|
2595
|
+
/**
|
|
2596
|
+
* Builds a set of custom footprint names.
|
|
2597
|
+
* These are components WITHOUT footprinter_string.
|
|
2598
|
+
*/
|
|
2599
|
+
findCustomFootprintNames() {
|
|
2600
|
+
const customNames = /* @__PURE__ */ new Set();
|
|
2601
|
+
const cadComponents = this.ctx.db.cad_component?.list() ?? [];
|
|
2602
|
+
const sourceComponents = this.ctx.db.source_component;
|
|
2603
|
+
for (const cadComponent of cadComponents) {
|
|
2604
|
+
if (!cadComponent.footprinter_string) {
|
|
2605
|
+
const sourceComp = cadComponent.source_component_id ? sourceComponents?.get(cadComponent.source_component_id) : null;
|
|
2606
|
+
if (sourceComp) {
|
|
2607
|
+
const footprintName = getKicadCompatibleComponentName(
|
|
2608
|
+
sourceComp,
|
|
2609
|
+
cadComponent
|
|
2610
|
+
);
|
|
2611
|
+
customNames.add(footprintName);
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
return customNames;
|
|
2616
|
+
}
|
|
2576
2617
|
_step() {
|
|
2577
2618
|
const kicadPcbString = this.ctx.kicadPcbString;
|
|
2578
2619
|
const fpLibraryName = this.ctx.fpLibraryName ?? "tscircuit";
|
|
@@ -2581,6 +2622,7 @@ var ExtractFootprintsStage = class extends ConverterStage {
|
|
|
2581
2622
|
"PCB content not available. Run GenerateKicadSchAndPcbStage first."
|
|
2582
2623
|
);
|
|
2583
2624
|
}
|
|
2625
|
+
const customFootprintNames = this.findCustomFootprintNames();
|
|
2584
2626
|
const uniqueFootprints = /* @__PURE__ */ new Map();
|
|
2585
2627
|
try {
|
|
2586
2628
|
const parsed = parseKicadSexpr2(kicadPcbString);
|
|
@@ -2594,9 +2636,13 @@ var ExtractFootprintsStage = class extends ConverterStage {
|
|
|
2594
2636
|
}
|
|
2595
2637
|
const footprints = pcb.footprints ?? [];
|
|
2596
2638
|
for (const footprint of footprints) {
|
|
2597
|
-
const
|
|
2598
|
-
|
|
2599
|
-
|
|
2639
|
+
const footprintEntry = this.sanitizeFootprint({
|
|
2640
|
+
footprint,
|
|
2641
|
+
fpLibraryName,
|
|
2642
|
+
customFootprintNames
|
|
2643
|
+
});
|
|
2644
|
+
if (!uniqueFootprints.has(footprintEntry.footprintName)) {
|
|
2645
|
+
uniqueFootprints.set(footprintEntry.footprintName, footprintEntry);
|
|
2600
2646
|
}
|
|
2601
2647
|
}
|
|
2602
2648
|
} catch (error) {
|
|
@@ -2605,10 +2651,15 @@ var ExtractFootprintsStage = class extends ConverterStage {
|
|
|
2605
2651
|
this.ctx.footprintEntries = Array.from(uniqueFootprints.values());
|
|
2606
2652
|
this.finished = true;
|
|
2607
2653
|
}
|
|
2608
|
-
sanitizeFootprint(
|
|
2654
|
+
sanitizeFootprint({
|
|
2655
|
+
footprint,
|
|
2656
|
+
fpLibraryName,
|
|
2657
|
+
customFootprintNames
|
|
2658
|
+
}) {
|
|
2609
2659
|
const libraryLink = footprint.libraryLink ?? "footprint";
|
|
2610
2660
|
const parts = libraryLink.split(":");
|
|
2611
2661
|
const footprintName = (parts.length > 1 ? parts[1] : parts[0])?.replace(/[\\\/]/g, "-").trim() || "footprint";
|
|
2662
|
+
const isBuiltin = !customFootprintNames.has(footprintName);
|
|
2612
2663
|
footprint.libraryLink = footprintName;
|
|
2613
2664
|
footprint.position = At2.from([0, 0, 0]);
|
|
2614
2665
|
footprint.locked = false;
|
|
@@ -2653,7 +2704,8 @@ var ExtractFootprintsStage = class extends ConverterStage {
|
|
|
2653
2704
|
return {
|
|
2654
2705
|
footprintName,
|
|
2655
2706
|
kicadModString: footprint.getString(),
|
|
2656
|
-
model3dSourcePaths: modelFiles
|
|
2707
|
+
model3dSourcePaths: modelFiles,
|
|
2708
|
+
isBuiltin
|
|
2657
2709
|
};
|
|
2658
2710
|
}
|
|
2659
2711
|
getOutput() {
|
|
@@ -2672,6 +2724,7 @@ var GenerateSymbolLibraryStage = class extends ConverterStage {
|
|
|
2672
2724
|
if (!this.ctx.libraryOutput) {
|
|
2673
2725
|
this.ctx.libraryOutput = {
|
|
2674
2726
|
kicadSymString: "",
|
|
2727
|
+
symbols: [],
|
|
2675
2728
|
footprints: [],
|
|
2676
2729
|
fpLibTableString: "",
|
|
2677
2730
|
symLibTableString: "",
|
|
@@ -2700,6 +2753,7 @@ var GenerateLibraryTablesStage = class extends ConverterStage {
|
|
|
2700
2753
|
const libraryName = this.ctx.libraryName ?? "tscircuit";
|
|
2701
2754
|
const fpLibraryName = this.ctx.fpLibraryName ?? "tscircuit";
|
|
2702
2755
|
const footprintEntries = this.ctx.footprintEntries ?? [];
|
|
2756
|
+
const symbolEntries = this.ctx.symbolEntries ?? [];
|
|
2703
2757
|
const model3dSourcePathsSet = /* @__PURE__ */ new Set();
|
|
2704
2758
|
for (const fp of footprintEntries) {
|
|
2705
2759
|
for (const modelPath of fp.model3dSourcePaths) {
|
|
@@ -2711,12 +2765,14 @@ var GenerateLibraryTablesStage = class extends ConverterStage {
|
|
|
2711
2765
|
if (!this.ctx.libraryOutput) {
|
|
2712
2766
|
this.ctx.libraryOutput = {
|
|
2713
2767
|
kicadSymString: "",
|
|
2768
|
+
symbols: [],
|
|
2714
2769
|
footprints: [],
|
|
2715
2770
|
fpLibTableString: "",
|
|
2716
2771
|
symLibTableString: "",
|
|
2717
2772
|
model3dSourcePaths: []
|
|
2718
2773
|
};
|
|
2719
2774
|
}
|
|
2775
|
+
this.ctx.libraryOutput.symbols = symbolEntries;
|
|
2720
2776
|
this.ctx.libraryOutput.footprints = footprintEntries;
|
|
2721
2777
|
this.ctx.libraryOutput.fpLibTableString = fpLibTableString;
|
|
2722
2778
|
this.ctx.libraryOutput.symLibTableString = symLibTableString;
|
|
@@ -2805,9 +2861,398 @@ var CircuitJsonToKicadLibraryConverter = class {
|
|
|
2805
2861
|
return this.getOutput().model3dSourcePaths;
|
|
2806
2862
|
}
|
|
2807
2863
|
};
|
|
2864
|
+
|
|
2865
|
+
// lib/kicad-library/kicad-library-converter-utils/renameKicadFootprint.ts
|
|
2866
|
+
import { parseKicadMod } from "kicadts";
|
|
2867
|
+
function renameKicadFootprint(params) {
|
|
2868
|
+
const { kicadFootprint, newKicadFootprintName, kicadLibraryName } = params;
|
|
2869
|
+
const footprint = parseKicadMod(kicadFootprint.kicadModString);
|
|
2870
|
+
footprint.libraryLink = newKicadFootprintName;
|
|
2871
|
+
for (const model of footprint.models) {
|
|
2872
|
+
const currentPath = model.path;
|
|
2873
|
+
if (currentPath.includes("${KIPRJMOD}/")) {
|
|
2874
|
+
const filename = currentPath.split("/").pop() ?? "";
|
|
2875
|
+
model.path = `\${KIPRJMOD}/3dmodels/${kicadLibraryName}.3dshapes/${filename}`;
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
return {
|
|
2879
|
+
footprintName: newKicadFootprintName,
|
|
2880
|
+
kicadModString: footprint.getString(),
|
|
2881
|
+
model3dSourcePaths: kicadFootprint.model3dSourcePaths
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
// lib/kicad-library/stages/ClassifyKicadFootprintsStage.ts
|
|
2886
|
+
function classifyKicadFootprints(ctx) {
|
|
2887
|
+
for (const extractedKicadComponent of ctx.extractedKicadComponents) {
|
|
2888
|
+
classifyFootprintsForComponent({
|
|
2889
|
+
ctx,
|
|
2890
|
+
extractedKicadComponent
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
function classifyFootprintsForComponent({
|
|
2895
|
+
ctx,
|
|
2896
|
+
extractedKicadComponent
|
|
2897
|
+
}) {
|
|
2898
|
+
const { tscircuitComponentName, kicadFootprints } = extractedKicadComponent;
|
|
2899
|
+
let hasAddedUserFootprint = false;
|
|
2900
|
+
for (const kicadFootprint of kicadFootprints) {
|
|
2901
|
+
if (kicadFootprint.isBuiltin) {
|
|
2902
|
+
addBuiltinFootprint({ ctx, kicadFootprint });
|
|
2903
|
+
} else {
|
|
2904
|
+
if (!hasAddedUserFootprint) {
|
|
2905
|
+
hasAddedUserFootprint = true;
|
|
2906
|
+
const renamedFootprint = renameKicadFootprint({
|
|
2907
|
+
kicadFootprint,
|
|
2908
|
+
newKicadFootprintName: tscircuitComponentName,
|
|
2909
|
+
kicadLibraryName: ctx.kicadLibraryName
|
|
2910
|
+
});
|
|
2911
|
+
addUserFootprint({ ctx, kicadFootprint: renamedFootprint });
|
|
2912
|
+
} else {
|
|
2913
|
+
addUserFootprint({ ctx, kicadFootprint });
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
function addUserFootprint({
|
|
2919
|
+
ctx,
|
|
2920
|
+
kicadFootprint
|
|
2921
|
+
}) {
|
|
2922
|
+
const alreadyExists = ctx.userKicadFootprints.some(
|
|
2923
|
+
(fp) => fp.footprintName === kicadFootprint.footprintName
|
|
2924
|
+
);
|
|
2925
|
+
if (!alreadyExists) {
|
|
2926
|
+
ctx.userKicadFootprints.push(kicadFootprint);
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
function addBuiltinFootprint({
|
|
2930
|
+
ctx,
|
|
2931
|
+
kicadFootprint
|
|
2932
|
+
}) {
|
|
2933
|
+
const alreadyExists = ctx.builtinKicadFootprints.some(
|
|
2934
|
+
(fp) => fp.footprintName === kicadFootprint.footprintName
|
|
2935
|
+
);
|
|
2936
|
+
if (!alreadyExists) {
|
|
2937
|
+
ctx.builtinKicadFootprints.push(kicadFootprint);
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
function componentHasCustomFootprint(extractedKicadComponent) {
|
|
2941
|
+
return extractedKicadComponent.kicadFootprints.some((fp) => !fp.isBuiltin);
|
|
2942
|
+
}
|
|
2943
|
+
|
|
2944
|
+
// lib/kicad-library/kicad-library-converter-utils/renameKicadSymbol.ts
|
|
2945
|
+
function renameKicadSymbol(params) {
|
|
2946
|
+
const { kicadSymbol, newKicadSymbolName } = params;
|
|
2947
|
+
const symbol = kicadSymbol.symbol;
|
|
2948
|
+
const oldName = symbol.libraryId;
|
|
2949
|
+
symbol.libraryId = newKicadSymbolName;
|
|
2950
|
+
if (oldName && symbol.subSymbols) {
|
|
2951
|
+
for (const subSymbol of symbol.subSymbols) {
|
|
2952
|
+
if (subSymbol.libraryId?.startsWith(oldName)) {
|
|
2953
|
+
const suffix = subSymbol.libraryId.slice(oldName.length);
|
|
2954
|
+
subSymbol.libraryId = newKicadSymbolName + suffix;
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
return { symbolName: newKicadSymbolName, symbol };
|
|
2959
|
+
}
|
|
2960
|
+
|
|
2961
|
+
// lib/kicad-library/kicad-library-converter-utils/updateKicadSymbolFootprint.ts
|
|
2962
|
+
function updateKicadSymbolFootprint(params) {
|
|
2963
|
+
const { kicadSymbol, kicadLibraryName, kicadFootprintName } = params;
|
|
2964
|
+
const properties = kicadSymbol.symbol.properties ?? [];
|
|
2965
|
+
for (const prop of properties) {
|
|
2966
|
+
if (prop.key === "Footprint") {
|
|
2967
|
+
prop.value = `${kicadLibraryName}:${kicadFootprintName}`;
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
// lib/kicad-library/kicad-library-converter-utils/updateBuiltinKicadSymbolFootprint.ts
|
|
2973
|
+
function updateBuiltinKicadSymbolFootprint(kicadSymbol) {
|
|
2974
|
+
const symbol = kicadSymbol.symbol;
|
|
2975
|
+
const properties = symbol.properties ?? [];
|
|
2976
|
+
for (const prop of properties) {
|
|
2977
|
+
if (prop.key === "Footprint" && prop.value) {
|
|
2978
|
+
const parts = prop.value.split(":");
|
|
2979
|
+
const footprintName = parts.length > 1 ? parts[1] : parts[0];
|
|
2980
|
+
prop.value = `tscircuit_builtin:${footprintName}`;
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
return { symbolName: kicadSymbol.symbolName, symbol };
|
|
2984
|
+
}
|
|
2985
|
+
|
|
2986
|
+
// lib/kicad-library/stages/ClassifyKicadSymbolsStage.ts
|
|
2987
|
+
function classifyKicadSymbols(ctx) {
|
|
2988
|
+
for (const extractedKicadComponent of ctx.extractedKicadComponents) {
|
|
2989
|
+
classifySymbolsForComponent({
|
|
2990
|
+
ctx,
|
|
2991
|
+
extractedKicadComponent
|
|
2992
|
+
});
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
function classifySymbolsForComponent({
|
|
2996
|
+
ctx,
|
|
2997
|
+
extractedKicadComponent
|
|
2998
|
+
}) {
|
|
2999
|
+
const { tscircuitComponentName, kicadSymbols } = extractedKicadComponent;
|
|
3000
|
+
const hasCustomFootprint = componentHasCustomFootprint(
|
|
3001
|
+
extractedKicadComponent
|
|
3002
|
+
);
|
|
3003
|
+
let hasAddedUserSymbol = false;
|
|
3004
|
+
for (const kicadSymbol of kicadSymbols) {
|
|
3005
|
+
if (!kicadSymbol.isBuiltin) {
|
|
3006
|
+
if (!hasAddedUserSymbol) {
|
|
3007
|
+
hasAddedUserSymbol = true;
|
|
3008
|
+
const renamedSymbol = renameKicadSymbol({
|
|
3009
|
+
kicadSymbol,
|
|
3010
|
+
newKicadSymbolName: tscircuitComponentName
|
|
3011
|
+
});
|
|
3012
|
+
if (hasCustomFootprint) {
|
|
3013
|
+
updateKicadSymbolFootprint({
|
|
3014
|
+
kicadSymbol: renamedSymbol,
|
|
3015
|
+
kicadLibraryName: ctx.kicadLibraryName,
|
|
3016
|
+
kicadFootprintName: tscircuitComponentName
|
|
3017
|
+
});
|
|
3018
|
+
}
|
|
3019
|
+
addUserSymbol({ ctx, kicadSymbol: renamedSymbol });
|
|
3020
|
+
} else {
|
|
3021
|
+
addUserSymbol({ ctx, kicadSymbol });
|
|
3022
|
+
}
|
|
3023
|
+
} else if (hasCustomFootprint && !hasAddedUserSymbol) {
|
|
3024
|
+
hasAddedUserSymbol = true;
|
|
3025
|
+
const renamedSymbol = renameKicadSymbol({
|
|
3026
|
+
kicadSymbol,
|
|
3027
|
+
newKicadSymbolName: tscircuitComponentName
|
|
3028
|
+
});
|
|
3029
|
+
updateKicadSymbolFootprint({
|
|
3030
|
+
kicadSymbol: renamedSymbol,
|
|
3031
|
+
kicadLibraryName: ctx.kicadLibraryName,
|
|
3032
|
+
kicadFootprintName: tscircuitComponentName
|
|
3033
|
+
});
|
|
3034
|
+
addUserSymbol({ ctx, kicadSymbol: renamedSymbol });
|
|
3035
|
+
} else {
|
|
3036
|
+
const updatedSymbol = updateBuiltinKicadSymbolFootprint(kicadSymbol);
|
|
3037
|
+
addBuiltinSymbol({ ctx, kicadSymbol: updatedSymbol });
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
function addUserSymbol({
|
|
3042
|
+
ctx,
|
|
3043
|
+
kicadSymbol
|
|
3044
|
+
}) {
|
|
3045
|
+
const alreadyExists = ctx.userKicadSymbols.some(
|
|
3046
|
+
(s) => s.symbolName === kicadSymbol.symbolName
|
|
3047
|
+
);
|
|
3048
|
+
if (!alreadyExists) {
|
|
3049
|
+
ctx.userKicadSymbols.push(kicadSymbol);
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
function addBuiltinSymbol({
|
|
3053
|
+
ctx,
|
|
3054
|
+
kicadSymbol
|
|
3055
|
+
}) {
|
|
3056
|
+
const alreadyExists = ctx.builtinKicadSymbols.some(
|
|
3057
|
+
(s) => s.symbolName === kicadSymbol.symbolName
|
|
3058
|
+
);
|
|
3059
|
+
if (!alreadyExists) {
|
|
3060
|
+
ctx.builtinKicadSymbols.push(kicadSymbol);
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
|
|
3064
|
+
// lib/kicad-library/stages/BuildKicadLibraryFilesStage.ts
|
|
3065
|
+
import { KicadSymbolLib as KicadSymbolLib2 } from "kicadts";
|
|
3066
|
+
|
|
3067
|
+
// lib/kicad-library/kicad-library-converter-utils/generateSymLibTable.ts
|
|
3068
|
+
function generateSymLibTable(params) {
|
|
3069
|
+
const { kicadLibraryName, includeBuiltin } = params;
|
|
3070
|
+
let content = "(sym_lib_table\n";
|
|
3071
|
+
content += ` (lib (name "${kicadLibraryName}")(type "KiCad")(uri "\${KIPRJMOD}/symbols/${kicadLibraryName}.kicad_sym")(options "")(descr ""))
|
|
3072
|
+
`;
|
|
3073
|
+
if (includeBuiltin) {
|
|
3074
|
+
content += ` (lib (name "tscircuit_builtin")(type "KiCad")(uri "\${KIPRJMOD}/symbols/tscircuit_builtin.kicad_sym")(options "")(descr ""))
|
|
3075
|
+
`;
|
|
3076
|
+
}
|
|
3077
|
+
content += ")\n";
|
|
3078
|
+
return content;
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3081
|
+
// lib/kicad-library/kicad-library-converter-utils/generateFpLibTable.ts
|
|
3082
|
+
function generateFpLibTable(params) {
|
|
3083
|
+
const { kicadLibraryName, includeBuiltin } = params;
|
|
3084
|
+
let content = "(fp_lib_table\n";
|
|
3085
|
+
content += ` (lib (name "${kicadLibraryName}")(type "KiCad")(uri "\${KIPRJMOD}/footprints/${kicadLibraryName}.pretty")(options "")(descr ""))
|
|
3086
|
+
`;
|
|
3087
|
+
if (includeBuiltin) {
|
|
3088
|
+
content += ` (lib (name "tscircuit_builtin")(type "KiCad")(uri "\${KIPRJMOD}/footprints/tscircuit_builtin.pretty")(options "")(descr ""))
|
|
3089
|
+
`;
|
|
3090
|
+
}
|
|
3091
|
+
content += ")\n";
|
|
3092
|
+
return content;
|
|
3093
|
+
}
|
|
3094
|
+
|
|
3095
|
+
// lib/kicad-library/stages/BuildKicadLibraryFilesStage.ts
|
|
3096
|
+
var KICAD_SYM_LIB_VERSION2 = 20211014;
|
|
3097
|
+
var KICAD_GENERATOR = "circuit-json-to-kicad";
|
|
3098
|
+
function buildKicadLibraryFiles(ctx) {
|
|
3099
|
+
buildUserSymbolLibrary(ctx);
|
|
3100
|
+
buildBuiltinSymbolLibrary(ctx);
|
|
3101
|
+
buildUserFootprintLibrary(ctx);
|
|
3102
|
+
buildBuiltinFootprintLibrary(ctx);
|
|
3103
|
+
buildLibraryTables(ctx);
|
|
3104
|
+
}
|
|
3105
|
+
function buildUserSymbolLibrary(ctx) {
|
|
3106
|
+
if (ctx.userKicadSymbols.length === 0) return;
|
|
3107
|
+
const symbolLib = new KicadSymbolLib2({
|
|
3108
|
+
version: KICAD_SYM_LIB_VERSION2,
|
|
3109
|
+
generator: KICAD_GENERATOR,
|
|
3110
|
+
symbols: ctx.userKicadSymbols.map((s) => s.symbol)
|
|
3111
|
+
});
|
|
3112
|
+
ctx.kicadProjectFsMap[`symbols/${ctx.kicadLibraryName}.kicad_sym`] = symbolLib.getString();
|
|
3113
|
+
}
|
|
3114
|
+
function buildBuiltinSymbolLibrary(ctx) {
|
|
3115
|
+
if (!ctx.includeBuiltins || ctx.builtinKicadSymbols.length === 0) return;
|
|
3116
|
+
const symbolLib = new KicadSymbolLib2({
|
|
3117
|
+
version: KICAD_SYM_LIB_VERSION2,
|
|
3118
|
+
generator: KICAD_GENERATOR,
|
|
3119
|
+
symbols: ctx.builtinKicadSymbols.map((s) => s.symbol)
|
|
3120
|
+
});
|
|
3121
|
+
ctx.kicadProjectFsMap["symbols/tscircuit_builtin.kicad_sym"] = symbolLib.getString();
|
|
3122
|
+
}
|
|
3123
|
+
function buildUserFootprintLibrary(ctx) {
|
|
3124
|
+
for (const kicadFootprint of ctx.userKicadFootprints) {
|
|
3125
|
+
const filePath = `footprints/${ctx.kicadLibraryName}.pretty/${kicadFootprint.footprintName}.kicad_mod`;
|
|
3126
|
+
ctx.kicadProjectFsMap[filePath] = kicadFootprint.kicadModString;
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
function buildBuiltinFootprintLibrary(ctx) {
|
|
3130
|
+
if (!ctx.includeBuiltins || ctx.builtinKicadFootprints.length === 0) return;
|
|
3131
|
+
for (const kicadFootprint of ctx.builtinKicadFootprints) {
|
|
3132
|
+
const filePath = `footprints/tscircuit_builtin.pretty/${kicadFootprint.footprintName}.kicad_mod`;
|
|
3133
|
+
ctx.kicadProjectFsMap[filePath] = kicadFootprint.kicadModString;
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
function buildLibraryTables(ctx) {
|
|
3137
|
+
const hasBuiltinFootprints = ctx.includeBuiltins && ctx.builtinKicadFootprints.length > 0;
|
|
3138
|
+
const hasBuiltinSymbols = ctx.includeBuiltins && ctx.builtinKicadSymbols.length > 0;
|
|
3139
|
+
ctx.kicadProjectFsMap["fp-lib-table"] = generateFpLibTable({
|
|
3140
|
+
kicadLibraryName: ctx.kicadLibraryName,
|
|
3141
|
+
includeBuiltin: hasBuiltinFootprints
|
|
3142
|
+
});
|
|
3143
|
+
ctx.kicadProjectFsMap["sym-lib-table"] = generateSymLibTable({
|
|
3144
|
+
kicadLibraryName: ctx.kicadLibraryName,
|
|
3145
|
+
includeBuiltin: hasBuiltinSymbols
|
|
3146
|
+
});
|
|
3147
|
+
}
|
|
3148
|
+
|
|
3149
|
+
// lib/kicad-library/KicadLibraryConverter.ts
|
|
3150
|
+
var KicadLibraryConverter = class {
|
|
3151
|
+
options;
|
|
3152
|
+
output = null;
|
|
3153
|
+
ctx;
|
|
3154
|
+
constructor(options) {
|
|
3155
|
+
this.options = options;
|
|
3156
|
+
this.ctx = createKicadLibraryConverterContext({
|
|
3157
|
+
kicadLibraryName: options.kicadLibraryName ?? "tscircuit_library",
|
|
3158
|
+
includeBuiltins: options.includeBuiltins ?? true
|
|
3159
|
+
});
|
|
3160
|
+
}
|
|
3161
|
+
async run() {
|
|
3162
|
+
this.ctx.builtTscircuitComponents = await this.buildTscircuitComponents();
|
|
3163
|
+
this.ctx.extractedKicadComponents = this.extractKicadComponents();
|
|
3164
|
+
classifyKicadFootprints(this.ctx);
|
|
3165
|
+
classifyKicadSymbols(this.ctx);
|
|
3166
|
+
buildKicadLibraryFiles(this.ctx);
|
|
3167
|
+
this.output = {
|
|
3168
|
+
kicadProjectFsMap: this.ctx.kicadProjectFsMap,
|
|
3169
|
+
model3dSourcePaths: this.ctx.model3dSourcePaths
|
|
3170
|
+
};
|
|
3171
|
+
}
|
|
3172
|
+
/**
|
|
3173
|
+
* Builds tscircuit components to circuit-json.
|
|
3174
|
+
*/
|
|
3175
|
+
async buildTscircuitComponents() {
|
|
3176
|
+
const builtTscircuitComponents = [];
|
|
3177
|
+
const { entrypoint } = this.options;
|
|
3178
|
+
const exports = await this.options.getExportsFromTsxFile(entrypoint);
|
|
3179
|
+
const componentExports = exports.filter((name) => /^[A-Z]/.test(name));
|
|
3180
|
+
for (const exportName of componentExports) {
|
|
3181
|
+
let componentPath = entrypoint;
|
|
3182
|
+
if (this.options.resolveExportPath) {
|
|
3183
|
+
const resolved = await this.options.resolveExportPath(
|
|
3184
|
+
entrypoint,
|
|
3185
|
+
exportName
|
|
3186
|
+
);
|
|
3187
|
+
if (resolved) componentPath = resolved;
|
|
3188
|
+
}
|
|
3189
|
+
const circuitJson = await this.options.buildFileToCircuitJson(
|
|
3190
|
+
componentPath,
|
|
3191
|
+
exportName
|
|
3192
|
+
);
|
|
3193
|
+
if (circuitJson && (!Array.isArray(circuitJson) || circuitJson.length > 0)) {
|
|
3194
|
+
builtTscircuitComponents.push({
|
|
3195
|
+
tscircuitComponentName: exportName,
|
|
3196
|
+
circuitJson
|
|
3197
|
+
});
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
return builtTscircuitComponents;
|
|
3201
|
+
}
|
|
3202
|
+
/**
|
|
3203
|
+
* Extracts KiCad footprints and symbols from built tscircuit components.
|
|
3204
|
+
*/
|
|
3205
|
+
extractKicadComponents() {
|
|
3206
|
+
const extractedKicadComponents = [];
|
|
3207
|
+
for (const builtTscircuitComponent of this.ctx.builtTscircuitComponents) {
|
|
3208
|
+
const { tscircuitComponentName, circuitJson } = builtTscircuitComponent;
|
|
3209
|
+
const libConverter = new CircuitJsonToKicadLibraryConverter(circuitJson, {
|
|
3210
|
+
libraryName: this.ctx.kicadLibraryName,
|
|
3211
|
+
footprintLibraryName: this.ctx.kicadLibraryName
|
|
3212
|
+
});
|
|
3213
|
+
libConverter.runUntilFinished();
|
|
3214
|
+
const libOutput = libConverter.getOutput();
|
|
3215
|
+
for (const path of libOutput.model3dSourcePaths) {
|
|
3216
|
+
if (!this.ctx.model3dSourcePaths.includes(path)) {
|
|
3217
|
+
this.ctx.model3dSourcePaths.push(path);
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
extractedKicadComponents.push({
|
|
3221
|
+
tscircuitComponentName,
|
|
3222
|
+
kicadFootprints: libOutput.footprints,
|
|
3223
|
+
kicadSymbols: libOutput.symbols,
|
|
3224
|
+
model3dSourcePaths: libOutput.model3dSourcePaths
|
|
3225
|
+
});
|
|
3226
|
+
}
|
|
3227
|
+
return extractedKicadComponents;
|
|
3228
|
+
}
|
|
3229
|
+
getOutput() {
|
|
3230
|
+
if (!this.output) {
|
|
3231
|
+
throw new Error(
|
|
3232
|
+
"Converter has not been run yet. Call run() before getOutput()."
|
|
3233
|
+
);
|
|
3234
|
+
}
|
|
3235
|
+
return this.output;
|
|
3236
|
+
}
|
|
3237
|
+
};
|
|
3238
|
+
function createKicadLibraryConverterContext(params) {
|
|
3239
|
+
return {
|
|
3240
|
+
kicadLibraryName: params.kicadLibraryName,
|
|
3241
|
+
includeBuiltins: params.includeBuiltins,
|
|
3242
|
+
builtTscircuitComponents: [],
|
|
3243
|
+
extractedKicadComponents: [],
|
|
3244
|
+
userKicadFootprints: [],
|
|
3245
|
+
builtinKicadFootprints: [],
|
|
3246
|
+
userKicadSymbols: [],
|
|
3247
|
+
builtinKicadSymbols: [],
|
|
3248
|
+
model3dSourcePaths: [],
|
|
3249
|
+
kicadProjectFsMap: {}
|
|
3250
|
+
};
|
|
3251
|
+
}
|
|
2808
3252
|
export {
|
|
2809
3253
|
CircuitJsonToKicadLibraryConverter,
|
|
2810
3254
|
CircuitJsonToKicadPcbConverter,
|
|
2811
3255
|
CircuitJsonToKicadProConverter,
|
|
2812
|
-
CircuitJsonToKicadSchConverter
|
|
3256
|
+
CircuitJsonToKicadSchConverter,
|
|
3257
|
+
KicadLibraryConverter
|
|
2813
3258
|
};
|