@tscircuit/cli 0.0.204 → 0.0.205
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/bun.lockb +0 -0
- package/bunfig.toml +2 -0
- package/cli/lib/cmd-fns/dev/fulfill-export-requests.ts +0 -11
- package/cli/lib/cmd-fns/export-gerbers.ts +8 -2
- package/cli/lib/export-fns/export-gerbers.ts +1 -6
- package/cli/lib/get-program.ts +5 -1
- package/cli/lib/util/create-context-and-run-program.ts +5 -0
- package/cli/tests/export-gerber-keyboard.test.ts +16 -0
- package/cli/tests/export-gerber.test.ts +49 -0
- package/cli/tests/fixtures/preload.ts +54 -0
- package/dist/cli.js +23 -22
- package/example-project/examples/basic-chip.tsx +2 -7
- package/frontend/views/HeaderMenu.tsx +2 -2
- package/package.json +8 -3
package/bun.lockb
CHANGED
|
Binary file
|
package/bunfig.toml
ADDED
|
@@ -60,17 +60,6 @@ export const fulfillExportRequests = async (
|
|
|
60
60
|
|
|
61
61
|
if (export_request.export_parameters.should_export_gerber_zip) {
|
|
62
62
|
console.log(kleur.gray(`\n exporting gerbers...`))
|
|
63
|
-
if (typeof Bun !== "undefined") {
|
|
64
|
-
const err_str =
|
|
65
|
-
"Bun currently isn't capable of exporting due to an archiver bug, exports will not work."
|
|
66
|
-
console.log(kleur.red(err_str))
|
|
67
|
-
await dev_server_axios.post("/api/export_requests/update", {
|
|
68
|
-
export_request_id: export_request.export_request_id,
|
|
69
|
-
has_error: true,
|
|
70
|
-
error: err_str,
|
|
71
|
-
})
|
|
72
|
-
return
|
|
73
|
-
}
|
|
74
63
|
const zip_buffer = await exportGerbersToZipBuffer(
|
|
75
64
|
{
|
|
76
65
|
example_file_path: export_request.example_file_path!,
|
|
@@ -5,15 +5,21 @@ import { exportGerbersToFile } from "cli/lib/export-fns/export-gerbers"
|
|
|
5
5
|
export const exportGerbersCmd = async (ctx: AppContext, args: any) => {
|
|
6
6
|
const params = z
|
|
7
7
|
.object({
|
|
8
|
-
file: z.string(),
|
|
8
|
+
file: z.string().optional(),
|
|
9
|
+
input: z.string().optional(),
|
|
9
10
|
export: z.string().optional(),
|
|
10
11
|
outputfile: z.string().optional().default("gerbers.zip"),
|
|
11
12
|
})
|
|
13
|
+
.refine((data) => data.file || data.input, {
|
|
14
|
+
message: "Either 'file' or 'input' must be provided",
|
|
15
|
+
})
|
|
12
16
|
.parse(args)
|
|
13
17
|
|
|
18
|
+
const inputFile = params.input || params.file
|
|
19
|
+
|
|
14
20
|
await exportGerbersToFile(
|
|
15
21
|
{
|
|
16
|
-
example_file_path:
|
|
22
|
+
example_file_path: inputFile!,
|
|
17
23
|
export_name: params.export,
|
|
18
24
|
output_zip_path: params.outputfile,
|
|
19
25
|
},
|
|
@@ -63,14 +63,9 @@ export const exportGerbersToFile = async (
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
console.log(kleur.gray("[zipping tmp dir]..."))
|
|
66
|
+
console.log(kleur.gray(" writing to " + params.output_zip_path))
|
|
66
67
|
const output = fs.createWriteStream(params.output_zip_path)
|
|
67
68
|
|
|
68
|
-
if (typeof Bun !== "undefined") {
|
|
69
|
-
throw new Error(
|
|
70
|
-
`Exporting gerbers doesn't currently work with Bun (bug w/ archiver module)`,
|
|
71
|
-
)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
69
|
const archive = archiver("zip", {
|
|
75
70
|
zlib: { level: 9 },
|
|
76
71
|
})
|
package/cli/lib/get-program.ts
CHANGED
|
@@ -281,7 +281,11 @@ export const getProgram = (ctx: AppContext) => {
|
|
|
281
281
|
exportCmd
|
|
282
282
|
.command("gerbers")
|
|
283
283
|
.description("Export Gerber files from an example file")
|
|
284
|
-
.
|
|
284
|
+
.option(
|
|
285
|
+
"--file <file>",
|
|
286
|
+
"Input example file (deprecated, use --input instead)",
|
|
287
|
+
)
|
|
288
|
+
.option("--input <input>", "Input example file")
|
|
285
289
|
.option(
|
|
286
290
|
"--export <export_name>",
|
|
287
291
|
"Name of export to soupify, if not specified, soupify the default/only export",
|
|
@@ -121,7 +121,12 @@ export const createContextAndRunProgram = async (process_args: any) => {
|
|
|
121
121
|
params: args,
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
if (args["color"] === false) {
|
|
125
|
+
kleur.enabled = false
|
|
126
|
+
}
|
|
127
|
+
|
|
124
128
|
delete args["cwd"]
|
|
129
|
+
delete args["color"]
|
|
125
130
|
|
|
126
131
|
const { _: positional, ...flagsAndParams } = args
|
|
127
132
|
const args_without_globals = positional.concat(dargs(flagsAndParams))
|
|
@@ -0,0 +1,16 @@
|
|
|
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 } from "fs"
|
|
6
|
+
|
|
7
|
+
test("tsci export gerbers --input example-project/examples/macrokeypad.tsx", async () => {
|
|
8
|
+
const tempDir = temporaryDirectory()
|
|
9
|
+
const { stdout, stderr } =
|
|
10
|
+
await $`bun cli/cli.ts export gerbers --input example-project/examples/macrokeypad.tsx --outputfile ${join(tempDir, "gerbers.zip")} --no-color`
|
|
11
|
+
|
|
12
|
+
expect(stderr.toString()).toBe("")
|
|
13
|
+
expect(stdout.toString()).toContain("gerbers.zip")
|
|
14
|
+
|
|
15
|
+
expect(existsSync(join(tempDir, "gerbers.zip"))).toBe(true)
|
|
16
|
+
})
|
|
@@ -0,0 +1,49 @@
|
|
|
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, readdirSync, readFileSync } from "fs"
|
|
6
|
+
import extract from "extract-zip"
|
|
7
|
+
import pcbStackup from "pcb-stackup"
|
|
8
|
+
|
|
9
|
+
test("tsci export gerbers --input example-project/examples/basic-chip.tsx", async () => {
|
|
10
|
+
const tempDir = temporaryDirectory()
|
|
11
|
+
const gerberPath = join(tempDir, "gerbers.zip")
|
|
12
|
+
const { stdout, stderr } =
|
|
13
|
+
await $`bun cli/cli.ts export gerbers --input example-project/examples/basic-chip.tsx --outputfile ${gerberPath} --no-color`
|
|
14
|
+
|
|
15
|
+
expect(stderr.toString()).toBe("")
|
|
16
|
+
expect(stdout.toString()).toContain("gerbers.zip")
|
|
17
|
+
|
|
18
|
+
expect(existsSync(join(tempDir, "gerbers.zip"))).toBe(true)
|
|
19
|
+
|
|
20
|
+
await extract(gerberPath, { dir: join(tempDir, "gerbers") })
|
|
21
|
+
|
|
22
|
+
const files = readdirSync(join(tempDir, "gerbers"))
|
|
23
|
+
const expectedFiles = [
|
|
24
|
+
"F_Mask.gbr",
|
|
25
|
+
"F_SilkScreen.gbr",
|
|
26
|
+
"B_Cu.gbr",
|
|
27
|
+
"plated.drl",
|
|
28
|
+
"B_SilkScreen.gbr",
|
|
29
|
+
"F_Cu.gbr",
|
|
30
|
+
"B_Paste.gbr",
|
|
31
|
+
"F_Paste.gbr",
|
|
32
|
+
"B_Mask.gbr",
|
|
33
|
+
"Edge_Cuts.gbr",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
expectedFiles.forEach((file) => {
|
|
37
|
+
expect(files).toContain(file)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const gerberOutputMap = Object.entries(
|
|
41
|
+
files.map((filename) => [
|
|
42
|
+
filename,
|
|
43
|
+
readFileSync(join(tempDir, "gerbers", filename)),
|
|
44
|
+
]),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
// Unfortunately doesn't work in bun yet due to some bug in node:stream
|
|
48
|
+
// expect(gerberOutputMap).toMatchGerberSnapshot(import.meta.path)
|
|
49
|
+
})
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import "bun-match-svg"
|
|
2
|
+
import { expect } from "bun:test"
|
|
3
|
+
import pcbStackup, { type Stackup } from "pcb-stackup"
|
|
4
|
+
import { Readable } from "stream"
|
|
5
|
+
|
|
6
|
+
async function toMatchGerberSnapshot(
|
|
7
|
+
this: any,
|
|
8
|
+
gerberOutput: Record<string, string>,
|
|
9
|
+
testPathOriginal: string,
|
|
10
|
+
svgName?: string,
|
|
11
|
+
) {
|
|
12
|
+
// Create layers array from gerberOutput
|
|
13
|
+
const layers = Object.entries(gerberOutput).map(([filename, content]) => ({
|
|
14
|
+
filename,
|
|
15
|
+
gerber: Readable.from(content),
|
|
16
|
+
}))
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const stackup = await pcbStackup(layers)
|
|
20
|
+
const svgArray: string[] = []
|
|
21
|
+
const svgNames: string[] = []
|
|
22
|
+
|
|
23
|
+
for (const item of Object.keys(stackup!) as Array<keyof Stackup>) {
|
|
24
|
+
const layer = stackup[item] as { svg: string }
|
|
25
|
+
if (layer.svg) {
|
|
26
|
+
svgArray.push(layer.svg)
|
|
27
|
+
svgNames.push(`${svgName}-${item}`)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return expect(svgArray).toMatchMultipleSvgSnapshots(
|
|
31
|
+
testPathOriginal,
|
|
32
|
+
svgNames,
|
|
33
|
+
)
|
|
34
|
+
} catch (error) {
|
|
35
|
+
throw new Error(`Failed to generate PCB stackup: ${error}`)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
expect.extend({
|
|
40
|
+
// biome-ignore lint/suspicious/noExplicitAny:
|
|
41
|
+
toMatchGerberSnapshot: toMatchGerberSnapshot as any,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
declare module "bun:test" {
|
|
45
|
+
interface Matchers<T = unknown> {
|
|
46
|
+
/**
|
|
47
|
+
* This doesn't currently work in bun, some bug in node:stream
|
|
48
|
+
*/
|
|
49
|
+
toMatchGerberSnapshot(
|
|
50
|
+
testImportMetaPath: string,
|
|
51
|
+
svgName?: string,
|
|
52
|
+
): Promise<MatcherResult>
|
|
53
|
+
}
|
|
54
|
+
}
|