@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.
- package/.github/workflows/formatbot.yml +6 -6
- package/.github/workflows/test.yml +1 -1
- package/api/lib/zod/export_parameters.ts +6 -0
- package/bun.lockb +0 -0
- package/cli/lib/cmd-fns/dev/derive-selector-from-pcb-component-id.ts +2 -2
- package/cli/lib/cmd-fns/dev/fulfill-export-requests.ts +19 -0
- package/cli/lib/cmd-fns/export-kicad-pcb.ts +36 -0
- package/cli/lib/cmd-fns/index.ts +1 -0
- package/cli/lib/export-fns/export-kicad-pcb.ts +32 -0
- package/cli/lib/get-program.ts +11 -0
- package/cli/lib/soupify/get-tmp-entrpoint-filepath.ts +3 -1
- package/cli/lib/soupify/soupify-with-core.ts +6 -1
- package/cli/lib/soupify/soupify.ts +1 -1
- package/cli/tests/export-kicad-pcb.test.ts +23 -0
- package/dist/cli.js +280 -213
- package/example-project/src/MyCircuit.tsx +1 -7
- package/example-project/src/manual-edits.ts +1 -72
- package/frontend/components/dialogs/generic-export-dialog.tsx +3 -1
- package/frontend/views/HeaderMenu.tsx +17 -2
- package/package.json +7 -5
|
@@ -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
|
|
29
|
-
uses:
|
|
28
|
+
- name: Setup bun
|
|
29
|
+
uses: oven-sh/setup-bun@v1
|
|
30
30
|
with:
|
|
31
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
46
|
+
run: bunx biome format .
|
|
47
47
|
|
|
48
48
|
- name: Restore lock files
|
|
49
49
|
if: steps.check_fork.outputs.is_fork == 'false'
|
|
@@ -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/
|
|
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
|
+
}
|
package/cli/lib/cmd-fns/index.ts
CHANGED
|
@@ -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
|
+
}
|
package/cli/lib/get-program.ts
CHANGED
|
@@ -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
|
+
})
|