circuit-json-to-kicad 0.0.32 → 0.0.34

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 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
- export { CircuitJsonToKicadLibraryConverter, CircuitJsonToKicadPcbConverter, CircuitJsonToKicadProConverter, CircuitJsonToKicadSchConverter, type FootprintEntry, type KicadLibraryOutput, type SymbolEntry };
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 = symbols[symbolName];
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 symbols[symbolName] || null;
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 symbols2 } from "schematic-symbols";
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 symbols3 = [];
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
- symbols3.push(symbol);
737
+ symbols4.push(symbol);
728
738
  }
729
739
  if (kicadSch) {
730
- kicadSch.symbols = symbols3;
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 = symbols2[symbolName];
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 symbols3 = [];
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
- symbols3.push(symbol);
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 && symbols3.length > 0) {
908
+ if (kicadSch && symbols4.length > 0) {
899
909
  const existingSymbols = kicadSch.symbols || [];
900
- kicadSch.symbols = [...existingSymbols, ...symbols3];
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 symbols3 = libSymbols.symbols ?? [];
2519
- for (const symbol of symbols3) {
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 sanitized = this.sanitizeFootprint(footprint, fpLibraryName);
2598
- if (!uniqueFootprints.has(sanitized.footprintName)) {
2599
- uniqueFootprints.set(sanitized.footprintName, sanitized);
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(footprint, fpLibraryName) {
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,425 @@ 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 namedExports = exports.filter(
3180
+ (name) => name !== "default" && /^[A-Z]/.test(name)
3181
+ );
3182
+ for (const exportName of namedExports) {
3183
+ let componentPath = entrypoint;
3184
+ if (this.options.resolveExportPath) {
3185
+ const resolved = await this.options.resolveExportPath(
3186
+ entrypoint,
3187
+ exportName
3188
+ );
3189
+ if (resolved) componentPath = resolved;
3190
+ }
3191
+ const circuitJson = await this.options.buildFileToCircuitJson(
3192
+ componentPath,
3193
+ exportName
3194
+ );
3195
+ if (circuitJson && (!Array.isArray(circuitJson) || circuitJson.length > 0)) {
3196
+ builtTscircuitComponents.push({
3197
+ tscircuitComponentName: exportName,
3198
+ circuitJson
3199
+ });
3200
+ }
3201
+ }
3202
+ if (exports.includes("default")) {
3203
+ let componentPath = entrypoint;
3204
+ if (this.options.resolveExportPath) {
3205
+ const resolved = await this.options.resolveExportPath(
3206
+ entrypoint,
3207
+ "default"
3208
+ );
3209
+ if (resolved) componentPath = resolved;
3210
+ }
3211
+ const componentName = deriveComponentNameFromPath(componentPath);
3212
+ const circuitJson = await this.options.buildFileToCircuitJson(
3213
+ componentPath,
3214
+ "default"
3215
+ );
3216
+ if (circuitJson && (!Array.isArray(circuitJson) || circuitJson.length > 0)) {
3217
+ builtTscircuitComponents.push({
3218
+ tscircuitComponentName: componentName,
3219
+ circuitJson
3220
+ });
3221
+ }
3222
+ }
3223
+ return builtTscircuitComponents;
3224
+ }
3225
+ /**
3226
+ * Extracts KiCad footprints and symbols from built tscircuit components.
3227
+ */
3228
+ extractKicadComponents() {
3229
+ const extractedKicadComponents = [];
3230
+ for (const builtTscircuitComponent of this.ctx.builtTscircuitComponents) {
3231
+ const { tscircuitComponentName, circuitJson } = builtTscircuitComponent;
3232
+ const libConverter = new CircuitJsonToKicadLibraryConverter(circuitJson, {
3233
+ libraryName: this.ctx.kicadLibraryName,
3234
+ footprintLibraryName: this.ctx.kicadLibraryName
3235
+ });
3236
+ libConverter.runUntilFinished();
3237
+ const libOutput = libConverter.getOutput();
3238
+ for (const path of libOutput.model3dSourcePaths) {
3239
+ if (!this.ctx.model3dSourcePaths.includes(path)) {
3240
+ this.ctx.model3dSourcePaths.push(path);
3241
+ }
3242
+ }
3243
+ extractedKicadComponents.push({
3244
+ tscircuitComponentName,
3245
+ kicadFootprints: libOutput.footprints,
3246
+ kicadSymbols: libOutput.symbols,
3247
+ model3dSourcePaths: libOutput.model3dSourcePaths
3248
+ });
3249
+ }
3250
+ return extractedKicadComponents;
3251
+ }
3252
+ getOutput() {
3253
+ if (!this.output) {
3254
+ throw new Error(
3255
+ "Converter has not been run yet. Call run() before getOutput()."
3256
+ );
3257
+ }
3258
+ return this.output;
3259
+ }
3260
+ };
3261
+ function createKicadLibraryConverterContext(params) {
3262
+ return {
3263
+ kicadLibraryName: params.kicadLibraryName,
3264
+ includeBuiltins: params.includeBuiltins,
3265
+ builtTscircuitComponents: [],
3266
+ extractedKicadComponents: [],
3267
+ userKicadFootprints: [],
3268
+ builtinKicadFootprints: [],
3269
+ userKicadSymbols: [],
3270
+ builtinKicadSymbols: [],
3271
+ model3dSourcePaths: [],
3272
+ kicadProjectFsMap: {}
3273
+ };
3274
+ }
3275
+ function deriveComponentNameFromPath(filePath) {
3276
+ const filename = filePath.split(/[/\\]/).pop() || filePath;
3277
+ return filename.replace(/\.(tsx?|jsx?)$/, "");
3278
+ }
2808
3279
  export {
2809
3280
  CircuitJsonToKicadLibraryConverter,
2810
3281
  CircuitJsonToKicadPcbConverter,
2811
3282
  CircuitJsonToKicadProConverter,
2812
- CircuitJsonToKicadSchConverter
3283
+ CircuitJsonToKicadSchConverter,
3284
+ KicadLibraryConverter
2813
3285
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "circuit-json-to-kicad",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.32",
4
+ "version": "0.0.34",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist"