circuit-json-to-tscircuit 0.0.49 → 0.0.51

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.
@@ -170,7 +170,9 @@ var convertFabrication = (circuitJson) => {
170
170
  if (fabText.layer === "bottom") {
171
171
  attrs.push(`layer="bottom"`);
172
172
  }
173
- elementStrings.push(`<fabricationnotetext ${attrs.join(" ")} />`);
173
+ elementStrings.push(
174
+ `<fabricationnotetext ${attrs.join(" ")}${formatPcbRotationAttr(fabText.ccw_rotation)} />`
175
+ );
174
176
  }
175
177
  for (const fabRect of fabricationNoteRects) {
176
178
  const center = fabRect.center ?? { x: 0, y: 0 };
@@ -869,6 +871,83 @@ var generateSymbolTsx = (circuitJson) => {
869
871
  `.trim();
870
872
  };
871
873
 
874
+ // lib/get-pin-attributes.ts
875
+ var attributeNames = {
876
+ is_input: "isInput",
877
+ is_output: "isOutput",
878
+ is_bidirectional: "isBidirectional",
879
+ is_passive: "isPassive",
880
+ can_use_tri_state: "canUseTriState",
881
+ is_using_tri_state: "isUsingTriState",
882
+ can_use_open_collector: "canUseOpenCollector",
883
+ is_using_open_collector: "isUsingOpenCollector",
884
+ can_use_open_emitter: "canUseOpenEmitter",
885
+ is_using_open_emitter: "isUsingOpenEmitter",
886
+ is_gpio: "isGpio",
887
+ highlight_color: "highlightColor",
888
+ must_be_connected: "mustBeConnected",
889
+ provides_power: "providesPower",
890
+ requires_power: "requiresPower",
891
+ provides_ground: "providesGround",
892
+ requires_ground: "requiresGround",
893
+ provides_voltage: "providesVoltage",
894
+ requires_voltage: "requiresVoltage",
895
+ do_not_connect: "doNotConnect",
896
+ include_in_board_pinout: "includeInBoardPinout",
897
+ can_use_internal_pullup: "canUseInternalPullup",
898
+ is_using_internal_pullup: "isUsingInternalPullup",
899
+ needs_external_pullup: "needsExternalPullup",
900
+ can_use_internal_pulldown: "canUseInternalPulldown",
901
+ is_using_internal_pulldown: "isUsingInternalPulldown",
902
+ needs_external_pulldown: "needsExternalPulldown",
903
+ can_use_open_drain: "canUseOpenDrain",
904
+ is_using_open_drain: "isUsingOpenDrain",
905
+ can_use_push_pull: "canUsePushPull",
906
+ is_using_push_pull: "isUsingPushPull",
907
+ should_have_decoupling_capacitor: "shouldHaveDecouplingCapacitor",
908
+ recommended_decoupling_capacitor_capacitance: "recommendedDecouplingCapacitorCapacitance"
909
+ };
910
+ var capabilities = [
911
+ "i2c_sda",
912
+ "i2c_scl",
913
+ "spi_mosi",
914
+ "spi_miso",
915
+ "spi_sck",
916
+ "spi_cs",
917
+ "uart_tx",
918
+ "uart_rx"
919
+ ];
920
+ var getPinAttributes = (circuitJson) => {
921
+ const components = circuitJson.filter((e) => e.type === "source_component");
922
+ if (components.length > 1) return void 0;
923
+ const componentId = components[0]?.source_component_id;
924
+ const pins = /* @__PURE__ */ new Map();
925
+ for (const port of circuitJson) {
926
+ if (port.type !== "source_port") continue;
927
+ if (componentId && port.source_component_id && port.source_component_id !== componentId)
928
+ continue;
929
+ const source = { ...port };
930
+ const attributes = {};
931
+ for (const [sourceName, propName] of Object.entries(attributeNames)) {
932
+ if (source[sourceName] !== void 0) {
933
+ attributes[propName] = source[sourceName];
934
+ }
935
+ }
936
+ const supported = capabilities.filter(
937
+ (c) => source[`supports_${c}`] === true
938
+ );
939
+ const active = capabilities.filter(
940
+ (c) => source[`is_configured_for_${c}`] === true
941
+ );
942
+ if (supported.length) attributes.capabilities = supported;
943
+ if (active.length) attributes.activeCapabilities = active;
944
+ if (Object.keys(attributes).length === 0) continue;
945
+ const key = port.pin_number !== void 0 ? `pin${port.pin_number}` : port.name;
946
+ pins.set(key, { ...pins.get(key), ...attributes });
947
+ }
948
+ return pins.size ? Object.fromEntries(pins) : void 0;
949
+ };
950
+
872
951
  // lib/get-component-using-template.ts
873
952
  var getComponentUsingTemplate = ({
874
953
  pinLabels,
@@ -880,6 +959,7 @@ var getComponentUsingTemplate = ({
880
959
  }) => {
881
960
  const footprintTsx = generateFootprintTsx(circuitJson);
882
961
  const symbolTsx = generateSymbolTsx(circuitJson);
962
+ const pinAttributes = getPinAttributes(circuitJson);
883
963
  return `
884
964
  import { type ChipProps } from "tscircuit"
885
965
  ${pinLabels ? `const pinLabels = ${JSON.stringify(pinLabels, null, " ")} as const
@@ -888,6 +968,7 @@ ${pinLabels ? `const pinLabels = ${JSON.stringify(pinLabels, null, " ")} as con
888
968
  ${footprintTsx ? `footprint={${footprintTsx}}` : ""}
889
969
  ${symbolTsx ? `symbol={${symbolTsx}}` : ""}
890
970
  ${pinLabels ? "pinLabels={pinLabels}" : ""}
971
+ ${pinAttributes ? `pinAttributes={${JSON.stringify(pinAttributes, null, " ")}}` : ""}
891
972
  ${objUrl ? `cadModel={{
892
973
  objUrl: "${objUrl}",
893
974
  rotationOffset: { x: 0, y: 0, z: 0 },
package/dist/cli/main.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  convertCircuitJsonToTscircuit
4
- } from "../chunk-YHGRHULT.js";
4
+ } from "../chunk-SGFWINLD.js";
5
5
 
6
6
  // cli/main.ts
7
7
  import { Command } from "commander";
@@ -16,7 +16,7 @@ var package_default = {
16
16
  "./cli": "./dist/cli/main.js"
17
17
  },
18
18
  type: "module",
19
- version: "0.0.48",
19
+ version: "0.0.50",
20
20
  repository: {
21
21
  type: "git",
22
22
  url: "https://github.com/tscircuit/circuit-json-to-tscircuit.git"
package/dist/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  convertCircuitJsonToTscircuit
3
- } from "../chunk-YHGRHULT.js";
3
+ } from "../chunk-SGFWINLD.js";
4
4
  export {
5
5
  convertCircuitJsonToTscircuit
6
6
  };
@@ -1,4 +1,5 @@
1
1
  import { su } from "@tscircuit/soup-util"
2
+ import { formatPcbRotationAttr } from "./footprint-tsx-attribute-formatters/format-pcb-rotation-attr"
2
3
  import type { FootprintElementConverter } from "./converter-types"
3
4
  import { escapeJsxText } from "./footprint-tsx-attribute-formatters/escape-jsx-text"
4
5
 
@@ -48,7 +49,9 @@ export const convertFabrication: FootprintElementConverter = (circuitJson) => {
48
49
  attrs.push(`layer="bottom"`)
49
50
  }
50
51
 
51
- elementStrings.push(`<fabricationnotetext ${attrs.join(" ")} />`)
52
+ elementStrings.push(
53
+ `<fabricationnotetext ${attrs.join(" ")}${formatPcbRotationAttr(fabText.ccw_rotation)} />`,
54
+ )
52
55
  }
53
56
 
54
57
  for (const fabRect of fabricationNoteRects) {
@@ -1,6 +1,7 @@
1
1
  import type { AnyCircuitElement } from "circuit-json"
2
2
  import { generateFootprintTsx } from "./generate-footprint-tsx"
3
3
  import { generateSymbolTsx } from "./generate-symbol-tsx"
4
+ import { getPinAttributes } from "./get-pin-attributes"
4
5
 
5
6
  export interface ComponentTemplateParams {
6
7
  pinLabels?: Record<string, string[]> | Record<string, string> // ChipProps["pinLabels"]
@@ -21,6 +22,7 @@ export const getComponentUsingTemplate = ({
21
22
  }: ComponentTemplateParams) => {
22
23
  const footprintTsx = generateFootprintTsx(circuitJson)
23
24
  const symbolTsx = generateSymbolTsx(circuitJson)
25
+ const pinAttributes = getPinAttributes(circuitJson)
24
26
  return `
25
27
  import { type ChipProps } from "tscircuit"
26
28
  ${pinLabels ? `const pinLabels = ${JSON.stringify(pinLabels, null, " ")} as const\n` : ""}export const ${componentName} = (props: ChipProps${pinLabels ? `<typeof pinLabels>` : ""}) => (
@@ -28,6 +30,7 @@ ${pinLabels ? `const pinLabels = ${JSON.stringify(pinLabels, null, " ")} as con
28
30
  ${footprintTsx ? `footprint={${footprintTsx}}` : ""}
29
31
  ${symbolTsx ? `symbol={${symbolTsx}}` : ""}
30
32
  ${pinLabels ? "pinLabels={pinLabels}" : ""}
33
+ ${pinAttributes ? `pinAttributes={${JSON.stringify(pinAttributes, null, " ")}}` : ""}
31
34
  ${objUrl ? `cadModel={{\n objUrl: \"${objUrl}\",\n rotationOffset: { x: 0, y: 0, z: 0 },\n positionOffset: { x: 0, y: 0, z: 0 },\n }}` : ""}
32
35
  ${supplierPartNumbers ? `supplierPartNumbers={${JSON.stringify(supplierPartNumbers, null, " ")}}` : ""}
33
36
  ${manufacturerPartNumber ? `manufacturerPartNumber=\"${manufacturerPartNumber}\"` : ""}
@@ -0,0 +1,91 @@
1
+ import type { AnyCircuitElement } from "circuit-json"
2
+
3
+ // Explicitly select electrical attributes; source-port IDs and other metadata
4
+ // must not become chip props. Newer Circuit JSON fields are read without parsing
5
+ // through an older schema, which would strip them from imported JSON.
6
+ const attributeNames = {
7
+ is_input: "isInput",
8
+ is_output: "isOutput",
9
+ is_bidirectional: "isBidirectional",
10
+ is_passive: "isPassive",
11
+ can_use_tri_state: "canUseTriState",
12
+ is_using_tri_state: "isUsingTriState",
13
+ can_use_open_collector: "canUseOpenCollector",
14
+ is_using_open_collector: "isUsingOpenCollector",
15
+ can_use_open_emitter: "canUseOpenEmitter",
16
+ is_using_open_emitter: "isUsingOpenEmitter",
17
+ is_gpio: "isGpio",
18
+ highlight_color: "highlightColor",
19
+ must_be_connected: "mustBeConnected",
20
+ provides_power: "providesPower",
21
+ requires_power: "requiresPower",
22
+ provides_ground: "providesGround",
23
+ requires_ground: "requiresGround",
24
+ provides_voltage: "providesVoltage",
25
+ requires_voltage: "requiresVoltage",
26
+ do_not_connect: "doNotConnect",
27
+ include_in_board_pinout: "includeInBoardPinout",
28
+ can_use_internal_pullup: "canUseInternalPullup",
29
+ is_using_internal_pullup: "isUsingInternalPullup",
30
+ needs_external_pullup: "needsExternalPullup",
31
+ can_use_internal_pulldown: "canUseInternalPulldown",
32
+ is_using_internal_pulldown: "isUsingInternalPulldown",
33
+ needs_external_pulldown: "needsExternalPulldown",
34
+ can_use_open_drain: "canUseOpenDrain",
35
+ is_using_open_drain: "isUsingOpenDrain",
36
+ can_use_push_pull: "canUsePushPull",
37
+ is_using_push_pull: "isUsingPushPull",
38
+ should_have_decoupling_capacitor: "shouldHaveDecouplingCapacitor",
39
+ recommended_decoupling_capacitor_capacitance:
40
+ "recommendedDecouplingCapacitorCapacitance",
41
+ } as const
42
+
43
+ const capabilities = [
44
+ "i2c_sda",
45
+ "i2c_scl",
46
+ "spi_mosi",
47
+ "spi_miso",
48
+ "spi_sck",
49
+ "spi_cs",
50
+ "uart_tx",
51
+ "uart_rx",
52
+ ] as const
53
+
54
+ /** Electrical props for the single component represented by a chip template. */
55
+ export const getPinAttributes = (circuitJson: AnyCircuitElement[]) => {
56
+ const components = circuitJson.filter((e) => e.type === "source_component")
57
+ // Pin numbers are local to a component. Never combine multiple components'
58
+ // attributes into a single chip.
59
+ if (components.length > 1) return undefined
60
+ const componentId = components[0]?.source_component_id
61
+ const pins = new Map<string, Record<string, unknown>>()
62
+ for (const port of circuitJson) {
63
+ if (port.type !== "source_port") continue
64
+ if (
65
+ componentId &&
66
+ port.source_component_id &&
67
+ port.source_component_id !== componentId
68
+ )
69
+ continue
70
+ const source: Record<string, unknown> = { ...port }
71
+ const attributes: Record<string, unknown> = {}
72
+ for (const [sourceName, propName] of Object.entries(attributeNames)) {
73
+ if (source[sourceName] !== undefined) {
74
+ attributes[propName] = source[sourceName]
75
+ }
76
+ }
77
+ const supported = capabilities.filter(
78
+ (c) => source[`supports_${c}`] === true,
79
+ )
80
+ const active = capabilities.filter(
81
+ (c) => source[`is_configured_for_${c}`] === true,
82
+ )
83
+ if (supported.length) attributes.capabilities = supported
84
+ if (active.length) attributes.activeCapabilities = active
85
+ if (Object.keys(attributes).length === 0) continue
86
+ const key =
87
+ port.pin_number !== undefined ? `pin${port.pin_number}` : port.name
88
+ pins.set(key, { ...pins.get(key), ...attributes })
89
+ }
90
+ return pins.size ? Object.fromEntries(pins) : undefined
91
+ }
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "./cli": "./dist/cli/main.js"
7
7
  },
8
8
  "type": "module",
9
- "version": "0.0.49",
9
+ "version": "0.0.51",
10
10
  "repository": {
11
11
  "type": "git",
12
12
  "url": "https://github.com/tscircuit/circuit-json-to-tscircuit.git"
@@ -0,0 +1,26 @@
1
+ import { expect, test } from "bun:test"
2
+ import { convertFabrication } from "../lib/generate-footprint-tsx/convert-fabrication"
3
+
4
+ // Repro for rotations lost by the TI EVM Circuit JSON -> TSX conversion.
5
+ test("preserves fabricationnotetext rotations on both layers", () => {
6
+ for (const layer of ["top", "bottom"]) {
7
+ for (const rotation of [0, 30, 90, 180, 270, -45, undefined]) {
8
+ const [tsx] = convertFabrication([
9
+ {
10
+ type: "pcb_fabrication_note_text",
11
+ layer,
12
+ ccw_rotation: rotation,
13
+ anchor_position: { x: 1, y: 2 },
14
+ text: "Assembly",
15
+ },
16
+ ] as any)
17
+ if (rotation === undefined) expect(tsx).not.toContain("pcbRotation")
18
+ else expect(tsx).toContain(`pcbRotation="${rotation}deg"`)
19
+ expect(
20
+ new Bun.Transpiler({ loader: "tsx" }).transformSync(
21
+ `export default () => (${tsx})`,
22
+ ),
23
+ ).toBeTruthy()
24
+ }
25
+ }
26
+ })
@@ -0,0 +1,91 @@
1
+ {
2
+ "circuitJson": [
3
+ {
4
+ "type": "source_component",
5
+ "ftype": "simple_chip",
6
+ "source_component_id": "source_component_1",
7
+ "name": "U1",
8
+ "manufacturer_part_number": "AP2127K-28TRG1"
9
+ },
10
+ {
11
+ "type": "source_port",
12
+ "source_port_id": "source_port_1",
13
+ "source_component_id": "source_component_1",
14
+ "name": "pin1",
15
+ "pin_number": 1,
16
+ "port_hints": ["VIN"],
17
+ "must_be_connected": true,
18
+ "requires_power": true,
19
+ "should_have_decoupling_capacitor": true,
20
+ "recommended_decoupling_capacitor_capacitance": "1uF"
21
+ },
22
+ {
23
+ "type": "source_port",
24
+ "source_port_id": "source_port_2",
25
+ "source_component_id": "source_component_1",
26
+ "name": "pin2",
27
+ "pin_number": 2,
28
+ "port_hints": ["GND"],
29
+ "must_be_connected": true,
30
+ "requires_ground": true
31
+ },
32
+ {
33
+ "type": "source_port",
34
+ "source_port_id": "source_port_3",
35
+ "source_component_id": "source_component_1",
36
+ "name": "pin3",
37
+ "pin_number": 3,
38
+ "port_hints": ["N_SHUTDOWN"],
39
+ "is_input": true,
40
+ "must_be_connected": true
41
+ },
42
+ {
43
+ "type": "source_port",
44
+ "source_port_id": "source_port_4",
45
+ "source_component_id": "source_component_1",
46
+ "name": "pin4",
47
+ "pin_number": 4,
48
+ "port_hints": ["NC"],
49
+ "do_not_connect": true
50
+ },
51
+ {
52
+ "type": "source_port",
53
+ "source_port_id": "source_port_5",
54
+ "source_component_id": "source_component_1",
55
+ "name": "pin5",
56
+ "pin_number": 5,
57
+ "port_hints": ["VOUT"],
58
+ "is_output": true,
59
+ "provides_power": true,
60
+ "provides_voltage": 2.8,
61
+ "should_have_decoupling_capacitor": true,
62
+ "recommended_decoupling_capacitor_capacitance": "1uF"
63
+ }
64
+ ],
65
+ "pinAttributes": {
66
+ "pin1": {
67
+ "requiresPower": true,
68
+ "mustBeConnected": true,
69
+ "shouldHaveDecouplingCapacitor": true,
70
+ "recommendedDecouplingCapacitorCapacitance": "1uF"
71
+ },
72
+ "pin2": {
73
+ "requiresGround": true,
74
+ "mustBeConnected": true
75
+ },
76
+ "pin3": {
77
+ "isInput": true,
78
+ "mustBeConnected": true
79
+ },
80
+ "pin4": {
81
+ "doNotConnect": true
82
+ },
83
+ "pin5": {
84
+ "isOutput": true,
85
+ "providesPower": true,
86
+ "providesVoltage": 2.8,
87
+ "shouldHaveDecouplingCapacitor": true,
88
+ "recommendedDecouplingCapacitorCapacitance": "1uF"
89
+ }
90
+ }
91
+ }