@tscircuit/cli 0.0.2

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.
Files changed (110) hide show
  1. package/.prettierrc +1 -0
  2. package/README.md +50 -0
  3. package/build-cli.ts +14 -0
  4. package/bun.lockb +0 -0
  5. package/cli.ts +12 -0
  6. package/dev-server-api/README.md +3 -0
  7. package/dev-server-api/bun.lockb +0 -0
  8. package/dev-server-api/edgespec.config.ts +4 -0
  9. package/dev-server-api/index.ts +1 -0
  10. package/dev-server-api/package-lock.json +1201 -0
  11. package/dev-server-api/package.json +24 -0
  12. package/dev-server-api/routes/api/dev_package_examples/create.ts +43 -0
  13. package/dev-server-api/routes/api/dev_package_examples/get.ts +37 -0
  14. package/dev-server-api/routes/api/dev_package_examples/list.ts +29 -0
  15. package/dev-server-api/routes/api/dev_package_examples/update.ts +31 -0
  16. package/dev-server-api/routes/api/health.ts +11 -0
  17. package/dev-server-api/src/db/create-schema.ts +14 -0
  18. package/dev-server-api/src/db/get-db.ts +78 -0
  19. package/dev-server-api/src/middlewares/with-db.ts +14 -0
  20. package/dev-server-api/src/middlewares/with-error-response.ts +24 -0
  21. package/dev-server-api/src/with-edge-spec.ts +8 -0
  22. package/dev-server-api/tsconfig.json +28 -0
  23. package/dev-server-frontend/.eslintrc.cjs +20 -0
  24. package/dev-server-frontend/README.md +3 -0
  25. package/dev-server-frontend/bun.lockb +0 -0
  26. package/dev-server-frontend/components.json +17 -0
  27. package/dev-server-frontend/index.html +13 -0
  28. package/dev-server-frontend/package.json +62 -0
  29. package/dev-server-frontend/postcss.config.js +6 -0
  30. package/dev-server-frontend/src/App.tsx +22 -0
  31. package/dev-server-frontend/src/ExampleContentView.tsx +60 -0
  32. package/dev-server-frontend/src/Header.tsx +53 -0
  33. package/dev-server-frontend/src/HeaderMenu.tsx +188 -0
  34. package/dev-server-frontend/src/components/command-k.tsx +62 -0
  35. package/dev-server-frontend/src/components/global-context-providers.tsx +11 -0
  36. package/dev-server-frontend/src/components/select-example-search.tsx +137 -0
  37. package/dev-server-frontend/src/components/ui/alert-dialog.tsx +139 -0
  38. package/dev-server-frontend/src/components/ui/alert.tsx +59 -0
  39. package/dev-server-frontend/src/components/ui/breadcrumb.tsx +115 -0
  40. package/dev-server-frontend/src/components/ui/button.tsx +57 -0
  41. package/dev-server-frontend/src/components/ui/card.tsx +76 -0
  42. package/dev-server-frontend/src/components/ui/command.tsx +153 -0
  43. package/dev-server-frontend/src/components/ui/context-menu.tsx +202 -0
  44. package/dev-server-frontend/src/components/ui/dialog.tsx +120 -0
  45. package/dev-server-frontend/src/components/ui/menubar.tsx +238 -0
  46. package/dev-server-frontend/src/components/ui/navigation-menu.tsx +128 -0
  47. package/dev-server-frontend/src/components/ui/popover.tsx +31 -0
  48. package/dev-server-frontend/src/components/ui/select.tsx +162 -0
  49. package/dev-server-frontend/src/components/ui/tabs.tsx +53 -0
  50. package/dev-server-frontend/src/components/ui/toggle-group.tsx +59 -0
  51. package/dev-server-frontend/src/components/ui/toggle.tsx +43 -0
  52. package/dev-server-frontend/src/components/ui/tooltip.tsx +28 -0
  53. package/dev-server-frontend/src/hooks/toast-if-api-not-connected.ts +23 -0
  54. package/dev-server-frontend/src/hooks/use-global-store.ts +42 -0
  55. package/dev-server-frontend/src/index.css +76 -0
  56. package/dev-server-frontend/src/lib/utils.ts +6 -0
  57. package/dev-server-frontend/src/main.tsx +13 -0
  58. package/dev-server-frontend/src/vite-env.d.ts +1 -0
  59. package/dev-server-frontend/tailwind.config.js +77 -0
  60. package/dev-server-frontend/tsconfig.json +26 -0
  61. package/dev-server-frontend/tsconfig.node.json +13 -0
  62. package/dev-server-frontend/vite.config.ts +23 -0
  63. package/dist/cli.js +1326 -0
  64. package/lib/cmd-fns/auth-login.ts +59 -0
  65. package/lib/cmd-fns/auth-logout.ts +3 -0
  66. package/lib/cmd-fns/auth-sessions-create.ts +3 -0
  67. package/lib/cmd-fns/auth-sessions-get.ts +3 -0
  68. package/lib/cmd-fns/auth-sessions-list.ts +5 -0
  69. package/lib/cmd-fns/config-print-config.ts +6 -0
  70. package/lib/cmd-fns/config-reveal-location.ts +5 -0
  71. package/lib/cmd-fns/config-set-log-requests.ts +7 -0
  72. package/lib/cmd-fns/config-set-registry.ts +7 -0
  73. package/lib/cmd-fns/config-set-session.ts +7 -0
  74. package/lib/cmd-fns/dev.ts +217 -0
  75. package/lib/cmd-fns/index.ts +27 -0
  76. package/lib/cmd-fns/package-examples-create.ts +33 -0
  77. package/lib/cmd-fns/package-examples-get.ts +20 -0
  78. package/lib/cmd-fns/package-examples-list.ts +18 -0
  79. package/lib/cmd-fns/package-files-create.ts +31 -0
  80. package/lib/cmd-fns/package-files-download.ts +29 -0
  81. package/lib/cmd-fns/package-files-get.ts +3 -0
  82. package/lib/cmd-fns/package-files-list.ts +28 -0
  83. package/lib/cmd-fns/package-files-upload-directory.ts +6 -0
  84. package/lib/cmd-fns/package-releases-create.ts +35 -0
  85. package/lib/cmd-fns/package-releases-get.ts +3 -0
  86. package/lib/cmd-fns/package-releases-list.ts +32 -0
  87. package/lib/cmd-fns/packages-create.ts +16 -0
  88. package/lib/cmd-fns/packages-get.ts +16 -0
  89. package/lib/cmd-fns/packages-list.ts +16 -0
  90. package/lib/cmd-fns/publish.ts +3 -0
  91. package/lib/cmd-fns/soupify.ts +27 -0
  92. package/lib/get-program.ts +191 -0
  93. package/lib/param-handlers/index.ts +16 -0
  94. package/lib/param-handlers/interact-for-local-directory.ts +58 -0
  95. package/lib/param-handlers/interact-for-local-file.ts +59 -0
  96. package/lib/param-handlers/interact-for-package-example-id.ts +25 -0
  97. package/lib/param-handlers/interact-for-package-name-with-version.ts +63 -0
  98. package/lib/param-handlers/interact-for-package-name.ts +45 -0
  99. package/lib/param-handlers/interact-for-package-release-id.ts +16 -0
  100. package/lib/param-handlers/param-handler-type.ts +7 -0
  101. package/lib/soupify.ts +71 -0
  102. package/lib/util/app-context.ts +14 -0
  103. package/lib/util/create-context-and-run-program.ts +116 -0
  104. package/package.json +62 -0
  105. package/tests/assets/example-project/examples/basic-capacitor.tsx +1 -0
  106. package/tests/assets/example-project/examples/basic-resistor.tsx +3 -0
  107. package/tests/assets/example-project/package.json +8 -0
  108. package/tests/assets/example-project/src/MyCircuit.tsx +3 -0
  109. package/tests/soupify.test.ts +9 -0
  110. package/tsconfig.json +109 -0
