@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 +0 -0
- package/cli/lib/cmd-fns/dev/index.ts +1 -0
- package/cli/lib/cmd-fns/dev/soupify-and-upload-example-file.ts +1 -0
- package/cli/lib/cmd-fns/publish/index.ts +1 -1
- package/cli/lib/cmd-fns/soupify.ts +2 -0
- package/cli/lib/cmd-fns/version.ts +1 -1
- package/cli/lib/get-program.ts +2 -0
- package/cli/lib/soupify/get-export-name-from-file.ts +29 -0
- package/cli/lib/soupify/get-tmp-entrpoint-filepath.ts +13 -0
- package/cli/lib/soupify/index.ts +1 -0
- package/cli/lib/soupify/run-entrypoint-file.ts +60 -0
- package/cli/lib/soupify/soupify-with-core.ts +52 -0
- package/cli/lib/soupify/soupify.ts +77 -0
- package/cli/tests/{soupify.test.ts → soupify-builder.test.ts} +1 -1
- package/cli/tests/soupify-core.test.ts +9 -0
- package/dist/cli.js +199 -123
- package/example-project/examples/basic-chip.tsx +4 -3
- package/frontend/views/HeaderMenu.tsx +1 -1
- package/package.json +6 -5
- package/tsconfig.json +10 -1
- package/cli/lib/soupify.ts +0 -115
package/bun.lockb
CHANGED
|
Binary file
|
|
@@ -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.
|
|
15
|
+
current: cliPackageJson.dependencies["@tscircuit/react-fiber"],
|
|
16
16
|
})
|
|
17
17
|
table.push({
|
|
18
18
|
name: "@tscircuit/schematic-viewer",
|
package/cli/lib/get-program.ts
CHANGED
|
@@ -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
|
|
@@ -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
|
+
})
|