@tscircuit/cli 0.0.206 → 0.0.208

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.
@@ -25,25 +25,25 @@ jobs:
25
25
  with:
26
26
  token: ${{ steps.check_fork.outputs.is_fork == 'true' && secrets.GITHUB_TOKEN || secrets.TSCIRCUIT_BOT_GITHUB_TOKEN }}
27
27
 
28
- - name: Setup Node.js
29
- uses: actions/setup-node@v3
28
+ - name: Setup bun
29
+ uses: oven-sh/setup-bun@v1
30
30
  with:
31
- node-version: '20'
31
+ bun-version: latest
32
32
 
33
33
  - name: Get @biomejs/biome version
34
34
  id: get-biome-version
35
35
  run: echo "BIOME_VERSION=$(node -p "require('./package.json').devDependencies['@biomejs/biome']")" >> $GITHUB_OUTPUT
36
36
 
37
37
  - name: Install @biomejs/biome
38
- run: npm install @biomejs/biome@${{ steps.get-biome-version.outputs.BIOME_VERSION }}
38
+ run: bun install
39
39
 
40
40
  - name: Run Formatter and autofix
41
41
  if: steps.check_fork.outputs.is_fork == 'false'
42
- run: npx @biomejs/biome format . --write
42
+ run: bunx biome format . --write
43
43
 
44
44
  - name: Format Check (cannot autofix against forks)
45
45
  if: steps.check_fork.outputs.is_fork == 'true'
46
- run: npx @biomejs/biome format .
46
+ run: bunx biome format .
47
47
 
48
48
  - name: Restore lock files
49
49
  if: steps.check_fork.outputs.is_fork == 'false'
@@ -29,4 +29,4 @@ jobs:
29
29
  run: bun run build
30
30
 
31
31
  - name: Run tests