@@ -0,0 +1,27 @@
1
+ import { AppContext } from "../util/app-context"
2
+ import { z } from "zod"
3
+ import * as Path from "path"
4
+ import { unlink } from "node:fs/promises"
5
+ import { soupify } from "lib/soupify"
6
+ import { writeFileSync } from "fs"
7
+
8
+ export const soupifyCmd = async (ctx: AppContext, args: any) => {
9
+ const params = z
10
+ .object({
11
+ file: z.string(),
12
+ export: z.string().optional(),
13
+ output: z.string().optional(),
14
+ })
15
+ .parse(args)
16
+
17
+ const soup = await soupify({
18
+ filePath: params.file,
19
+ exportName: params.export,
20
+ })
21
+
22
+ if (params.output) {
23
+ await writeFileSync(params.output, JSON.stringify(soup, null, 2))
24
+ } else {
25
+ console.log(JSON.stringify(soup, null, 2))
26
+ }
27
+ }
@@ -0,0 +1,191 @@
1
+ import { Command } from "commander"
2
+ import packageJson from "../package.json"
3
+ import { AppContext } from "./util/app-context"
4
+ import * as CMDFN from "lib/cmd-fns"
5
+
6
+ // | Endpoint | Description |
7
+ // | ------------------------------------- | ---------------------------- |
8
+ // | /packages/search | Search for packages |
9
+ // | /packages/list | List packages with a filter |
10
+ // | /packages/get | Get a package by id or name |
11
+ // | /packages/create | Create a new package |
12
+ // | /package_releases/list | List package releases |
13
+ // | /package_releases/get | Get a package release by id |
14
+ // | /package_releases/create | Create a new package release |
15
+ // | /package_files/list | List package files |
16
+ // | /package_files/get | Get a package file by id |
17
+ // | /package_files/download | Download a package file |
18
+ // | /package_files/create | Create a new package file |
19
+ // | /package_examples/list | List package examples |
20
+ // | /package_examples/get | Get a package example by id |
21
+ // | /package_examples/create | Create a new package example |
22
+
23
+ export const getProgram = (ctx: AppContext) => {
24
+ const cmd = new Command("tsci")
25
+
26
+ cmd.version(packageJson.version)
27
+
28
+ const authCmd = cmd.command("auth")
29
+ authCmd.command("login").action((args) => CMDFN.authLogin(ctx, args))
30
+ authCmd.command("logout").action((args) => CMDFN.authLogout(ctx, args))
31
+
32
+ const configCmd = cmd.command("config")
33
+
34
+ configCmd
35
+ .command("reveal-location")
36
+ .action((args) => CMDFN.configRevealLocation(ctx, args))
37
+ configCmd
38
+ .command("set-registry")
39
+ .requiredOption("--server <server>", "Registry URL")
40
+ .action((args) => CMDFN.configSetRegistry(ctx, args))
41
+ configCmd
42
+ .command("set-session")
43
+ .requiredOption("--session-token <session_token>", "Session Token")
44
+ .action((args) => CMDFN.configSetSession(ctx, args))
45
+ configCmd
46
+ .command("set-log-requests")
47
+ .requiredOption("--log-requests", "Should log requests to registry")
48
+ .action((args) => CMDFN.configSetLogRequests(ctx, args))
49
+ configCmd
50
+ .command("print-config")
51
+ .action((args) => CMDFN.configPrintConfig(ctx, args))
52
+
53
+ const authSessionsCmd = authCmd.command("sessions")
54
+
55
+ authSessionsCmd
56
+ .command("create")
57
+ .action((args) => CMDFN.authSessionsCreate(ctx, args))
58
+ authSessionsCmd
59
+ .command("list")
60
+ .action((args) => CMDFN.authSessionsList(ctx, args))
61
+ authSessionsCmd
62
+ .command("get")
63
+ .action((args) => CMDFN.authSessionsGet(ctx, args))
64
+
65
+ const packagesCmd = cmd.command("packages")
66
+
67
+ packagesCmd.command("list").action((args) => CMDFN.packagesList(ctx, args))
68
+ packagesCmd
69
+ .command("get")
70
+ .option("--package-id <package_id>", "Package Id")
71
+ .option("--name <name>", "Package name")
72
+ .action((args) => CMDFN.packagesGet(ctx, args))
73
+ packagesCmd
74
+ .command("create")
75
+ .requiredOption("--name <name>", "Package name")
76
+ .option("--author-id <author_account_id>", "Author Id")
77
+ .option("--description <description>", "Package description")
78
+ .action((args) => CMDFN.packagesCreate(ctx, args))
79
+
80
+ const packageReleases = cmd.command("package_releases")
81
+
82
+ packageReleases
83
+ .command("list")
84
+ .requiredOption("--package-name <package_name>", "Package name")
85
+ .option("--verbose", "Verbose objects (includes uuids)")
86
+ .action((args) => CMDFN.packageReleasesList(ctx, args))
87
+ packageReleases
88
+ .command("get")
89
+ .action((args) => CMDFN.packageReleasesGet(ctx, args))
90
+ packageReleases
91
+ .command("create")
92
+ .option(
93
+ "-p, --package-name-with-version <package_name_with_version>",
94
+ "Package name and version"
95
+ )
96
+ .option("--package-name <package_name>", "Package name")
97
+ .option("--release-version <release_version>", "Version to publish")
98
+ .action((args) => CMDFN.packageReleasesCreate(ctx, args))
99
+
100
+ const packageFiles = cmd.command("package_files")
101
+
102
+ packageFiles
103
+ .command("list")
104
+ .option(
105
+ "--package-name-with-version <package_name_with_version>",
106
+ "Package name with version"
107
+ )
108
+ .option(
109
+ "--package-name <package_name>",
110
+ "Package name (use latest version)"
111
+ )
112
+ .option("--package-release-id <package_release_id>", "Package Release Id")
113
+ .action((args) => CMDFN.packageFilesList(ctx, args))
114
+ packageFiles.command("get").action((args) => CMDFN.packageFilesGet(ctx, args))
115
+ packageFiles
116
+ .command("download")
117
+ .requiredOption(
118
+ "--package-name-with-version <package_name_with_version>",
119
+ "Package name and version"
120
+ )
121
+ .requiredOption("--remote-file-path <remote_file_path>", "Remote file path")
122
+ .option(
123
+ "--output <output>",
124
+ "Output file path (optional), prints to stdout if not provided"
125
+ )
126
+ .action((args) => CMDFN.packageFilesDownload(ctx, args))
127
+ packageFiles
128
+ .command("create")
129
+ .option(
130
+ "-p, --package-release-id <package_release_id>",
131
+ "Package Release Id"
132
+ )
133
+ .option(
134
+ "--package-name-with-version <package_name_with_version>",
135
+ "Package name with version e.g. @tscircuit/arduino@1.2.3"
136
+ )
137
+ .requiredOption("--file <file>", "File to upload")
138
+ .action((args) => CMDFN.packageFilesCreate(ctx, args))
139
+
140
+ packageFiles
141
+ .command("upload-directory")
142
+ .requiredOption("--directory <directory>", "Directory to upload")
143
+ .action((args) => CMDFN.packageFilesUploadDirectory(ctx, args))
144
+
145
+ const packageExamples = cmd.command("package_examples")
146
+
147
+ packageExamples
148
+ .command("list")
149
+ .requiredOption("--package-name-with-version <package_name_with_version>")
150
+ .action((args) => CMDFN.packageExamplesList(ctx, args))
151
+ packageExamples
152
+ .command("get")
153
+ .requiredOption("--package-example-id <package_example_id>")
154
+ .action((args) => CMDFN.packageExamplesGet(ctx, args))
155
+ packageExamples
156
+ .command("create")
157
+ .requiredOption("--package-name-with-version <package_name_with_version>")
158
+ .requiredOption("--file <file>")
159
+ .option("--export <export>", "Name of export to soupify")
160
+ .action((args) => CMDFN.packageExamplesCreate(ctx, args))
161
+
162
+ cmd.command("publish").action((args) => CMDFN.publish(ctx, args))
163
+
164
+ cmd
165
+ .command("version")
166
+ .action(() => console.log(`tsci v${packageJson.version}`))
167
+
168
+ cmd.command("login").action((args) => CMDFN.authLogin(ctx, args))
169
+ cmd.command("logout").action((args) => CMDFN.authLogout(ctx, args))
170
+
171
+ cmd
172
+ .command("soupify")
173
+ .requiredOption("--file <file>", "Input example files")
174
+ .option(
175
+ "--export <export_name>",
176
+ "Name of export to soupify, if not specified, soupify the default/only export"
177
+ )
178
+ .action((args) => CMDFN.soupify(ctx, args))
179
+
180
+ cmd
181
+ .command("dev")
182
+ .option("--cwd <cwd>", "Current working directory")
183
+ .option("--port <port>", "Port to run dev server on")
184
+ .action((args) => CMDFN.dev(ctx, args))
185
+
186
+ cmd.command("add").action((args) => {})
187
+ cmd.command("remove").action((args) => {})
188
+ cmd.command("install").action((args) => {})
189
+
190
+ return cmd
191
+ }
@@ -0,0 +1,16 @@
1
+ import { interactForLocalDirectory } from "./interact-for-local-directory"
2
+ import { interactForLocalFile } from "./interact-for-local-file"
3
+ import { interactForPackageExampleId } from "./interact-for-package-example-id"
4
+ import { interactForPackageName } from "./interact-for-package-name"
5
+ import { interactForPackageNameWithVersion } from "./interact-for-package-name-with-version"
6
+ import { interactForPackageReleaseId } from "./interact-for-package-release-id"
7
+ import { ParamHandler } from "./param-handler-type"
8
+
9
+ export const PARAM_HANDLERS_BY_PARAM_NAME: Record<string, ParamHandler> = {
10
+ file: interactForLocalFile,
11
+ cwd: interactForLocalDirectory,
12
+ package_release_id: interactForPackageReleaseId,
13
+ package_name: interactForPackageName,
14
+ package_name_with_version: interactForPackageNameWithVersion,
15
+ package_example_id: interactForPackageExampleId,
16
+ }
@@ -0,0 +1,58 @@
1
+ import { ParamHandler } from "./param-handler-type"
2
+ import * as fs from "fs/promises"
3
+
4
+ export const interactForLocalDirectory: ParamHandler = async ({ prompts }) => {
5
+ const { selectionMode } = await prompts({
6
+ type: "select",
7
+ name: "selectionMode",
8
+ message: "Select a directory",
9
+ choices: [
10
+ { title: "Manually Enter Path", value: "manual" },
11
+ { title: "Browse for Directory", value: "browse" },
12
+ ],
13
+ })
14
+
15
+ if (selectionMode === "manual") {
16
+ const { manualPath } = await prompts({
17
+ type: "text",
18
+ name: "manualPath",
19
+ message: "Enter the path to the directory",
20
+ })
21
+ return manualPath
22
+ }
23
+
24
+ let currentDirectory = "."
25
+ while (true) {
26
+ const files = await fs.readdir(currentDirectory)
27
+ const dirChoices = files
28
+ .filter((d) => !d.includes("."))
29
+ .map((file) => ({ title: file, value: file }))
30
+ const { selectedDir } = await prompts({
31
+ type: "autocomplete",
32
+ name: "selectedDir",
33
+ message: "Select a directory",
34
+ initial: "__select__",
35
+ choices: [
36
+ { title: ".. (Go Up)", value: ".." },
37
+ { title: `["${currentDirectory}"]`, value: "__select__" },
38
+ ...dirChoices,
39
+ ],
40
+ })
41
+ if (selectedDir === "..") {
42
+ // Go up one directory
43
+ const lastIndex = currentDirectory.lastIndexOf("/")
44
+ currentDirectory = currentDirectory.substring(0, lastIndex)
45
+ } else if (selectedDir === "__select__") {
46
+ return currentDirectory
47
+ } else {
48
+ // Check if selectedFile is a file or directory
49
+ const stats = await fs.stat(`${currentDirectory}/${selectedDir}`)
50
+ if (stats.isFile()) {
51
+ throw new Error("Selected file is a file, not a directory")
52
+ } else {
53
+ // Go into the selected directory
54
+ currentDirectory = `${currentDirectory}/${selectedDir}`
55
+ }
56
+ }
57
+ }
58
+ }
@@ -0,0 +1,59 @@
1
+ import { ParamHandler } from "./param-handler-type"
2
+ import * as fs from "fs/promises"
3
+
4
+ // Use prompts to allow searching for a file
5
+ // 1. Show a prompt with two options
6
+ // - "Manually Enter Path"
7
+ // - "Browse for File"
8
+ // 2. If Browse for File is selected, show all the files in the current
9
+ // directory with the "autocomplete" prompts type, then allow the user
10
+ // to select a file or directory
11
+ export const interactForLocalFile: ParamHandler = async ({ prompts }) => {
12
+ const { selectionMode } = await prompts({
13
+ type: "select",
14
+ name: "selectionMode",
15
+ message: "Select a file",
16
+ choices: [
17
+ { title: "Manually Enter Path", value: "manual" },
18
+ { title: "Browse for File", value: "browse" },
19
+ ],
20
+ })
21
+
22
+ if (selectionMode === "manual") {
23
+ const { manualPath } = await prompts({
24
+ type: "text",
25
+ name: "manualPath",
26
+ message: "Enter the path to the file",
27
+ })
28
+ return manualPath
29
+ }
30
+
31
+ let currentDirectory = "."
32
+ while (true) {
33
+ const files = await fs.readdir(currentDirectory)
34
+ const { selectedFile } = await prompts({
35
+ type: "autocomplete",
36
+ name: "selectedFile",
37
+ message: "Select a file or directory",
38
+ choices: [
39
+ { title: ".. (Go Up)", value: ".." },
40
+ ...files.map((file) => ({ title: file, value: file })),
41
+ ],
42
+ })
43
+ if (selectedFile === "..") {
44
+ // Go up one directory
45
+ const lastIndex = currentDirectory.lastIndexOf("/")
46
+ currentDirectory = currentDirectory.substring(0, lastIndex)
47
+ } else {
48
+ // Check if selectedFile is a file or directory
49
+ const stats = await fs.stat(`${currentDirectory}/${selectedFile}`)
50
+ if (stats.isFile()) {
51
+ // Break out of the loop and return the selected file
52
+ return `${currentDirectory}/${selectedFile}`
53
+ } else {
54
+ // Go into the selected directory
55
+ currentDirectory = `${currentDirectory}/${selectedFile}`
56
+ }
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,25 @@
1
+ import { interactForPackageReleaseId } from "./interact-for-package-release-id"
2
+ import { ParamHandler } from "./param-handler-type"
3
+
4
+ export const interactForPackageExampleId: ParamHandler = async (params) => {
5
+ const { prompts, ctx } = params
6
+ const package_release_id = await interactForPackageReleaseId(params)
7
+
8
+ const package_examples = await ctx.axios
9
+ .post("/package_examples/list", {
10
+ package_release_id,
11
+ })
12
+ .then((r) => r.data.package_examples)
13
+
14
+ const { package_example_id } = await prompts({
15
+ type: "autocomplete",
16
+ name: "package_example_id",
17
+ message: "Select a package example",
18
+ choices: package_examples.map((p: any) => ({
19
+ title: `${p.file_path} (${p.export_name})`,
20
+ value: p.package_example_id,
21
+ })),
22
+ })
23
+
24
+ return package_example_id
25
+ }
@@ -0,0 +1,63 @@
1
+ import { ParamHandler } from "./param-handler-type"
2
+
3
+ export const interactForPackageNameWithVersion: ParamHandler = async ({
4
+ prompts,
5
+ ctx,
6
+ }) => {
7
+ // Find all package names by this user
8
+ const myPackages: Array<{ name: string; latest_version: string }> =
9
+ await ctx.axios
10
+ .post("/packages/list", {
11
+ is_writable: true,
12
+ })
13
+ .then((r) => r.data.packages)
14
+
15
+ // Autocomplete prompts with all packages, first option is "Enter Manually"
16
+ const { selectedPackage } = await prompts({
17
+ type: "autocomplete",
18
+ name: "selectedPackage",
19
+ message: "Select a package",
20
+ choices: [
21
+ {
22
+ title: "Enter Manually",
23
+ value: null,
24
+ },
25
+ ...myPackages.flatMap((p) => [
26
+ {
27
+ title: `${p.name}@${p.latest_version}`,
28
+ value: `${p.name}@${p.latest_version}`,
29
+ },
30
+ {
31
+ title: `${p.name}@...`,
32
+ value: `${p.name}@...`,
33
+ },
34
+ ]),
35
+ ],
36
+ })
37
+
38
+ if (selectedPackage.endsWith("@...")) {
39
+ // TODO Poll for all versions available
40
+
41
+ const { version } = await prompts({
42
+ type: "text",
43
+ name: "version",
44
+ message: "Enter the version",
45
+ })
46
+
47
+ return selectedPackage.replace("@...", `@${version}`)
48
+ }
49
+
50
+ if (selectedPackage === "Enter Manually") {
51
+ const { packageName } = await prompts({
52
+ type: "text",
53
+ name: "packageName",
54
+ message: "Enter the package name",
55
+ })
56
+
57
+ return packageName
58
+ }
59
+
60
+ // TODO cache recently selected packages
61
+
62
+ return selectedPackage
63
+ }
@@ -0,0 +1,45 @@
1
+ import { ParamHandler } from "./param-handler-type"
2
+ import * as fs from "fs/promises"
3
+
4
+ export const interactForPackageName: ParamHandler = async ({
5
+ prompts,
6
+ ctx,
7
+ }) => {
8
+ // Find all package names by this user
9
+ const myPackages: Array<{ name: string }> = await ctx.axios
10
+ .post("/packages/list", {
11
+ is_writable: true,
12
+ })
13
+ .then((r) => r.data.packages)
14
+
15
+ // Autocomplete prompts with all packages, first option is "Enter Manually"
16
+ const { selectedPackage } = await prompts({
17
+ type: "autocomplete",
18
+ name: "selectedPackage",
19
+ message: "Select a package",
20
+ choices: [
21
+ {
22
+ title: "Enter Manually",
23
+ value: null,
24
+ },
25
+ ...myPackages.map((p) => ({
26
+ title: p.name,
27
+ value: p.name,
28
+ })),
29
+ ],
30
+ })
31
+
32
+ if (selectedPackage === "Enter Manually") {
33
+ const { packageName } = await prompts({
34
+ type: "text",
35
+ name: "packageName",
36
+ message: "Enter the package name",
37
+ })
38
+
39
+ return packageName
40
+ }
41
+
42
+ // TODO cache recently selected packages
43
+
44
+ return selectedPackage
45
+ }
@@ -0,0 +1,16 @@
1
+ import { interactForPackageNameWithVersion } from "./interact-for-package-name-with-version"
2
+ import { ParamHandler } from "./param-handler-type"
3
+
4
+ export const interactForPackageReleaseId: ParamHandler = async (params) => {
5
+ const { ctx, prompts } = params
6
+
7
+ const package_name_with_version = await interactForPackageNameWithVersion(
8
+ params
9
+ )
10
+
11
+ return await ctx.axios
12
+ .post("/package_releases/get", {
13
+ package_name_with_version,
14
+ })
15
+ .then((r) => r.data.package_release.package_release_id)
16
+ }
@@ -0,0 +1,7 @@
1
+ import { AppContext } from "lib/util/app-context"
2
+ import prompts from "prompts"
3
+
4
+ export type ParamHandler = (params: {
5
+ prompts: typeof prompts
6
+ ctx: AppContext
7
+ }) => Promise<string | null | void>
package/lib/soupify.ts ADDED
@@ -0,0 +1,71 @@
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
+
9
+ export const soupify = async ({
10
+ filePath,
11
+ exportName,
12
+ }: {
13
+ filePath: string
14
+ exportName?: string
15
+ }) => {
16
+ const tmpFilePath = Path.join(
17
+ Path.dirname(filePath),
18
+ Path.basename(filePath).replace(/\.[^\.]+$/, "") + ".__tmp_entrypoint.tsx"
19
+ )
20
+
21
+ writeFileSync(
22
+ tmpFilePath,
23
+ `
24
+ import React from "react"
25
+ import { createRoot } from "@tscircuit/react-fiber"
26
+ import { createProjectBuilder } from "@tscircuit/builder"
27
+
28
+ import * as EXPORTS from "./${Path.basename(filePath)}"
29
+
30
+ const Component = EXPORTS["${exportName}"]
31
+
32
+ if (!Component) {
33
+ console.log(JSON.stringify({
34
+ COMPILE_ERROR: 'Failed to find "${exportName}" export in "${filePath}"'
35
+ }))
36
+ process.exit(0)
37
+ }
38
+
39
+ const projectBuilder = createProjectBuilder()
40
+ const elements = await createRoot().render(<Component />, projectBuilder)
41
+
42
+ console.log(JSON.stringify(elements))
43
+
44
+ `.trim()
45
+ )
46
+
47
+ const processResult = await $`bun ${tmpFilePath}`.captureCombined(true)
48
+
49
+ const rawSoup = processResult.stdout
50
+ const errText = processResult.stderr
51
+
52
+ await unlink(tmpFilePath)
53
+
54
+ try {
55
+ const soup = JSON.parse(rawSoup)
56
+
57
+ if (soup.COMPILE_ERROR) {
58
+ // console.log(kleur.red(`Failed to compile ${filePath}`))
59
+ // console.log(soup.COMPILE_ERROR)
60
+ throw new Error(soup.COMPILE_ERROR)
61
+ }
62
+
63
+ return soup
64
+ } catch (e: any) {
65
+ // console.log(kleur.red(`Failed to parse result of soupify: ${e.toString()}`))
66
+ const t = Date.now()
67
+ console.log(`Outputting raw soupify result to .tscircuit/err-${t}.log`)
68
+ writeFileSync(`.tscircuit/err-${t}.log`, rawSoup + "\n\n" + errText)
69
+ throw e
70
+ }
71
+ }
@@ -0,0 +1,14 @@
1
+ import { AxiosInstance } from "axios"
2
+ import Configstore from "configstore"
3
+
4
+ export type AppContext = {
5
+ args: any
6
+ cwd: string
7
+ cmd: string[]
8
+ params: Record<string, any>
9
+ registry_url: string
10
+ axios: AxiosInstance
11
+ profile: string
12
+ profile_config: Configstore
13
+ global_config: Configstore
14
+ }