@tscircuit/cli 0.0.5 → 0.0.8

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.
@@ -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
+ }
@@ -8,8 +8,10 @@ import { getDevServerAxios } from "./get-dev-server-axios"
8
8
  import { uploadExamplesFromDirectory } from "./upload-examples-from-directory"
9
9
  import { unlink } from "fs/promises"
10
10
  import * as Path from "path"
11
- import { appendFileSync } from "fs"
12
11
  import { startWatcher } from "./start-watcher"
12
+ import { createOrModifyNpmrc } from "../init/create-or-modify-npmrc"
13
+ import { checkIfInitialized } from "./check-if-initialized"
14
+ import { initCmd } from "../init"
13
15
 
14
16
  export const devCmd = async (ctx: AppContext, args: any) => {
15
17
  const params = z
@@ -21,17 +23,31 @@ export const devCmd = async (ctx: AppContext, args: any) => {
21
23
 
22
24
  const { cwd, port } = params
23
25
 
26
+ // In the future we should automatically run "tsci init" if the directory
27
+ // isn't properly initialized, for now we're just going to do a spot check
28
+ const isInitialized = await checkIfInitialized(ctx)
29
+
30
+ if (!isInitialized) {
31
+ const { confirmInitialize } = await prompts({
32
+ type: "confirm",
33
+ name: "confirmInitialize",
34
+ message: "Would you like to initialize this project now?",
35
+ initial: true,
36
+ })
37
+
38
+ if (confirmInitialize) {
39
+ return initCmd(ctx, {})
40
+ } else {
41
+ process.exit(1)
42
+ }
43
+ }
44
+
45
+ await createOrModifyNpmrc({ quiet: false }, ctx)
46
+
24
47
  // Load package.json, install tscircuit if it's not installed. If any other
25
48
  // tscircuit dependency like @tscircuit/builder is installed, then don't
26
49
  // install anything. If there is nothing in the current directory, ask to
27
- // instead run "tsck init"
28
- // TODO
29
-
30
- // Check that examples directory exists, if not create it and put inside
31
- // a sample file ExampleCircuit.ts
32
- // TODO
33
-
34
- // Create .tscircuit directory if it doesn't exist
50
+ // instead run "tsci init"
35
51
  // TODO
36
52
 
37
53
  // Add .tscircuit to .gitignore if it's not already there
@@ -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
+ }
@@ -3,6 +3,7 @@ import { join as joinPath } from "path"
3
3
  import { AxiosInstance } from "axios"
4
4
  import { readdirSync, readFileSync } from "fs"
5
5
  import { soupify } from "lib/soupify"
6
+ import { inferExportNameFromSource } from "./infer-export-name-from-source"
6
7
 
7
8
  export const soupifyAndUploadExampleFile = async ({
8
9
  examplesDir,
@@ -17,23 +18,7 @@ export const soupifyAndUploadExampleFile = async ({
17
18
  const examplePath = joinPath(examplesDir, exampleFileName)
18
19
  const exampleContent = readFileSync(examplePath).toString()
19
20
 
20
- let exportName = "default"
21
-
22
- if (!exampleContent.includes("export default")) {
23
- // try to infer an export if possible
24
- const matches = Array.from(
25
- exampleContent.matchAll(/export\s+(const|function)\s+([A-Z]\w+)\s*=?/g)
26
- ).map((m) => m[2])
27
- if (matches.length === 0) {
28
- throw new Error(`No export detected in "${exampleFileName}"`)
29
- }
30
- if (matches.length > 1) {
31
- throw new Error(
32
- `Multiple exports detected in "${exampleFileName}", only single exports currently working`
33
- )
34
- }
35
- exportName = matches[0]
36
- }
21
+ const exportName = inferExportNameFromSource(exampleContent)
37
22
 
38
23
  console.log(kleur.gray(`[soupifying] ${exampleFileName}...`))
39
24
  const soup = await soupify({
@@ -0,0 +1,26 @@
1
+ import { AppContext } from "../util/app-context"
2
+ import { z } from "zod"
3
+ import { getDevServerAxios } from "./dev/get-dev-server-axios"
4
+ import { uploadExamplesFromDirectory } from "./dev/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"
@@ -0,0 +1,21 @@
1
+ import { existsSync, writeFileSync } from "fs"
2
+ import kleur from "kleur"
3
+ import * as Path from "path"
4
+ import { getGeneratedNpmrc } from "./get-generated-npmrc"
5
+ import { AppContext } from "lib/util/app-context"
6
+
7
+ export const createOrModifyNpmrc = async (
8
+ { quiet = true }: { quiet?: boolean },
9
+ ctx: AppContext
10
+ ) => {
11
+ const npmrcPath = Path.join(ctx.cwd, ".npmrc")
12
+
13
+ if (existsSync(npmrcPath)) {
14
+ // TODO check that @tsci registry is correctly set
15
+ if (!quiet) {
16
+ console.log(kleur.yellow("npmrc already exists, not doing anything"))
17
+ }
18
+ } else {
19
+ writeFileSync(npmrcPath, getGeneratedNpmrc(ctx))
20
+ }
21
+ }
@@ -0,0 +1,8 @@
1
+ import { AppContext } from "lib/util/app-context"
2
+
3
+ export const getGeneratedNpmrc = (ctx: AppContext) =>
4
+ `
5
+
6
+ @tsci:registry=${ctx.registry_url}/npm
7
+
8
+ `.trim()
@@ -0,0 +1,34 @@
1
+ export const getGeneratedReadme = ({
2
+ name,
3
+ shouldHaveProjectGeneratedNotice = false,
4
+ }: {
5
+ name: string
6
+ shouldHaveProjectGeneratedNotice?: boolean
7
+ }) => {
8
+ return `
9
+ # ${name}
10
+ ${
11
+ shouldHaveProjectGeneratedNotice
12
+ ? `\n\n> This project was generated using [tsci](https://github.com/tscircuit/tscircuit)\n`
13
+ : ""
14
+ }
15
+ To develop and view the examples, run \`tsci dev\` and open [http://localhost:3020](http://localhost:3020) in your browser.
16
+
17
+ ## Developing
18
+
19
+ You should install an editor like [VS Code](https://code.visualstudio.com/) with a typescript extension. Many web developers already have this.
20
+
21
+ Usually, you'll want to develop some named circuits inside of the \`lib\` directory,
22
+ export them in the \`index.ts\` file, then show how to use them in the \`examples\` directory.
23
+
24
+ Any file in \`examples\` will be automatically loaded and appear in the browser preview when you run \`tsci dev\`. It auto-reloads when you make changes, no need to reload the page or re-run the command.
25
+
26
+ > [!TIP] Make sure to replace this README with some details about your project and how to use it.
27
+
28
+ ## Publishing
29
+
30
+ After you're satisfied with your project, publish it on the [tscircuit registry](https://registry.tscircuit.com) with \`tsci publish\`
31
+
32
+
33
+ `.trim()
34
+ }
@@ -0,0 +1,34 @@
1
+ export const getGeneratedTsconfig = () =>
2
+ `
3
+
4
+ {
5
+ "compilerOptions": {
6
+ // Enable latest features
7
+ "lib": ["ESNext"],
8
+ "target": "ESNext",
9
+ "module": "ESNext",
10
+ "moduleDetection": "force",
11
+ "jsx": "react-jsx",
12
+ "allowJs": true,
13
+ "types": ["@tscircuit/react-fiber"],
14
+ "baseUrl": ".",
15
+
16
+ // Bundler mode
17
+ "moduleResolution": "bundler",
18
+ "allowImportingTsExtensions": true,
19
+ "verbatimModuleSyntax": true,
20
+ "noEmit": true,
21
+
22
+ // Best practices
23
+ "strict": true,
24
+ "skipLibCheck": true,
25
+ "noFallthroughCasesInSwitch": true,
26
+
27
+ // Some stricter flags (disabled by default)
28
+ "noUnusedLocals": false,
29
+ "noUnusedParameters": false,
30
+ "noPropertyAccessFromIndexSignature": false
31
+ }
32
+ }
33
+
34
+ `.trim()
@@ -1,4 +1,4 @@
1
- import { AppContext } from "../util/app-context"
1
+ import { AppContext } from "../../util/app-context"
2
2
  import { z } from "zod"
3
3
  import {
4
4
  readFileSync,
@@ -10,6 +10,10 @@ import {
10
10
  } from "fs"
11
11
  import * as Path from "node:path"
12
12
  import $ from "dax-sh"
13
+ import { getGeneratedReadme } from "./get-generated-readme"
14
+ import { getGeneratedTsconfig } from "./get-generated-tsconfig"
15
+ import { getGeneratedNpmrc } from "./get-generated-npmrc"
16
+ import { createOrModifyNpmrc } from "./create-or-modify-npmrc"
13
17
 
14
18
  export const initCmd = async (ctx: AppContext, args: any) => {
15
19
  const params = z
@@ -26,6 +30,17 @@ export const initCmd = async (ctx: AppContext, args: any) => {
26
30
  params.dir = `.`
27
31
  }
28
32
 
33
+ if (!params.name) {
34
+ try {
35
+ const myAccount = await ctx.axios
36
+ .get("/accounts/get")
37
+ .then((r) => r.data.account)
38
+ params.name = `@${myAccount.github_username}/${Path.basename(params.dir)}`
39
+ } catch (e) {
40
+ params.name = Path.basename(params.dir ?? ctx.cwd)
41
+ }
42
+ }
43
+
29
44
  let runtime = params.runtime
30
45
  if (!runtime) {
31
46
  const bunExists = $.commandExistsSync("bun")
@@ -69,11 +84,14 @@ export const initCmd = async (ctx: AppContext, args: any) => {
69
84
  writeFileSync("package.json", JSON.stringify(packageJson, null, 2))
70
85
 
71
86
  console.log(`Adding ".tscircuit" to .gitignore`)
72
- appendFileSync(".gitignore", "\n.tscircuit\n*.__tmp_entrypoint.tsx", {
87
+ appendFileSync(".gitignore", "\n.tscircuit\n*.__tmp_entrypoint.tsx\ndist", {
73
88
  encoding: "utf-8",
74
89
  flag: "a+",
75
90
  })
76
91
 
92
+ console.log("Add .npmrc with tscircuit registry...")
93
+ await createOrModifyNpmrc({ quiet: false }, ctx)
94
+
77
95
  console.log("Creating lib and examples directories...")
78
96
  mkdirSync("examples", { recursive: true })
79
97
  mkdirSync("lib", { recursive: true })
@@ -107,69 +125,14 @@ export const MyExample = () => (
107
125
  // Override the README file
108
126
  writeFileSync(
109
127
  "README.md",
110
- `
111
- # ${params.name}
112
-
113
- > This project was generated using [tsck](https://github.com/tscircuit/tscircuit)
114
-
115
- To develop and view the examples, run \`tsck dev\` and open [http://localhost:3020](http://localhost:3020) in your browser.
116
-
117
- ## Developing
118
-
119
- You should install an editor like [VS Code](https://code.visualstudio.com/) with a typescript extension. Many web developers already have this.
120
-
121
- Usually, you'll want to develop some named circuits inside of the \`lib\` directory,
122
- export them in the \`index.ts\` file, then show how to use them in the \`examples\` directory.
123
-
124
- Any file in \`examples\` will be automatically loaded and appear in the browser preview when you run \`tsck dev\`. It auto-reloads when you make changes, no need to reload the page or re-run the command.
125
-
126
- > [!TIP] Make sure to replace this README with some details about your project and how to use it.
127
-
128
- ## Publishing
129
-
130
- After you're satisfied with your project, publish it on the [tscircuit registry](https://registry.tscircuit.com) with \`tsck publish\`
131
-
132
-
133
-
134
- `.trim()
128
+ getGeneratedReadme({
129
+ name: params.name!,
130
+ shouldHaveProjectGeneratedNotice: true,
131
+ })
135
132
  )
136
133
 
137
134
  // Open tsconfig.json and modify it to import the tscircuit types
138
- writeFileSync(
139
- "tsconfig.json",
140
- `
141
- {
142
- "compilerOptions": {
143
- // Enable latest features
144
- "lib": ["ESNext"],
145
- "target": "ESNext",
146
- "module": "ESNext",
147
- "moduleDetection": "force",
148
- "jsx": "react-jsx",
149
- "allowJs": true,
150
- "types": ["@tscircuit/react-fiber"],
151
- "baseUrl": ".",
152
-
153
- // Bundler mode
154
- "moduleResolution": "bundler",
155
- "allowImportingTsExtensions": true,
156
- "verbatimModuleSyntax": true,
157
- "noEmit": true,
158
-
159
- // Best practices
160
- "strict": true,
161
- "skipLibCheck": true,
162
- "noFallthroughCasesInSwitch": true,
163
-
164
- // Some stricter flags (disabled by default)
165
- "noUnusedLocals": false,
166
- "noUnusedParameters": false,
167
- "noPropertyAccessFromIndexSignature": false
168
- }
169
- }
170
-
171
- `.trim()
172
- )
135
+ writeFileSync("tsconfig.json", getGeneratedTsconfig())
173
136
 
174
- console.log("Done! Run `tsck dev` to start developing!")
137
+ console.log("Done! Run `tsci dev` to start developing!")
175
138
  }
@@ -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 installCmd = 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 install ${flagsString} ${params.packages
27
+ .map((p) => `@tsci/${p.replace(/\//, ".")}`)
28
+ .join(" ")}`
29
+ console.log(kleur.gray(`> ${cmd}`))
30
+
31
+ await $`npm install ${flagsString} ${params.packages
32
+ .map((p) => `@tsci/${p.replace(/\//, ".")}`)
33
+ .join(" ")}`
34
+ }