@tscircuit/cli 0.0.4 → 0.0.6

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 (36) hide show
  1. package/README.md +31 -22
  2. package/bun.lockb +0 -0
  3. package/dev-server-frontend/bun.lockb +0 -0
  4. package/lib/cmd-fns/add.ts +34 -0
  5. package/lib/cmd-fns/config-reveal-location.ts +1 -1
  6. package/lib/cmd-fns/dev/check-if-initialized.ts +22 -0
  7. package/lib/cmd-fns/dev/dev-server-request-handler.ts +54 -0
  8. package/lib/cmd-fns/dev/get-dev-server-axios.ts +25 -0
  9. package/lib/cmd-fns/dev/index.ts +33 -156
  10. package/lib/cmd-fns/dev/infer-export-name-from-source.ts +17 -0
  11. package/lib/cmd-fns/dev/soupify-and-upload-example-file.ts +38 -0
  12. package/lib/cmd-fns/dev/start-dev-server.ts +34 -0
  13. package/lib/cmd-fns/dev/start-watcher.ts +48 -0
  14. package/lib/cmd-fns/dev/upload-examples-from-directory.ts +26 -0
  15. package/lib/cmd-fns/dev-server-upload.ts +26 -0
  16. package/lib/cmd-fns/index.ts +5 -0
  17. package/lib/cmd-fns/init/create-or-modify-npmrc.ts +21 -0
  18. package/lib/cmd-fns/init/get-generated-npmrc.ts +8 -0
  19. package/lib/cmd-fns/init/get-generated-readme.ts +34 -0
  20. package/lib/cmd-fns/init/get-generated-tsconfig.ts +34 -0
  21. package/lib/cmd-fns/{init.ts → init/index.ts} +19 -64
  22. package/lib/cmd-fns/install.ts +34 -0
  23. package/lib/cmd-fns/publish/index.ts +296 -0
  24. package/lib/cmd-fns/remove.ts +31 -0
  25. package/lib/cmd-fns/uninstall.ts +31 -0
  26. package/lib/get-program.ts +48 -7
  27. package/lib/util/create-context-and-run-program.ts +1 -1
  28. package/lib/util/get-all-package-files.ts +35 -0
  29. package/package.json +13 -6
  30. package/tests/assets/example-project/README.md +18 -0
  31. package/tests/assets/example-project/index.ts +1 -0
  32. package/tests/assets/example-project/package.json +2 -1
  33. package/tests/assets/example-project/src/MyCircuit.tsx +1 -1
  34. package/tests/init.test.ts +1 -1
  35. package/dist/cli.js +0 -1465
  36. package/lib/cmd-fns/publish.ts +0 -3
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # `tsck` - The TSCircuit Command Line Tool
1
+ # `tsci` - The TSCircuit Command Line Tool
2
2
 
3
3
  Command line tool for developing tscircuit projects and interacting with the
4
4
  tscircuit registry.
@@ -11,39 +11,48 @@ npm install -g @tscircuit/cli
11
11
 
12
12
  ## Usage
13
13
 
14
- The `tsck` CLI is interactive by default. You can specify the `-y` or any fully-
14
+ The `tsci` CLI is interactive by default. You can specify the `-y` or any fully-
15
15
  qualified with all required arguments to skip the interactive mode.
16
16
 
17
17
  ```bash
18
18
  # Interactively choose a command and options:
19
- tsck
19
+ tsci
20
20
 
21
21
  # Login
22
- tsck login
22
+ tsci login
23
23
 
24
24
  # Create a Project
25
- tsck init
25
+ tsci init
26
26
 
27
- # Develop a Project
28
- tsck dev # build, view and edit circuit files in browser
27
+ # Develop a Project (preview, export, and edit circuit files in browser)
28
+ tsci dev
29
29
 
30
30
  # Manage Dependencies
31
- tsck install
32
- tsck add some-package
33
- tsck remove some-package
31
+ tsci install
32
+ tsci add some-package
33
+ tsci remove some-package
34
34
 
35
- # Lint a Project
36
- tsck lint
37
- tsck lint 2024 # use 2024 tscircuit recommendations
35
+ # Publish a Project
36
+ tsci publish
37
+ ```
38
38
 