32
- run: bun test
32
+ run: bun test --timeout 15000
@@ -5,6 +5,7 @@ export const export_parameters = z.object({
5
5
  should_export_pnp_csv: z.boolean().default(false),
6
6
  should_export_bom_csv: z.boolean().default(false),
7
7
  should_export_soup_json: z.boolean().default(false),
8
+ should_export_kicad_pcb: z.boolean().default(false),
8
9
  gerbers_zip_file_name: z
9
10
  .string()
10
11
  .nullable()
@@ -13,6 +14,11 @@ export const export_parameters = z.object({
13
14
  pnp_csv_file_name: z.string().nullable().optional().default("pnp.csv"),
14
15
  bom_csv_file_name: z.string().nullable().optional().default("bom.csv"),
15
16
  soup_json_file_name: z.string().nullable().optional().default("soup.json"),
17
+ kicad_pcb_file_name: z
18
+ .string()
19
+ .nullable()
20
+ .optional()
21
+ .default("output.kicad_pcb"),
16
22
  })
17
23
 
18
24
  export type ExportParametersInput = z.input<typeof export_parameters>
package/bun.lockb CHANGED
Binary file
@@ -1,4 +1,4 @@
1
- import type { AnySoupElement } from "@tscircuit/builder"
1
+ import type { AnySoupElement } from "@tscircuit/soup"
2
2
  import { su } from "@tscircuit/soup-util"
3
3
 
4
4
  export const deriveSelectorFromPcbComponentId = ({
@@ -8,7 +8,7 @@ export const deriveSelectorFromPcbComponentId = ({
8
8
  soup: AnySoupElement[]
9
9
  pcb_component_id: string
10
10
  }) => {
11
- const source_component = su(soup).source_component.getUsing({
11
+ const source_component = su(soup as any).source_component.getUsing({
12
12
  pcb_component_id,
13
13
  })
14
14
  if (!source_component) {
@@ -6,6 +6,7 @@ import { exportPnpCsvToBuffer } from "cli/lib/export-fns/export-pnp-csv"
6
6
  import { exportBomCsvToBuffer } from "cli/lib/export-fns/export-bom-csv"
7
7
  import { soupify } from "cli/lib/soupify"
8
8
  import { ExportRequest } from "api/db/schema"
9
+ import { exportKicadPcbToBuffer } from "cli/lib/export-fns/export-kicad-pcb"
9
10
 
10
11
  export const uploadBufferToExportFile = async ({
11
12
  dev_server_axios,
@@ -139,5 +140,23 @@ export const fulfillExportRequests = async (
139
140
  export_request_id: export_request.export_request_id,
140
141
  })
141
142
  }
143
+
144
+ if (export_request.export_parameters.should_export_kicad_pcb) {
145
+ console.log(kleur.gray(`\n exporting KiCad PCB...`))
146
+ const kicadPcbBuffer = await exportKicadPcbToBuffer(
147
+ {
148
+ example_file_path: export_request.example_file_path!,
149
+ export_name: export_request.export_name!,
150
+ },
151
+ ctx,
152
+ )
153
+
154
+ await uploadBufferToExportFile({
155
+ dev_server_axios,
156
+ file_buffer: kicadPcbBuffer,
157
+ file_name: export_request.export_parameters.kicad_pcb_file_name!,
158
+ export_request_id: export_request.export_request_id,
159
+ })
160
+ }
142
161
  }
143
162
  }
@@ -0,0 +1,36 @@
1
+ import { AppContext } from "../util/app-context"
2
+ import { z } from "zod"
3
+ import { soupify } from "cli/lib/soupify"
4
+ import {
5
+ convertCircuitJsonToKiCadPcb,
6
+ convertKiCadPcbToSExprString,
7
+ } from "kicad-converter"
8
+ import fs from "fs/promises"
9
+ import kleur from "kleur"
10
+
11
+ export const exportKicadPcb = async (ctx: AppContext, args: any) => {
12
+ const params = z
13
+ .object({
14
+ input: z.string(),
15
+ export: z.string().optional(),
16
+ outputfile: z.string().default("output.kicad_pcb"),
17
+ })
18
+ .parse(args)
19
+
20
+ console.log(kleur.gray("[soupifying]..."))
21
+ const soup = await soupify(
22
+ {
23
+ filePath: params.input,
24
+ exportName: params.export,
25
+ },
26
+ ctx,
27
+ )
28
+
29
+ console.log(kleur.gray("[converting to KiCad PCB]..."))
30
+ const kicadPcb = convertCircuitJsonToKiCadPcb(soup)
31
+
32
+ console.log(kleur.gray(`[writing to ${params.outputfile}]...`))
33
+ await fs.writeFile(params.outputfile, convertKiCadPcbToSExprString(kicadPcb))
34
+
35
+ console.log(kleur.green(`KiCad PCB file exported to ${params.outputfile}`))
36
+ }
@@ -38,6 +38,7 @@ export { configClear } from "./config-clear"
38
38
  export { openCmd as open } from "./open"
39
39
  export { versionCmd as version } from "./version"
40
40
  export { exportGerbersCmd as exportGerbers } from "./export-gerbers"
41
+ export { exportKicadPcb } from "./export-kicad-pcb"
41
42
  export { devServerFulfillExportRequests } from "./dev-server-fulfill-export-requests"
42
43
  export { lintCmd as lint } from "./lint"
43
44
  export { renderCmd as render } from "./render"
@@ -0,0 +1,32 @@
1
+ import { AppContext } from "../util/app-context"
2
+ import { soupify } from "cli/lib/soupify"
3
+ import {
4
+ convertCircuitJsonToKiCadPcb,
5
+ convertKiCadPcbToSExprString,
6
+ } from "kicad-converter"
7
+ import kleur from "kleur"
8
+
9
+ export const exportKicadPcbToBuffer = async (
10
+ params: {
11
+ example_file_path: string
12
+ export_name?: string
13
+ },
14
+ ctx: AppContext,
15
+ ) => {
16
+ console.log(kleur.gray("[soupifying]..."))
17
+ const soup = await soupify(
18
+ {
19
+ filePath: params.example_file_path,
20
+ exportName: params.export_name,
21
+ },
22
+ ctx,
23
+ )
24
+
25
+ console.log(kleur.gray("[converting to KiCad PCB]..."))
26
+ const kicadPcb = convertCircuitJsonToKiCadPcb(soup)
27
+
28
+ console.log(kleur.gray("[converting to S-expression string]..."))
29
+ const kicadPcbString = convertKiCadPcbToSExprString(kicadPcb)
30
+
31
+ return Buffer.from(kicadPcbString, "utf-8")
32
+ }
@@ -293,6 +293,17 @@ export const getProgram = (ctx: AppContext) => {
293
293
  .option("--outputfile <outputfile>", "Output file name", "gerbers.zip")
294
294
  .action((args) => CMDFN.exportGerbers(ctx, args))
295
295
 
296
+ exportCmd
297
+ .command("kicad_pcb")
298
+ .description("Export KiCad PCB file from an example file")
299
+ .option("--input <input>", "Input example file")
300
+ .option(
301
+ "--export <export_name>",
302
+ "Name of export to soupify, if not specified, soupify the default/only export",
303
+ )
304
+ .option("--outputfile <outputfile>", "Output file name", "output.kicad_pcb")
305
+ .action((args) => CMDFN.exportKicadPcb(ctx, args))
306
+
296
307
  cmd
297
308
  .command("soupify")
298
309
  .description("Convert an example file to tscircuit soup")
@@ -1,6 +1,7 @@
1
1
  import Path from "node:path"
2
+ import fs from "fs/promises"
2
3
 
3
- export const getTmpEntrypointFilePath = (filePath: string) => {
4
+ export const getTmpEntrypointFilePath = async (filePath: string) => {
4
5
  const tmpEntrypointPath = Path.resolve(
5
6
  Path.dirname(filePath),
6
7
  Path.basename(filePath).replace(/\.[^\.]+$/, "") + ".__tmp_entrypoint.tsx",
@@ -9,5 +10,6 @@ export const getTmpEntrypointFilePath = (filePath: string) => {
9
10
  Path.dirname(filePath),
10
11
  Path.basename(filePath).replace(/\.[^\.]+$/, "") + ".__tmp_output.json",
11
12
  )
13
+
12
14
  return { tmpEntrypointPath, tmpOutputPath }
13
15
  }
@@ -10,6 +10,7 @@ import Debug from "debug"
10
10
  import { getExportNameFromFile } from "./get-export-name-from-file"
11
11
  import { getTmpEntrypointFilePath } from "./get-tmp-entrpoint-filepath"
12
12
  import { runEntrypointFile } from "./run-entrypoint-file"
13
+ import fs from "node:fs/promises"
13
14
 
14
15
  const debug = Debug("tscircuit:soupify")
15
16
 
@@ -25,7 +26,11 @@ export const soupifyWithCore = async (
25
26
  exportName ??= await getExportNameFromFile(filePath)
26
27
 
27
28
  const { tmpEntrypointPath, tmpOutputPath } =
28
- getTmpEntrypointFilePath(filePath)
29
+ await getTmpEntrypointFilePath(filePath)
30
+
31
+ // Remove existing entrypoint or tmp output files
32
+ await fs.unlink(tmpEntrypointPath).catch(() => {})
33
+ await fs.unlink(tmpOutputPath).catch(() => {})
29
34
 
30
35
  debug(`writing to ${tmpEntrypointPath}`)
31
36
  writeFileSync(
@@ -32,7 +32,7 @@ export const soupifyWithBuilder = async (
32
32
  exportName ??= await getExportNameFromFile(filePath)
33
33
 
34
34
  const { tmpEntrypointPath, tmpOutputPath } =
35
- getTmpEntrypointFilePath(filePath)
35
+ await getTmpEntrypointFilePath(filePath)
36
36
 
37
37
  debug(`writing to ${tmpEntrypointPath}`)
38
38
  writeFileSync(
@@ -0,0 +1,23 @@
1
+ import { test, expect, describe } from "bun:test"
2
+ import { $ } from "bun"
3
+ import { temporaryDirectory } from "tempy"
4
+ import { join } from "path"
5
+ import { existsSync, readFileSync } from "fs"
6
+
7
+ test("tsci export kicad_pcb --input example-project/examples/macrokeypad.tsx", async () => {
8
+ const tempDir = temporaryDirectory()
9
+ const kicadPcbPath = join(tempDir, "output.kicad_pcb")
10
+ const { stdout, stderr } =
11
+ await $`bun cli/cli.ts export kicad_pcb --input example-project/examples/macrokeypad.tsx --outputfile ${kicadPcbPath} --no-color`
12
+
13
+ expect(stderr.toString()).toBe("")
14
+ expect(stdout.toString()).toContain("output.kicad_pcb")
15
+
16
+ expect(existsSync(kicadPcbPath)).toBe(true)
17
+
18
+ const kicadPcbContent = readFileSync(kicadPcbPath, "utf-8")
19
+ expect(kicadPcbContent).toContain("(kicad_pcb")
20
+ expect(kicadPcbContent).toContain("(version")
21
+ expect(kicadPcbContent).toContain("(footprint")
22
+ expect(kicadPcbContent).toContain("(layer")
23
+ })