@tscircuit/cli 0.0.168 → 0.0.170

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 CHANGED
Binary file
@@ -23,6 +23,7 @@ export const devCmd = async (ctx: AppContext, args: any) => {
23
23
  const params = z
24
24
  .object({
25
25
  port: z.coerce.number().optional().default(3020),
26
+ core: z.boolean().optional(),
26
27
  })
27
28
  .parse(args)
28
29
 
@@ -30,6 +30,7 @@ export const soupifyAndUploadExampleFile = async (
30
30
  {
31
31
  filePath: examplePath,
32
32
  exportName,
33
+ useCore: ctx.params.core,
33
34
  },
34
35
  ctx,
35
36
  )
@@ -7,7 +7,7 @@ import { existsSync, readFileSync } from "fs"
7
7
  import { getAllPackageFiles } from "cli/lib/util/get-all-package-files"
8
8
  import prompts from "prompts"
9
9
  import { getGeneratedReadme } from "../init/get-generated-readme"
10
- import { soupify } from "../../soupify"
10
+ import { soupify } from "../../soupify/soupify"
11
11
  import { inferExportNameFromSource } from "../dev/infer-export-name-from-source"
12
12
  import $ from "dax-sh"
13
13
  import semver from "semver"
@@ -11,6 +11,7 @@ export const soupifyCmd = async (ctx: AppContext, args: any) => {
11
11
  file: z.string(),
12
12
  export: z.string().optional(),
13
13
  output: z.string().optional(),
14
+ core: z.boolean().optional(),
14
15
  })
15
16
  .parse(args)
16
17
 
@@ -18,6 +19,7 @@ export const soupifyCmd = async (ctx: AppContext, args: any) => {
18
19
  {
19
20
  filePath: params.file,
20
21
  exportName: params.export,
22
+ useCore: params.core,
21
23
  },
22
24
  ctx,
23
25
  )
