circuit-json-to-kicad 0.0.35 → 0.0.37

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
@@ -212,6 +212,38 @@ declare class CircuitJsonToKicadLibraryConverter {
212
212
  getModel3dSourcePaths(): string[];
213
213
  }
214
214
 
215
+ /**
216
+ * KiCad footprint metadata that can be extracted from component props.
217
+ * This mirrors the KicadFootprintMetadata type from @tscircuit/props.
218
+ */
219
+ interface KicadFootprintMetadata {
220
+ footprintName?: string;
221
+ layer?: string;
222
+ properties?: {
223
+ Reference?: KicadPropertyMetadata;
224
+ Value?: KicadPropertyMetadata;
225
+ Datasheet?: KicadPropertyMetadata;
226
+ Description?: KicadPropertyMetadata;
227
+ };
228
+ attributes?: {
229
+ through_hole?: boolean;
230
+ smd?: boolean;
231
+ exclude_from_pos_files?: boolean;
232
+ exclude_from_bom?: boolean;
233
+ };
234
+ embeddedFonts?: boolean;
235
+ }
236
+ interface KicadPropertyMetadata {
237
+ value: string;
238
+ at?: {
239
+ x: number | string;
240
+ y: number | string;
241
+ rotation?: number | string;
242
+ };
243
+ layer?: string;
244
+ uuid?: string;
245
+ hide?: boolean;
246
+ }
215
247
  interface KicadLibraryConverterOptions {
216
248
  /**
217
249
  * Name for the generated KiCad library (e.g., "my-library").
@@ -248,6 +280,12 @@ interface KicadLibraryConverterOptions {
248
280
  * Default: true
249
281
  */
250
282
  includeBuiltins?: boolean;
283
+ /**
284
+ * Callback to get KiCad footprint metadata from a component via prop introspection.
285
+ * This allows extracting kicadFootprintMetadata props without rendering the component.
286
+ * Return null if no metadata is available.
287
+ */
288
+ getComponentKicadMetadata?: (filePath: string, componentName: string) => Promise<KicadFootprintMetadata | null>;
251
289
  }