39
- # Format a Project
40
- tsck format
41
- tsck format 2024
39
+ ## Developing
42
40
 
43
- # Publish a Project
44
- tsck publish
41
+ This project is developed with [bun](https://bun.sh/), make sure you have
42
+ that installed.
45
43
 
46
- # View Your Project on Registry
47
- tsck open
48
- tsck view
49
- ```
44
+ Run `bun boostrap` to install dependencies and `bun cli.ts` to run test the cli in development.
45
+
46
+ To run tests, run `bun test`
47
+
48
+ When you're developing the dev-server, you should do the following:
49
+
50
+ 1. Run the development server with `bun start:dev-server:dev`
51
+ 2. Upload examples using `bun dev-server upload --watch ./tests/assets/example-project`
52
+ 3. Visit `http://localhost:3020` in your browser
53
+
54
+ ## Features Coming Soon
55
+
56
+ - [`tsci format`](https://github.com/tscircuit/cli/issues/1)
57
+ - [`tsci lint`](https://github.com/tscircuit/cli/issues/2)
58
+ - [`tsci open`](https://github.com/tscircuit/cli/issues/4)
package/bun.lockb CHANGED
Binary file
Binary file
@@ -0,0 +1,34 @@
1
+ import kleur from "kleur"
2
+ import { AppContext } from "lib/util/app-context"
3
+ import { z } from "zod"
4
+ import { createOrModifyNpmrc } from "./init/create-or-modify-npmrc"
5
+ import $ from "dax-sh"
6
+
7
+ export const addCmd = async (ctx: AppContext, args: any) => {
8
+ const params = z
9
+ .object({
10
+ packages: z.array(z.string()),
11
+ flags: z
12
+ .object({ dev: z.boolean().optional().default(false) })
13
+ .optional()
14
+ .default({}),
15
+ })
16
+ .parse(args)
17
+
18
+ params.packages = params.packages.map((p) => p.replace(/^@/, ""))
19
+
20
+ await createOrModifyNpmrc({ quiet: true }, ctx)
21
+
22
+ $.cd(ctx.cwd)
23
+
24
+ const flagsString = params.flags.dev ? "--dev" : ""
25
+
26
+ const cmd = `npm add ${flagsString} ${params.packages
27
+ .map((p) => `@tsci/${p.replace(/\//, ".")}`)
28
+ .join(" ")}`
29
+ console.log(kleur.gray(`> ${cmd}`))
30
+
31
+ await $`npm add ${flagsString} ${params.packages
32
+ .map((p) => `@tsci/${p.replace(/\//, ".")}`)
33
+ .join(" ")}`
34
+ }
@@ -1,5 +1,5 @@
1
1
  import { AppContext } from "../util/app-context"
2
2
 
3
3
  export const configRevealLocation = async (ctx: AppContext, args: any) => {
4
- console.log(`tsck config path: ${ctx.global_config.path}`)
4
+ console.log(`tsci config path: ${ctx.global_config.path}`)
5
5
  }
@@ -0,0 +1,22 @@
1
+ import { AppContext } from "../../util/app-context"
2
+ import kleur from "kleur"
3
+ import * as Path from "path"
4
+ import { existsSync, readFileSync } from "fs"
5
+
6
+ export const checkIfInitialized = async (ctx: AppContext) => {
7
+ const packageJsonPath = Path.join(ctx.cwd, "package.json")
8
+ if (!existsSync(packageJsonPath)) {
9
+ console.error(kleur.red(`No package.json found`))
10
+ return false
11
+ }
12
+
13
+ const packageJsonRaw = readFileSync(packageJsonPath, "utf-8")
14
+ if (!packageJsonRaw.includes("tscircuit")) {
15
+ console.error(
16
+ kleur.red(`No tscircuit dependencies are installed in this project.`)
17
+ )
18
+ return false
19
+ }
20
+
21
+ return true
22
+ }
@@ -0,0 +1,54 @@
1
+ import apiServer from "../../../dev-server-api/dist/bundle"
2
+ import frontendVfs from "../../../dev-server-frontend/dist/bundle"
3
+ import EdgeRuntimePrimitives from "@edge-runtime/primitives"
4
+ import mime from "mime-types"
5
+
6
+ export const devServerRequestHandler = async (bunReq: Request) => {
7
+ const url = new URL(bunReq.url)
8
+ const requestType = url.pathname.startsWith("/api")
9
+ ? "api"
10
+ : url.pathname.startsWith("/preview")
11
+ ? "preview"
12
+ : "other"
13
+
14
+ if (requestType === "api") {
15
+ // We have to shim Bun's Request until they fix the issue where
16
+ // .clone() doesn't clone the body
17
+ // https://github.com/oven-sh/bun/pull/8668
18
+ const req = new EdgeRuntimePrimitives.Request(bunReq.url, {
19
+ headers: bunReq.headers,
20
+ method: bunReq.method,
21
+ body: bunReq.body,
22
+ })
23
+
24
+ const response = await apiServer.makeRequest(req, {})
25
+ return response
26
+ } else if (requestType === "preview") {
27
+ let frontendPath = url.pathname.replace("/preview", "")
28
+ if (frontendPath === "/" || frontendPath === "") {
29
+ frontendPath = "index.html"
30
+ }
31
+ frontendPath = frontendPath.replace(/^\//, "")
32
+
33
+ const fileContent: Buffer = (frontendVfs as any)[frontendPath]
34
+ if (!fileContent) {
35
+ return new Response("Not Found", { status: 404 })
36
+ }
37
+ return new Response(
38
+ // Buffer.from(fileContent.toString(), "base64").toString("utf-8"),
39
+ fileContent.toString("utf-8"),
40
+ {
41
+ headers: {
42
+ "Content-Type": mime.lookup(frontendPath) || "text/plain",
43
+ },
44
+ }
45
+ )
46
+ } else {
47
+ return new Response(null, {
48
+ status: 302,
49
+ headers: {
50
+ Location: "/preview",
51
+ },
52
+ })
53
+ }
54
+ }
@@ -0,0 +1,25 @@
1
+ import kleur from "kleur"
2
+ import defaultAxios from "axios"
3
+
4
+ export const getDevServerAxios = ({ serverUrl }: { serverUrl: string }) => {
5
+ const devServerAxios = defaultAxios.create({
6
+ baseURL: serverUrl,
7
+ })
8
+ devServerAxios.interceptors.response.use(
9
+ (res) => res,
10
+ (err) => {
11
+ console.log(
12
+ kleur.red(
13
+ `[ERR] ${err.response?.status} ${err.config.method?.toUpperCase()} ${
14
+ err.config.url
15
+ }\n\n${JSON.stringify(err.response?.data, null, " ")}`
16
+ .replace(/\\n/g, "\n")
17
+ .replace(/\\"/g, '"')
18
+ )
19
+ )
20
+ console.log(kleur.yellow("[Request Body]:"), err.config.data)
21
+ return Promise.reject(err)
22
+ }
23
+ )
24
+ return devServerAxios
25
+ }
@@ -1,16 +1,16 @@
1
1
  import { AppContext } from "../../util/app-context"
2
2
  import { z } from "zod"
3
3
  import kleur from "kleur"
4
- import { join as joinPath } from "path"
5
4
  import prompts from "prompts"
6
5
  import open from "open"
7
- import apiServer from "../../../dev-server-api/dist/bundle"
8
- import frontendVfs from "../../../dev-server-frontend/dist/bundle"
9
- import defaultAxios from "axios"
10
- import { readdirSync, readFileSync } from "fs"
11
- import { soupify } from "lib/soupify"
12
- import EdgeRuntimePrimitives from "@edge-runtime/primitives"
13
- import mime from "mime-types"
6
+ import { startDevServer } from "./start-dev-server"
7
+ import { getDevServerAxios } from "./get-dev-server-axios"
8
+ import { uploadExamplesFromDirectory } from "./upload-examples-from-directory"
9
+ import { unlink } from "fs/promises"
10
+ import * as Path from "path"
11
+ import { startWatcher } from "./start-watcher"
12
+ import { createOrModifyNpmrc } from "../init/create-or-modify-npmrc"
13
+ import { checkIfInitialized } from "./check-if-initialized"
14
14
 
15
15
  export const devCmd = async (ctx: AppContext, args: any) => {
16
16
  const params = z
@@ -22,28 +22,32 @@ export const devCmd = async (ctx: AppContext, args: any) => {
22
22
 
23
23
  const { cwd, port } = params
24
24
 
25
- // Load package.json, install tscircuit if it's not installed. If any other
26
- // tscircuit dependency like @tscircuit/builder is installed, then don't
27
- // install anything
28
- // TODO
25
+ // In the future we should automatically run "tsci init" if the directory
26
+ // isn't properly initialized, for now we're just going to do a spot check
27
+ const isInitialized = await checkIfInitialized(ctx)
29
28
 
30
- // Check that examples directory exists, if not create it and put inside
31
- // a sample file ExampleCircuit.ts
32
- // TODO
29
+ if (!isInitialized) {
30
+ console.log(
31
+ kleur.red(
32
+ `This project is not properly initialized. Please run "tsci init" first, or follow the manual installation steps here:\n\nhttps://github.com/tscircuit/tscircuit/blob/main/docs/manual-installation.md`
33
+ )
34
+ )
35
+ process.exit(1)
36
+ }
33
37
 
34
- // Create .tscircuit directory if it doesn't exist
38
+ await createOrModifyNpmrc({ quiet: false }, ctx)
39
+
40
+ // Load package.json, install tscircuit if it's not installed. If any other
41
+ // tscircuit dependency like @tscircuit/builder is installed, then don't
42
+ // install anything. If there is nothing in the current directory, ask to
43
+ // instead run "tsci init"
35
44
  // TODO
36
45
 
37
46
  // Add .tscircuit to .gitignore if it's not already there
38
47
  // TODO
39
48
 
40
- // Delete and recreate .tscircuit/dev-server.db
41
-
42
- // INSIDE BACKGROUND SERVICE
43
- // Soupify contents of examples directory, upload to sqlite db
44
- // TODO
45
- // Watch and re-soupify on file changes
46
- // TODO
49
+ // Delete old .tscircuit/dev-server.db
50
+ unlink(Path.join(cwd, ".tscircuit/dev-server.db")).catch(() => {})
47
51
 
48
52
  console.log(
49
53
  kleur.green(
@@ -51,144 +55,16 @@ export const devCmd = async (ctx: AppContext, args: any) => {
51
55
  )
52
56
  )
53
57
  const serverUrl = `http://localhost:${port}`
54
- const axios = defaultAxios.create({
55
- baseURL: serverUrl,
56
- })
57
- axios.interceptors.response.use(
58
- (res) => res,
59
- (err) => {
60
- console.log(
61
- kleur.red(
62
- `[ERR] ${err.response?.status} ${err.config.method?.toUpperCase()} ${
63
- err.config.url
64
- }\n\n${JSON.stringify(err.response?.data, null, " ")}`
65
- .replace(/\\n/g, "\n")
66
- .replace(/\\"/g, '"')
67
- )
68
- )
69
- console.log(kleur.yellow("[Request Body]:"), err.config.data)
70
- return Promise.reject(err)
71
- }
72
- )
73
-
74
- const requestHandler = async (bunReq: Request) => {
75
- const url = new URL(bunReq.url)
76
- const requestType = url.pathname.startsWith("/api")
77
- ? "api"
78
- : url.pathname.startsWith("/preview")
79
- ? "preview"
80
- : "other"
58
+ const devServerAxios = getDevServerAxios({ serverUrl })
81
59
 
82
- if (requestType === "api") {
83
- // We have to shim Bun's Request until they fix the issue where
84
- // .clone() doesn't clone the body
85
- // https://github.com/oven-sh/bun/pull/8668
86
- const req = new EdgeRuntimePrimitives.Request(bunReq.url, {
87
- headers: bunReq.headers,
88
- method: bunReq.method,
89
- body: bunReq.body,
90
- })
91
-
92
- const response = await apiServer.makeRequest(req, {})
93
- return response
94
- } else if (requestType === "preview") {
95
- let frontendPath = url.pathname.replace("/preview", "")
96
- if (frontendPath === "/" || frontendPath === "") {
97
- frontendPath = "index.html"
98
- }
99
- frontendPath = frontendPath.replace(/^\//, "")
100
-
101
- const fileContent: Buffer = (frontendVfs as any)[frontendPath]
102
- if (!fileContent) {
103
- return new Response("Not Found", { status: 404 })
104
- }
105
- return new Response(
106
- // Buffer.from(fileContent.toString(), "base64").toString("utf-8"),
107
- fileContent.toString("utf-8"),
108
- {
109
- headers: {
110
- "Content-Type": mime.lookup(frontendPath) || "text/plain",
111
- },
112
- }
113
- )
114
- } else {
115
- return new Response(null, {
116
- status: 302,
117
- headers: {
118
- Location: "/preview",
119
- },
120
- })
121
- }
122
- }
123
-
124
- let server: any
125
- if (typeof Bun !== "undefined") {
126
- server = Bun.serve({
127
- fetch: requestHandler,
128
- development: false,
129
- port,
130
- })
131
- } else {
132
- // Hono messes up the globals, only import it if we don't have Bun
133
- const { Hono } = await import("hono")
134
- const { serve } = await import("@hono/node-server")
135
- const honoApp = new Hono()
136
- honoApp.all("/*", (c) => requestHandler(c.req.raw))
137
- server = serve({
138
- fetch: honoApp.fetch,
139
- port,
140
- })
141
- }
142
-
143
- console.log("Running health check against dev server...")
144
- await axios.get("/api/health")
60
+ const server = await startDevServer({ port, devServerAxios })
145
61
 
146
62
  // Soupify all examples
147
63
  console.log(`Loading examples...`)
148
- const examplesDir = joinPath(cwd, "examples")
149
- const exampleFileNames = readdirSync(examplesDir)
150
- for (const exampleFileName of exampleFileNames) {
151
- try {
152
- const examplePath = joinPath(examplesDir, exampleFileName)
153
- const exampleContent = readFileSync(examplePath).toString()
64
+ await uploadExamplesFromDirectory({ devServerAxios, cwd })
154
65
 
155
- let exportName = "default"
156
-
157
- if (!exampleContent.includes("export default")) {
158
- // try to infer an export if possible
159
- const matches = Array.from(
160
- exampleContent.matchAll(
161
- /export\s+(const|function)\s+([A-Z]\w+)\s*=?/g
162
- )
163
- ).map((m) => m[2])
164
- if (matches.length === 0) {
165
- throw new Error(`No export detected in "${exampleFileName}"`)
166
- }
167
- if (matches.length > 1) {
168
- throw new Error(
169
- `Multiple exports detected in "${exampleFileName}", only single exports currently working`
170
- )
171
- }
172
- exportName = matches[0]
173
- }
174
-
175
- console.log("Soupifying...")
176
- const soup = await soupify({
177
- filePath: examplePath,
178
- exportName,
179
- })
180
- console.log(`Soupified ${exampleFileName}!`)
181
- console.log(`Uploading ${exampleFileName}...`)
182
- await axios.post("/api/dev_package_examples/create", {
183
- tscircuit_soup: soup,
184
- file_path: examplePath,
185
- export_name: exportName,
186
- })
187
- console.log(`Uploaded ${exampleFileName}!`)
188
- } catch (e: any) {
189
- console.log(kleur.red(e.toString()))
190
- }
191
- }
66
+ // Start watcher
67
+ const watcher = await startWatcher({ cwd, devServerAxios })
192
68
 
193
69
  while (true) {
194
70
  const { action } = await prompts({
@@ -211,6 +87,7 @@ export const devCmd = async (ctx: AppContext, args: any) => {
211
87
  } else if (!action || action === "stop") {
212
88
  if (server.stop) server.stop()
213
89
  if (server.close) server.close()
90
+ watcher.stop()
214
91
  break
215
92
  }
216
93
  }
@@ -0,0 +1,17 @@
1
+ export const inferExportNameFromSource = (sourceContent: string): string => {
2
+ if (sourceContent.includes("export default")) {
3
+ return "default"
4
+ }
5
+ const matches = Array.from(
6
+ sourceContent.matchAll(/export\s+(const|function)\s+([A-Z]\w+)\s*=?/g)
7
+ ).map((m) => m[2])
8
+ if (matches.length === 0) {
9
+ throw new Error(`No export detected in "${sourceContent}"`)
10
+ }
11
+ if (matches.length > 1) {
12
+ throw new Error(
13
+ `Multiple exports detected in "${sourceContent}", only single exports currently working`
14
+ )
15
+ }
16
+ return matches[0]
17
+ }
@@ -0,0 +1,38 @@
1
+ import kleur from "kleur"
2
+ import { join as joinPath } from "path"
3
+ import { AxiosInstance } from "axios"
4
+ import { readdirSync, readFileSync } from "fs"
5
+ import { soupify } from "lib/soupify"
6
+ import { inferExportNameFromSource } from "./infer-export-name-from-source"
7
+
8
+ export const soupifyAndUploadExampleFile = async ({
9
+ examplesDir,
10
+ exampleFileName,
11
+ devServerAxios,
12
+ }: {
13
+ examplesDir: string
14
+ exampleFileName: string
15
+ devServerAxios: AxiosInstance
16
+ }) => {
17
+ try {
18
+ const examplePath = joinPath(examplesDir, exampleFileName)
19
+ const exampleContent = readFileSync(examplePath).toString()
20
+
21
+ const exportName = inferExportNameFromSource(exampleContent)
22
+
23
+ console.log(kleur.gray(`[soupifying] ${exampleFileName}...`))
24
+ const soup = await soupify({
25
+ filePath: examplePath,
26
+ exportName,
27
+ })
28
+ console.log(kleur.gray(`[uploading ] ${exampleFileName}...`))
29
+ await devServerAxios.post("/api/dev_package_examples/create", {
30
+ tscircuit_soup: soup,
31
+ file_path: examplePath,
32
+ export_name: exportName,
33
+ })
34
+ console.log(kleur.gray(`[ done ] ${exampleFileName}!`))
35
+ } catch (e: any) {
36
+ console.log(kleur.red(e.toString()))
37
+ }
38
+ }
@@ -0,0 +1,34 @@
1
+ import { AxiosInstance } from "axios"
2
+ import { devServerRequestHandler } from "./dev-server-request-handler"
3
+
4
+ export const startDevServer = async ({
5
+ port,
6
+ devServerAxios,
7
+ }: {
8
+ port: number
9
+ devServerAxios: AxiosInstance
10
+ }) => {
11
+ let server: any
12
+ if (typeof Bun !== "undefined") {
13
+ server = Bun.serve({
14
+ fetch: devServerRequestHandler,
15
+ development: false,
16
+ port,
17
+ })
18
+ } else {
19
+ // Hono messes up the globals, only import it if we don't have Bun
20
+ const { Hono } = await import("hono")
21
+ const { serve } = await import("@hono/node-server")
22
+ const honoApp = new Hono()
23
+ honoApp.all("/*", (c) => devServerRequestHandler(c.req.raw))
24
+ server = serve({
25
+ fetch: honoApp.fetch,
26
+ port,
27
+ })
28
+ }
29
+
30
+ console.log("Running health check against dev server...")
31
+ await devServerAxios.get("/api/health")
32
+
33
+ return server
34
+ }
@@ -0,0 +1,48 @@
1
+ import { AxiosInstance } from "axios"
2
+ import chokidar from "chokidar"
3
+ import { uploadExamplesFromDirectory } from "./upload-examples-from-directory"
4
+ import kleur from "kleur"
5
+
6
+ export const startWatcher = async ({
7
+ cwd,
8
+ devServerAxios,
9
+ }: {
10
+ cwd: string
11
+ devServerAxios: AxiosInstance
12
+ }) => {
13
+ const watcher = chokidar.watch(`${cwd}/**/*.tsx`, {
14
+ ignored: /node_modules/,
15
+ persistent: true,
16
+ })
17
+
18
+ const upload_queue_state = {
19
+ dirty: false,
20
+ should_run: true,
21
+ }
22
+ watcher.on("change", async (path) => {
23
+ console.log(`File ${path} has been changed`)
24
+ // TODO analyze to determine which examples were impacted
25
+ upload_queue_state.dirty = true
26
+ })
27
+
28
+ async function uploadInBackground() {
29
+ while (upload_queue_state.should_run) {
30
+ if (upload_queue_state.dirty) {
31
+ console.log(kleur.yellow("Changes detected, re-uploading examples..."))
32
+ upload_queue_state.dirty = false
33
+ await uploadExamplesFromDirectory({ cwd, devServerAxios })
34
+ }
35
+ await new Promise((resolve) => setTimeout(resolve, 100))
36
+ }
37
+ }
38
+
39
+ const _backgroundUploaderPromise = uploadInBackground()
40
+
41
+ return {
42
+ _backgroundUploaderPromise,
43
+ stop: () => {
44
+ upload_queue_state.should_run = false
45
+ watcher.close()
46
+ },
47
+ }
48
+ }
@@ -0,0 +1,26 @@
1
+ import kleur from "kleur"
2
+ import { join as joinPath } from "path"
3
+ import { AxiosInstance } from "axios"
4
+ import { readdirSync, readFileSync } from "fs"
5
+ import { soupify } from "lib/soupify"
6
+ import { soupifyAndUploadExampleFile } from "./soupify-and-upload-example-file"
7
+
8
+ export const uploadExamplesFromDirectory = async ({
9
+ cwd,
10
+ devServerAxios,
11
+ }: {
12
+ cwd: string
13
+ devServerAxios: AxiosInstance
14
+ }) => {
15
+ const examplesDir = joinPath(cwd, "examples")
16
+ const exampleFileNames = readdirSync(examplesDir)
17
+ for (const exampleFileName of exampleFileNames) {
18
+ if (exampleFileName.endsWith(".__tmp_entrypoint.tsx")) continue
19
+ if (!exampleFileName.endsWith(".tsx")) continue
20
+ await soupifyAndUploadExampleFile({
21
+ devServerAxios,
22
+ examplesDir,
23
+ exampleFileName,
24
+ })
25
+ }
26
+ }
@@ -0,0 +1,26 @@
1
+ import { AppContext } from "../util/app-context"
2
+ import { z } from "zod"
3
+ import { getDevServerAxios } from "./get-dev-server-axios"
4
+ import { uploadExamplesFromDirectory } from "./upload-examples-from-directory"
5
+ import { startWatcher } from "./dev/start-watcher"
6
+
7
+ export const devServerUpload = async (ctx: AppContext, args: any) => {
8
+ const params = z
9
+ .object({
10
+ dir: z.string().optional().default(ctx.cwd),
11
+ port: z.coerce.number().optional().default(3020),
12
+ watch: z.boolean().optional().default(false),
13
+ })
14
+ .parse(args)
15
+
16
+ const serverUrl = `http://localhost:${params.port}`
17
+ const devServerAxios = getDevServerAxios({ serverUrl })
18
+
19
+ console.log(`Loading examples...`)
20
+ await uploadExamplesFromDirectory({ devServerAxios, cwd: params.dir })
21
+
22
+ if (params.watch) {
23
+ // Start watcher
24
+ const watcher = await startWatcher({ cwd: params.dir, devServerAxios })
25
+ }
26
+ }
@@ -26,3 +26,8 @@ export { publish } from "./publish"
26
26
  export { soupifyCmd as soupify } from "./soupify"
27
27
  export { devCmd as dev } from "./dev"
28
28
  export { initCmd as init } from "./init"
29
+ export { addCmd as add } from "./add"
30
+ export { removeCmd as remove } from "./remove"
31
+ export { installCmd as install } from "./install"
32
+ export { uninstallCmd as uninstall } from "./uninstall"
33
+ export { devServerUpload } from "./dev-server-upload"