@@ -12,7 +12,7 @@ export const versionCmd = async (ctx: AppContext, args: any) => {
12
12
  table.push({ name: "@tscircuit/cli", current: cliPackageJson.version })
13
13
  table.push({
14
14
  name: "@tscircuit/react-fiber",
15
- current: cliPackageJson.devDependencies["@tscircuit/react-fiber"],
15
+ current: cliPackageJson.dependencies["@tscircuit/react-fiber"],
16
16
  })
17
17
  table.push({
18
18
  name: "@tscircuit/schematic-viewer",
@@ -12,6 +12,7 @@ export const getProgram = (ctx: AppContext) => {
12
12
  .description("Run development server in current directory")
13
13
  .option("--cwd <cwd>", "Current working directory")
14
14
  .option("--port <port>", "Port to run dev server on")
15
+ .option("--core", "Use @tscircuit/core beta")
15
16
  .option("--no-cleanup", "Don't cleanup temporary files (for debugging)")
16
17
  .action((args) => CMDFN.dev(ctx, args))
17
18
 
@@ -293,6 +294,7 @@ export const getProgram = (ctx: AppContext) => {
293
294
  .description("Convert an example file to tscircuit soup")
294
295
  .requiredOption("--file <file>", "Input example files")
295
296
  .option("--output <output.json>", "Output file")
297
+ .option("--core", "Use @tscircuit/core to build (future version)")
296
298
  .option(
297
299
  "--export <export_name>",
298
300
  "Name of export to soupify, if not specified, soupify the default/only export",
@@ -0,0 +1,29 @@
1
+ import { readFile } from "node:fs/promises"
2
+ import Debug from "debug"
3
+
4
+ const debug = Debug("tscircuit:soupify")
5
+
6
+ export const getExportNameFromFile = async (filePath: string) => {
7
+ debug(`reading ${filePath}`)
8
+ const targetFileContent = await readFile(filePath, "utf-8")
9
+
10
+ let exportName: string | undefined
11
+ if (targetFileContent.includes("export default")) {
12
+ exportName = "default"
13
+ } else {
14
+ // Look for "export const <name>" or "export function <name>"
15
+ const exportRegex = /export\s+(?:const|function)\s+(\w+)/g
16
+ const match = exportRegex.exec(targetFileContent)
17
+ if (match) {
18
+ exportName = match[1]
19
+ }
20
+ }
21
+
22
+ if (!exportName) {
23
+ throw new Error(
24
+ `Couldn't derive an export name and didn't find default export in "${filePath}"`,
25
+ )
26
+ }
27
+
28
+ return exportName
29
+ }
@@ -0,0 +1,13 @@
1
+ import Path from "node:path"
2
+
3
+ export const getTmpEntrypointFilePath = (filePath: string) => {
4
+ const tmpEntrypointPath = Path.join(
5
+ Path.dirname(filePath),
6
+ Path.basename(filePath).replace(/\.[^\.]+$/, "") + ".__tmp_entrypoint.tsx",
7
+ )
8
+ const tmpOutputPath = Path.join(
9
+ Path.dirname(filePath),
10
+ Path.basename(filePath).replace(/\.[^\.]+$/, "") + ".__tmp_output.json",
11
+ )
12
+ return { tmpEntrypointPath, tmpOutputPath }
13
+ }
@@ -0,0 +1 @@
1
+ export * from "./soupify"
@@ -0,0 +1,60 @@
1
+ import Debug from "debug"
2
+ import { writeFileSync } from "fs"
3
+ import { readFile, unlink } from "node:fs/promises"
4
+ import $ from "dax-sh"
5
+ import kleur from "kleur"
6
+ import { AppContext } from "../util/app-context"
7
+
8
+ const debug = Debug("tscircuit:soupify")
9
+
10
+ /**
11
+ * Runs the entrypoint file to generate circuit json (soup) for a given file
12
+ */
13
+ export const runEntrypointFile = async (
14
+ {
15
+ tmpEntrypointPath,
16
+ tmpOutputPath,
17
+ }: { tmpEntrypointPath: string; tmpOutputPath: string },
18
+ ctx: Pick<AppContext, "runtime" | "params">,
19
+ ) => {
20
+ debug(`using runtime ${ctx.runtime}`)
21
+ const processCmdPart1 =
22
+ ctx.runtime === "node"
23
+ ? $`npx tsx ${tmpEntrypointPath}`
24
+ : $`bun ${tmpEntrypointPath}`
25
+
26
+ debug(`starting process....`)
27
+ const processResult = await processCmdPart1
28
+ .stdout(debug.enabled ? "inheritPiped" : "piped")
29
+ .stderr(debug.enabled ? "inheritPiped" : "piped")
30
+ .noThrow()
31
+
32
+ const rawSoup = await readFile(tmpOutputPath, "utf-8")
33
+ const errText = processResult.stderr
34
+
35
+ if (ctx.params.cleanup !== false) {
36
+ debug(`deleting ${tmpEntrypointPath}`)
37
+ await unlink(tmpEntrypointPath)
38
+ debug(`deleting ${tmpOutputPath}`)
39
+ await unlink(tmpOutputPath)
40
+ }
41
+
42
+ try {
43
+ debug(`parsing result of soupify...`)
44
+ const soup = JSON.parse(rawSoup)
45
+
46
+ if (soup.COMPILE_ERROR) {
47
+ // console.log(kleur.red(`Failed to compile ${filePath}`))
48
+ console.log(kleur.red(soup.COMPILE_ERROR))
49
+ throw new Error(soup.COMPILE_ERROR)
50
+ }
51
+
52
+ return soup
53
+ } catch (e: any) {
54
+ // console.log(kleur.red(`Failed to parse result of soupify: ${e.toString()}`))
55
+ const t = Date.now()
56
+ console.log(`Dumping raw output to .tscircuit/err-${t}.log`)
57
+ writeFileSync(`.tscircuit/err-${t}.log`, rawSoup + "\n\n" + errText)
58
+ throw new Error(errText)
59
+ }
60
+ }
@@ -0,0 +1,52 @@
1
+ import { AppContext } from "../util/app-context"
2
+ import { z } from "zod"
3
+ import $ from "dax-sh"
4
+ import * as Path from "path"
5
+ import { unlink } from "node:fs/promises"
6
+ import kleur from "kleur"
7
+ import { writeFileSync } from "fs"
8
+ import { readFile } from "fs/promises"
9
+ import Debug from "debug"
10
+ import { getExportNameFromFile } from "./get-export-name-from-file"
11
+ import { getTmpEntrypointFilePath } from "./get-tmp-entrpoint-filepath"
12
+ import { runEntrypointFile } from "./run-entrypoint-file"
13
+
14
+ const debug = Debug("tscircuit:soupify")
15
+
16
+ export const soupifyWithCore = async (
17
+ params: {
18
+ filePath: string
19
+ exportName?: string
20
+ },
21
+ ctx: Pick<AppContext, "runtime" | "params">,
22
+ ) => {
23
+ let { filePath, exportName } = params
24
+
25
+ exportName ??= await getExportNameFromFile(filePath)
26
+
27
+ const { tmpEntrypointPath, tmpOutputPath } =
28
+ getTmpEntrypointFilePath(filePath)
29
+
30
+ debug(`writing to ${tmpEntrypointPath}`)
31
+ writeFileSync(
32
+ tmpEntrypointPath,
33
+ `
34
+ import React from "react"
35
+ import { Project } from "@tscircuit/core"
36
+ import * as EXPORTS from "./${Path.basename(filePath)}"
37
+ import { writeFileSync } from "node:fs"
38
+
39
+ const Component = EXPORTS["${exportName}"]
40
+
41
+ const project = new Project()
42
+
43
+ project.add(<Component />)
44
+
45
+ project.render()
46
+
47
+ writeFileSync("${tmpOutputPath}", JSON.stringify(project.getCircuitJson()))
48
+ `.trim(),
49
+ )
50
+
51
+ return await runEntrypointFile({ tmpEntrypointPath, tmpOutputPath }, ctx)
52
+ }
@@ -0,0 +1,77 @@
1
+ import { AppContext } from "../util/app-context"
2
+ import { z } from "zod"
3
+ import $ from "dax-sh"
4
+ import * as Path from "path"
5
+ import { unlink } from "node:fs/promises"
6
+ import kleur from "kleur"
7
+ import { writeFileSync } from "fs"
8
+ import { readFile } from "fs/promises"
9
+ import Debug from "debug"
10
+ import { soupifyWithCore } from "./soupify-with-core"
11
+ import { getExportNameFromFile } from "./get-export-name-from-file"
12
+ import { getTmpEntrypointFilePath } from "./get-tmp-entrpoint-filepath"
13
+ import { runEntrypointFile } from "./run-entrypoint-file"
14
+
15
+ const debug = Debug("tscircuit:soupify")
16
+
17
+ export const soupifyWithBuilder = async (
18
+ params: {
19
+ filePath: string
20
+ exportName?: string
21
+ /**
22
+ * Use @tscircuit/core instead of @tscircuit/builder, this will be the
23
+ * default eventually
24
+ */
25
+ useCore?: boolean
26
+ },
27
+ ctx: Pick<AppContext, "runtime" | "params">,
28
+ ) => {
29
+ let { filePath, exportName, useCore } = params
30
+ if (useCore) return soupifyWithCore(params, ctx)
31
+
32
+ exportName ??= await getExportNameFromFile(filePath)
33
+
34
+ const { tmpEntrypointPath, tmpOutputPath } =
35
+ getTmpEntrypointFilePath(filePath)
36
+
37
+ debug(`writing to ${tmpEntrypointPath}`)
38
+ writeFileSync(
39
+ tmpEntrypointPath,
40
+ `
41
+ import React from "react"
42
+ import { createRoot } from "@tscircuit/react-fiber"
43
+ import { createProjectBuilder } from "@tscircuit/builder"
44
+ import { writeFileSync } from "node:fs"
45
+
46
+ let Component
47
+ try {
48
+ const EXPORTS = await import("./${Path.basename(filePath)}")
49
+ Component = EXPORTS["${exportName}"]
50
+ } catch (e) {
51
+ writeFileSync("${tmpOutputPath}", JSON.stringify({
52
+ COMPILE_ERROR: e.message + "\\n\\n" + e.stack,
53
+ }))
54
+ }
55
+
56
+ if (!Component) {
57
+ console.log(JSON.stringify({
58
+ COMPILE_ERROR: 'Failed to find "${exportName}" export in "${filePath}"'
59
+ }))
60
+ writeFileSync("${tmpOutputPath}", JSON.stringify({
61
+ COMPILE_ERROR: e.message + "\\n\\n" + e.stack,
62
+ }))
63
+ process.exit(0)
64
+ }
65
+
66
+ const projectBuilder = createProjectBuilder()
67
+ const elements = await createRoot().render(<Component />, projectBuilder)
68
+
69
+ writeFileSync("${tmpOutputPath}", JSON.stringify(elements))
70
+
71
+ `.trim(),
72
+ )
73
+
74
+ return await runEntrypointFile({ tmpEntrypointPath, tmpOutputPath }, ctx)
75
+ }
76
+
77
+ export const soupify = soupifyWithBuilder
@@ -1,7 +1,7 @@
1
1
  import { test, expect, describe } from "bun:test"
2
2
  import { $ } from "bun"
3
3
 
4
- test("soupify", async () => {
4
+ test("soupify (builder)", async () => {
5
5
  const result =
6
6
  await $`bun cli/cli.ts soupify -y --file ./example-project/examples/basic-chip.tsx`.text()
7
7
 
@@ -0,0 +1,9 @@
1
+ import { test, expect, describe } from "bun:test"
2
+ import { $ } from "bun"
3
+
4
+ test("soupify (core)", async () => {
5
+ const result =
6
+ await $`bun cli/cli.ts soupify --core -y --file ./example-project/examples/basic-chip.tsx`.text()
7
+
8
+ expect(result).toContain("10000") // R1 resistor value
9
+ })