252
290
  interface KicadLibraryConverterOutput {
253
291
  /**
@@ -280,4 +318,4 @@ declare class KicadLibraryConverter {
280
318
  getOutput(): KicadLibraryConverterOutput;
281
319
  }
282
320
 
283
- export { CircuitJsonToKicadLibraryConverter, CircuitJsonToKicadPcbConverter, CircuitJsonToKicadProConverter, CircuitJsonToKicadSchConverter, type FootprintEntry, KicadLibraryConverter, type KicadLibraryConverterOptions, type KicadLibraryConverterOutput, type KicadLibraryOutput, type SymbolEntry };
321
+ export { CircuitJsonToKicadLibraryConverter, CircuitJsonToKicadPcbConverter, CircuitJsonToKicadProConverter, CircuitJsonToKicadSchConverter, type FootprintEntry, type KicadFootprintMetadata, KicadLibraryConverter, type KicadLibraryConverterOptions, type KicadLibraryConverterOutput, type KicadLibraryOutput, type KicadPropertyMetadata, type SymbolEntry };
package/dist/index.js CHANGED
@@ -1911,6 +1911,9 @@ var AddFootprintsStage = class extends ConverterStage {
1911
1911
  componentRotation: component.rotation || 0
1912
1912
  });
1913
1913
  if (fpText) {
1914
+ if (sourceComponent?.name && textElement.text === sourceComponent.name) {
1915
+ fpText.type = "reference";
1916
+ }
1914
1917
  fpTexts.push(fpText);
1915
1918
  }
1916
1919
  }
@@ -2585,7 +2588,12 @@ import {
2585
2588
  parseKicadSexpr as parseKicadSexpr2,
2586
2589
  KicadPcb as KicadPcb2,
2587
2590
  FootprintModel as FootprintModel2,
2588
- At as At2
2591
+ At as At2,
2592
+ EmbeddedFonts as EmbeddedFonts4,
2593
+ FootprintAttr,
2594
+ Property,
2595
+ TextEffects as TextEffects6,
2596
+ TextEffectsFont as TextEffectsFont6
2589
2597
  } from "kicadts";
2590
2598
  function getBasename(filePath) {
2591
2599
  const parts = filePath.split(/[/\\]/);
@@ -2664,11 +2672,71 @@ var ExtractFootprintsStage = class extends ConverterStage {
2664
2672
  footprint.position = At2.from([0, 0, 0]);
2665
2673
  footprint.locked = false;
2666
2674
  footprint.placed = false;
2675
+ if (!footprint.descr) {
2676
+ footprint.descr = "";
2677
+ }
2678
+ if (!footprint.tags) {
2679
+ footprint.tags = "";
2680
+ }
2681
+ if (!footprint.embeddedFonts) {
2682
+ footprint.embeddedFonts = new EmbeddedFonts4(false);
2683
+ }
2684
+ if (!footprint.attr) {
2685
+ const attr = new FootprintAttr();
2686
+ const padTypes = (footprint.fpPads ?? []).map((pad) => pad.padType);
2687
+ if (padTypes.some((padType) => padType.includes("thru_hole"))) {
2688
+ attr.type = "through_hole";
2689
+ } else if (padTypes.some((padType) => padType.includes("smd"))) {
2690
+ attr.type = "smd";
2691
+ }
2692
+ footprint.attr = attr;
2693
+ }
2667
2694
  footprint.uuid = void 0;
2668
2695
  footprint.path = void 0;
2669
2696
  footprint.sheetfile = void 0;
2670
2697
  footprint.sheetname = void 0;
2671
- footprint.properties = [];
2698
+ const defaultFont = new TextEffectsFont6();
2699
+ defaultFont.size = { width: 1.27, height: 1.27 };
2700
+ defaultFont.thickness = 0.15;
2701
+ const defaultEffects = new TextEffects6({ font: defaultFont });
2702
+ footprint.properties = [
2703
+ new Property({
2704
+ key: "Reference",
2705
+ value: "Ref**",
2706
+ position: [0, 0, 0],
2707
+ layer: "F.SilkS",
2708
+ uuid: generateDeterministicUuid(`${footprintName}-property-Reference`),
2709
+ effects: defaultEffects
2710
+ }),
2711
+ new Property({
2712
+ key: "Value",
2713
+ value: "Val**",
2714
+ position: [0, 0, 0],
2715
+ layer: "F.Fab",
2716
+ uuid: generateDeterministicUuid(`${footprintName}-property-Value`),
2717
+ effects: defaultEffects
2718
+ }),
2719
+ new Property({
2720
+ key: "Datasheet",
2721
+ value: "",
2722
+ position: [0, 0, 0],
2723
+ layer: "F.Fab",
2724
+ hidden: true,
2725
+ uuid: generateDeterministicUuid(`${footprintName}-property-Datasheet`),
2726
+ effects: defaultEffects
2727
+ }),
2728
+ new Property({
2729
+ key: "Description",
2730
+ value: "",
2731
+ position: [0, 0, 0],
2732
+ layer: "F.Fab",
2733
+ hidden: true,
2734
+ uuid: generateDeterministicUuid(
2735
+ `${footprintName}-property-Description`
2736
+ ),
2737
+ effects: defaultEffects
2738
+ })
2739
+ ];
2672
2740
  const texts = footprint.fpTexts ?? [];
2673
2741
  for (const text of texts) {
2674
2742
  text.uuid = void 0;
@@ -2680,9 +2748,14 @@ var ExtractFootprintsStage = class extends ConverterStage {
2680
2748
  }
2681
2749
  footprint.fpTexts = texts;
2682
2750
  const pads = footprint.fpPads ?? [];
2683
- for (const pad of pads) {
2684
- pad.uuid = void 0;
2685
- pad.net = void 0;
2751
+ for (let i = 0; i < pads.length; i++) {
2752
+ const pad = pads[i];
2753
+ if (pad) {
2754
+ pad.uuid = generateDeterministicUuid(
2755
+ `${footprintName}-pad-${pad.number ?? i}`
2756
+ );
2757
+ pad.net = void 0;
2758
+ }
2686
2759
  }
2687
2760
  footprint.fpPads = pads;
2688
2761
  const models = footprint.models ?? [];
@@ -2883,6 +2956,117 @@ function renameKicadFootprint(params) {
2883
2956
  };
2884
2957
  }
2885
2958
 
2959
+ // lib/kicad-library/kicad-library-converter-utils/applyKicadFootprintMetadata.ts
2960
+ import {
2961
+ parseKicadSexpr as parseKicadSexpr3,
2962
+ Footprint as Footprint3,
2963
+ Property as Property2,
2964
+ TextEffects as TextEffects7,
2965
+ TextEffectsFont as TextEffectsFont7
2966
+ } from "kicadts";
2967
+ function applyKicadFootprintMetadata(kicadModString, metadata, footprintName) {
2968
+ try {
2969
+ const parsed = parseKicadSexpr3(kicadModString);
2970
+ const footprint = parsed.find(
2971
+ (node) => node instanceof Footprint3
2972
+ );
2973
+ if (!footprint) {
2974
+ return kicadModString;
2975
+ }
2976
+ if (metadata.embeddedFonts !== void 0) {
2977
+ }
2978
+ if (metadata.properties) {
2979
+ const defaultFont = new TextEffectsFont7();
2980
+ defaultFont.size = { width: 1.27, height: 1.27 };
2981
+ defaultFont.thickness = 0.15;
2982
+ const defaultEffects = new TextEffects7({ font: defaultFont });
2983
+ const newProperties = [];
2984
+ const refMeta = metadata.properties.Reference;
2985
+ newProperties.push(
2986
+ new Property2({
2987
+ key: "Reference",
2988
+ value: refMeta?.value ?? "REF**",
2989
+ position: refMeta?.at ? [
2990
+ Number(refMeta.at.x),
2991
+ Number(refMeta.at.y),
2992
+ Number(refMeta.at.rotation ?? 0)
2993
+ ] : [0, 0, 0],
2994
+ layer: refMeta?.layer ?? "F.SilkS",
2995
+ uuid: refMeta?.uuid ?? generateDeterministicUuid(`${footprintName}-property-Reference`),
2996
+ effects: defaultEffects,
2997
+ hidden: refMeta?.hide
2998
+ })
2999
+ );
3000
+ const valMeta = metadata.properties.Value;
3001
+ newProperties.push(
3002
+ new Property2({
3003
+ key: "Value",
3004
+ value: valMeta?.value ?? footprintName,
3005
+ position: valMeta?.at ? [
3006
+ Number(valMeta.at.x),
3007
+ Number(valMeta.at.y),
3008
+ Number(valMeta.at.rotation ?? 0)
3009
+ ] : [0, 0, 0],
3010
+ layer: valMeta?.layer ?? "F.Fab",
3011
+ uuid: valMeta?.uuid ?? generateDeterministicUuid(`${footprintName}-property-Value`),
3012
+ effects: defaultEffects,
3013
+ hidden: valMeta?.hide
3014
+ })
3015
+ );
3016
+ const dsMeta = metadata.properties.Datasheet;
3017
+ newProperties.push(
3018
+ new Property2({
3019
+ key: "Datasheet",
3020
+ value: dsMeta?.value ?? "",
3021
+ position: dsMeta?.at ? [
3022
+ Number(dsMeta.at.x),
3023
+ Number(dsMeta.at.y),
3024
+ Number(dsMeta.at.rotation ?? 0)
3025
+ ] : [0, 0, 0],
3026
+ layer: dsMeta?.layer ?? "F.Fab",
3027
+ uuid: dsMeta?.uuid ?? generateDeterministicUuid(`${footprintName}-property-Datasheet`),
3028
+ effects: defaultEffects,
3029
+ hidden: dsMeta?.hide ?? true
3030
+ })
3031
+ );
3032
+ const descMeta = metadata.properties.Description;
3033
+ newProperties.push(
3034
+ new Property2({
3035
+ key: "Description",
3036
+ value: descMeta?.value ?? "",
3037
+ position: descMeta?.at ? [
3038
+ Number(descMeta.at.x),
3039
+ Number(descMeta.at.y),
3040
+ Number(descMeta.at.rotation ?? 0)
3041
+ ] : [0, 0, 0],
3042
+ layer: descMeta?.layer ?? "F.Fab",
3043
+ uuid: descMeta?.uuid ?? generateDeterministicUuid(`${footprintName}-property-Description`),
3044
+ effects: defaultEffects,
3045
+ hidden: descMeta?.hide ?? true
3046
+ })
3047
+ );
3048
+ footprint.properties = newProperties;
3049
+ }
3050
+ if (metadata.attributes && footprint.attr) {
3051
+ if (metadata.attributes.through_hole) {
3052
+ footprint.attr.type = "through_hole";
3053
+ } else if (metadata.attributes.smd) {
3054
+ footprint.attr.type = "smd";
3055
+ }
3056
+ if (metadata.attributes.exclude_from_pos_files !== void 0) {
3057
+ footprint.attr.excludeFromPosFiles = metadata.attributes.exclude_from_pos_files;
3058
+ }
3059
+ if (metadata.attributes.exclude_from_bom !== void 0) {
3060
+ footprint.attr.excludeFromBom = metadata.attributes.exclude_from_bom;
3061
+ }
3062
+ }
3063
+ return footprint.getString();
3064
+ } catch (error) {
3065
+ console.warn(`Failed to apply kicadFootprintMetadata:`, error);
3066
+ return kicadModString;
3067
+ }
3068
+ }
3069
+
2886
3070
  // lib/kicad-library/stages/ClassifyKicadFootprintsStage.ts
2887
3071
  function classifyKicadFootprints(ctx) {
2888
3072
  for (const extractedKicadComponent of ctx.extractedKicadComponents) {
@@ -2898,17 +3082,28 @@ function classifyFootprintsForComponent({
2898
3082
  }) {
2899
3083
  const { tscircuitComponentName, kicadFootprints } = extractedKicadComponent;
2900
3084
  let hasAddedUserFootprint = false;
3085
+ const metadata = ctx.footprintMetadataMap.get(tscircuitComponentName);
2901
3086
  for (const kicadFootprint of kicadFootprints) {
2902
3087
  if (kicadFootprint.isBuiltin) {
2903
3088
  addBuiltinFootprint({ ctx, kicadFootprint });
2904
3089
  } else {
2905
3090
  if (!hasAddedUserFootprint) {
2906
3091
  hasAddedUserFootprint = true;
2907
- const renamedFootprint = renameKicadFootprint({
3092
+ let renamedFootprint = renameKicadFootprint({
2908
3093
  kicadFootprint,
2909
3094
  newKicadFootprintName: tscircuitComponentName,
2910
3095
  kicadLibraryName: ctx.kicadLibraryName
2911
3096
  });
3097
+ if (metadata) {
3098
+ renamedFootprint = {
3099
+ ...renamedFootprint,
3100
+ kicadModString: applyKicadFootprintMetadata(
3101
+ renamedFootprint.kicadModString,
3102
+ metadata,
3103
+ tscircuitComponentName
3104
+ )
3105
+ };
3106
+ }
2912
3107
  addUserFootprint({ ctx, kicadFootprint: renamedFootprint });
2913
3108
  } else {
2914
3109
  addUserFootprint({ ctx, kicadFootprint });
@@ -3156,7 +3351,8 @@ var KicadLibraryConverter = class {
3156
3351
  this.options = options;
3157
3352
  this.ctx = createKicadLibraryConverterContext({
3158
3353
  kicadLibraryName: options.kicadLibraryName ?? "tscircuit_library",
3159
- includeBuiltins: options.includeBuiltins ?? true
3354
+ includeBuiltins: options.includeBuiltins ?? true,
3355
+ getComponentKicadMetadata: options.getComponentKicadMetadata
3160
3356
  });
3161
3357
  }
3162
3358
  async run() {
@@ -3189,6 +3385,15 @@ var KicadLibraryConverter = class {
3189
3385
  );
3190
3386
  if (resolved) componentPath = resolved;
3191
3387
  }
3388
+ if (this.ctx.getComponentKicadMetadata) {
3389
+ const metadata = await this.ctx.getComponentKicadMetadata(
3390
+ componentPath,
3391
+ exportName
3392
+ );
3393
+ if (metadata) {
3394
+ this.ctx.footprintMetadataMap.set(exportName, metadata);
3395
+ }
3396
+ }
3192
3397
  const circuitJson = await this.options.buildFileToCircuitJson(
3193
3398
  componentPath,
3194
3399
  exportName
@@ -3210,6 +3415,15 @@ var KicadLibraryConverter = class {
3210
3415
  if (resolved) componentPath = resolved;
3211
3416
  }
3212
3417
  const componentName = deriveComponentNameFromPath(componentPath);
3418
+ if (this.ctx.getComponentKicadMetadata) {
3419
+ const metadata = await this.ctx.getComponentKicadMetadata(
3420
+ componentPath,
3421
+ "default"
3422
+ );
3423
+ if (metadata) {
3424
+ this.ctx.footprintMetadataMap.set(componentName, metadata);
3425
+ }
3426
+ }
3213
3427
  const circuitJson = await this.options.buildFileToCircuitJson(
3214
3428
  componentPath,
3215
3429
  "default"
@@ -3263,6 +3477,8 @@ function createKicadLibraryConverterContext(params) {
3263
3477
  return {
3264
3478
  kicadLibraryName: params.kicadLibraryName,
3265
3479
  includeBuiltins: params.includeBuiltins,
3480
+ getComponentKicadMetadata: params.getComponentKicadMetadata,
3481
+ footprintMetadataMap: /* @__PURE__ */ new Map(),
3266
3482
  builtTscircuitComponents: [],
3267
3483
  extractedKicadComponents: [],
3268
3484
  userKicadFootprints: [],
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.35",
4
+ "version": "0.0.37",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist"