@tscircuit/cli 0.1.26 → 0.1.28
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/.github/workflows/bun-test.yml +25 -0
- package/cli/clone/register.ts +65 -138
- package/cli/init/register.ts +43 -109
- package/dist/main.js +224 -221
- package/lib/shared/detect-pkg-manager.ts +15 -0
- package/lib/shared/generate-ts-config.ts +34 -0
- package/lib/shared/setup-tsci-packages.ts +68 -0
- package/lib/shared/write-file-if-not-exists.ts +10 -0
- package/package.json +1 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Created using @tscircuit/plop (npm install -g @tscircuit/plop)
|
|
2
|
+
name: Bun Test
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
timeout-minutes: 5
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout code
|
|
14
|
+
uses: actions/checkout@v2
|
|
15
|
+
|
|
16
|
+
- name: Setup bun
|
|
17
|
+
uses: oven-sh/setup-bun@v1
|
|
18
|
+
with:
|
|
19
|
+
bun-version: latest
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: bun install
|
|
23
|
+
|
|
24
|
+
- name: Run tests
|
|
25
|
+
run: bun test
|
package/cli/clone/register.ts
CHANGED
|
@@ -2,53 +2,8 @@ import type { Command } from "commander"
|
|
|
2
2
|
import { getKy } from "lib/registry-api/get-ky"
|
|
3
3
|
import * as fs from "node:fs"
|
|
4
4
|
import * as path from "node:path"
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
// Detect the package manager being used in the project
|
|
8
|
-
const detectPackageManager = (): string => {
|
|
9
|
-
const userAgent = process.env.npm_config_user_agent || ""
|
|
10
|
-
if (userAgent.startsWith("yarn")) return "yarn"
|
|
11
|
-
if (userAgent.startsWith("pnpm")) return "pnpm"
|
|
12
|
-
if (userAgent.startsWith("bun")) return "bun"
|
|
13
|
-
|
|
14
|
-
if (fs.existsSync("yarn.lock")) return "yarn"
|
|
15
|
-
if (fs.existsSync("pnpm-lock.yaml")) return "pnpm"
|
|
16
|
-
if (fs.existsSync("bun.lockb")) return "bun"
|
|
17
|
-
|
|
18
|
-
return "npm" // Default to npm
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Generate a React-compatible tsconfig.json
|
|
22
|
-
const generateTsConfig = (dir: string) => {
|
|
23
|
-
const tsconfigPath = path.join(dir, "tsconfig.json")
|
|
24
|
-
const tsconfigContent = JSON.stringify(
|
|
25
|
-
{
|
|
26
|
-
compilerOptions: {
|
|
27
|
-
target: "ES6",
|
|
28
|
-
module: "ESNext",
|
|
29
|
-
jsx: "react-jsx",
|
|
30
|
-
outDir: "dist",
|
|
31
|
-
strict: true,
|
|
32
|
-
esModuleInterop: true,
|
|
33
|
-
moduleResolution: "node",
|
|
34
|
-
skipLibCheck: true,
|
|
35
|
-
forceConsistentCasingInFileNames: true,
|
|
36
|
-
resolveJsonModule: true,
|
|
37
|
-
sourceMap: true,
|
|
38
|
-
allowSyntheticDefaultImports: true,
|
|
39
|
-
experimentalDecorators: true,
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
null,
|
|
43
|
-
2,
|
|
44
|
-
)
|
|
45
|
-
if (!fs.existsSync(tsconfigPath)) {
|
|
46
|
-
fs.writeFileSync(tsconfigPath, tsconfigContent.trimStart())
|
|
47
|
-
console.log(`Created: ${tsconfigPath}`)
|
|
48
|
-
} else {
|
|
49
|
-
console.log(`Skipped: ${tsconfigPath} already exists`)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
5
|
+
import { setupTsciProject } from "lib/shared/setup-tsci-packages"
|
|
6
|
+
import { generateTsConfig } from "lib/shared/generate-ts-config"
|
|
52
7
|
|
|
53
8
|
export const registerClone = (program: Command) => {
|
|
54
9
|
program
|
|
@@ -56,120 +11,92 @@ export const registerClone = (program: Command) => {
|
|
|
56
11
|
.description("Clone a snippet from the registry")
|
|
57
12
|
.argument("<snippet>", "Snippet to clone (e.g. author/snippetName)")
|
|
58
13
|
.action(async (snippetPath: string) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (!snippetPath.startsWith("@tsci/") && snippetPath.includes("/")) {
|
|
62
|
-
;[author, snippetName] = snippetPath.split("/")
|
|
63
|
-
} else {
|
|
64
|
-
const trimmedPath = snippetPath.replace("@tsci/", "")
|
|
65
|
-
const firstDotIndex = trimmedPath.indexOf(".")
|
|
66
|
-
author = trimmedPath.slice(0, firstDotIndex)
|
|
67
|
-
snippetName = trimmedPath.slice(firstDotIndex + 1)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (!author || !snippetName) {
|
|
14
|
+
const match = snippetPath.match(/^(?:@tsci\/)?([^/.]+)[/.](.+)$/)
|
|
15
|
+
if (!match) {
|
|
71
16
|
console.error(
|
|
72
|
-
"Invalid snippet path. Use format: author/snippetName, author.snippetName or @tsci/author.snippetName",
|
|
17
|
+
"Invalid snippet path. Use format: author/snippetName, author.snippetName, or @tsci/author.snippetName",
|
|
73
18
|
)
|
|
74
19
|
process.exit(1)
|
|
75
20
|
}
|
|
76
21
|
|
|
77
|
-
const
|
|
22
|
+
const [, author, snippetName] = match
|
|
23
|
+
console.log(`Cloning ${author}/${snippetName}...`)
|
|
78
24
|
|
|
25
|
+
const ky = getKy()
|
|
26
|
+
let packageFileList
|
|
79
27
|
try {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
created_at: string
|
|
89
|
-
}>
|
|
90
|
-
}>("package_files/list", {
|
|
91
|
-
json: {
|
|
92
|
-
package_name: `${author}/${snippetName}`,
|
|
93
|
-
use_latest_version: true,
|
|
28
|
+
packageFileList = await ky
|
|
29
|
+
.post<{ package_files: Array<{ file_path: string }> }>(
|
|
30
|
+
"package_files/list",
|
|
31
|
+
{
|
|
32
|
+
json: {
|
|
33
|
+
package_name: `${author}/${snippetName}`,
|
|
34
|
+
use_latest_version: true,
|
|
35
|
+
},
|
|
94
36
|
},
|
|
95
|
-
|
|
37
|
+
)
|
|
96
38
|
.json()
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error(
|
|
41
|
+
"Failed to fetch package files:",
|
|
42
|
+
error instanceof Error ? error.message : error,
|
|
43
|
+
)
|
|
44
|
+
process.exit(1)
|
|
45
|
+
}
|
|
97
46
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (!fs.existsSync(dirPath)) {
|
|
101
|
-
fs.mkdirSync(dirPath)
|
|
102
|
-
}
|
|
47
|
+
const dirPath = path.resolve(`${author}.${snippetName}`)
|
|
48
|
+
fs.mkdirSync(dirPath, { recursive: true })
|
|
103
49
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
? fileInfo.file_path.slice(1)
|
|
108
|
-
: fileInfo.file_path
|
|
50
|
+
for (const fileInfo of packageFileList.package_files) {
|
|
51
|
+
const filePath = fileInfo.file_path.replace(/^\/|dist\//g, "")
|
|
52
|
+
if (!filePath) continue
|
|
109
53
|
|
|
110
|
-
|
|
54
|
+
const fullPath = path.join(dirPath, filePath)
|
|
55
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true })
|
|
111
56
|
|
|
57
|
+
try {
|
|
112
58
|
const fileContent = await ky
|
|
113
|
-
.post<{
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
file_path: fileInfo.file_path,
|
|
59
|
+
.post<{ package_file: { content_text: string } }>(
|
|
60
|
+
"package_files/get",
|
|
61
|
+
{
|
|
62
|
+
json: {
|
|
63
|
+
package_name: `${author}/${snippetName}`,
|
|
64
|
+
file_path: fileInfo.file_path,
|
|
65
|
+
},
|
|
121
66
|
},
|
|
122
|
-
|
|
67
|
+
)
|
|
123
68
|
.json()
|
|
124
69
|
|
|
125
|
-
|
|
126
|
-
const dirName = path.dirname(fullPath)
|
|
70
|
+
let fileText = fileContent.package_file.content_text
|
|
127
71
|
|
|
128
|
-
//
|
|
129
|
-
if (
|
|
130
|
-
|
|
72
|
+
// Ensure all .tsx files contain "import '@tscircuit/core';"
|
|
73
|
+
if (
|
|
74
|
+
filePath.endsWith(".tsx") &&
|
|
75
|
+
!fileText.includes("@tscircuit/core")
|
|
76
|
+
) {
|
|
77
|
+
fileText = `import "@tscircuit/core";\n\n${fileText}`
|
|
131
78
|
}
|
|
132
79
|
|
|
133
|
-
fs.writeFileSync(fullPath,
|
|
80
|
+
fs.writeFileSync(fullPath, fileText)
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.warn(
|
|
83
|
+
`Skipping ${filePath} due to error:`,
|
|
84
|
+
error instanceof Error ? error.message : error,
|
|
85
|
+
)
|
|
134
86
|
}
|
|
87
|
+
}
|
|
135
88
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
generateTsConfig(dirPath)
|
|
141
|
-
|
|
142
|
-
// Detect package manager and install dependencies
|
|
143
|
-
const packageManager = detectPackageManager()
|
|
144
|
-
console.log(`Detected package manager: ${packageManager}`)
|
|
89
|
+
fs.writeFileSync(
|
|
90
|
+
path.join(dirPath, ".npmrc"),
|
|
91
|
+
"@tsci:registry=https://npm.tscircuit.com",
|
|
92
|
+
)
|
|
145
93
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
try {
|
|
149
|
-
console.log("Installing dependencies...")
|
|
150
|
-
const installCommand =
|
|
151
|
-
packageManager === "yarn"
|
|
152
|
-
? `cd ${dirPath} && yarn add -D ${dependencies}`
|
|
153
|
-
: packageManager === "pnpm"
|
|
154
|
-
? `cd ${dirPath} && pnpm add -D ${dependencies}`
|
|
155
|
-
: packageManager === "bun"
|
|
156
|
-
? `cd ${dirPath} && bun add -D ${dependencies}`
|
|
157
|
-
: `cd ${dirPath} && npm install -D ${dependencies}`
|
|
158
|
-
execSync(installCommand, { stdio: "inherit" })
|
|
159
|
-
console.log("Dependencies installed successfully.")
|
|
160
|
-
} catch (error) {
|
|
161
|
-
console.error("Failed to install dependencies:", error)
|
|
162
|
-
}
|
|
94
|
+
generateTsConfig(dirPath)
|
|
95
|
+
setupTsciProject(dirPath)
|
|
163
96
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
console.error("Failed to clone snippet:", error.message)
|
|
169
|
-
} else {
|
|
170
|
-
console.error("Failed to clone snippet:", error)
|
|
171
|
-
}
|
|
172
|
-
process.exit(1)
|
|
173
|
-
}
|
|
97
|
+
console.log(`Successfully cloned to ${dirPath}/`)
|
|
98
|
+
console.log(
|
|
99
|
+
`Run "cd ${path.dirname(dirPath)} && tsci dev" to start developing.`,
|
|
100
|
+
)
|
|
174
101
|
})
|
|
175
102
|
}
|
package/cli/init/register.ts
CHANGED
|
@@ -1,136 +1,70 @@
|
|
|
1
1
|
import type { Command } from "commander"
|
|
2
2
|
import * as fs from "node:fs"
|
|
3
3
|
import * as path from "node:path"
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const detectPackageManager = (): string => {
|
|
8
|
-
const userAgent = process.env.npm_config_user_agent || ""
|
|
9
|
-
if (userAgent.startsWith("yarn")) return "yarn"
|
|
10
|
-
if (userAgent.startsWith("pnpm")) return "pnpm"
|
|
11
|
-
if (userAgent.startsWith("bun")) return "bun"
|
|
12
|
-
|
|
13
|
-
if (fs.existsSync("yarn.lock")) return "yarn"
|
|
14
|
-
if (fs.existsSync("pnpm-lock.yaml")) return "pnpm"
|
|
15
|
-
if (fs.existsSync("bun.lockb")) return "bun"
|
|
16
|
-
|
|
17
|
-
return "npm" // Default to npm
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Generate a React-compatible tsconfig.json
|
|
21
|
-
const generateTsConfig = (dir: string) => {
|
|
22
|
-
const tsconfigPath = path.join(dir, "tsconfig.json")
|
|
23
|
-
const tsconfigContent = JSON.stringify(
|
|
24
|
-
{
|
|
25
|
-
compilerOptions: {
|
|
26
|
-
target: "ES6",
|
|
27
|
-
module: "ESNext",
|
|
28
|
-
jsx: "react-jsx",
|
|
29
|
-
outDir: "dist",
|
|
30
|
-
strict: true,
|
|
31
|
-
esModuleInterop: true,
|
|
32
|
-
moduleResolution: "node",
|
|
33
|
-
skipLibCheck: true,
|
|
34
|
-
forceConsistentCasingInFileNames: true,
|
|
35
|
-
resolveJsonModule: true,
|
|
36
|
-
sourceMap: true,
|
|
37
|
-
allowSyntheticDefaultImports: true,
|
|
38
|
-
experimentalDecorators: true,
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
null,
|
|
42
|
-
2,
|
|
43
|
-
)
|
|
44
|
-
if (!fs.existsSync(tsconfigPath)) {
|
|
45
|
-
fs.writeFileSync(tsconfigPath, tsconfigContent.trimStart())
|
|
46
|
-
console.log(`Created: ${tsconfigPath}`)
|
|
47
|
-
} else {
|
|
48
|
-
console.log(`Skipped: ${tsconfigPath} already exists`)
|
|
49
|
-
}
|
|
50
|
-
}
|
|
4
|
+
import { setupTsciProject } from "lib/shared/setup-tsci-packages"
|
|
5
|
+
import { generateTsConfig } from "lib/shared/generate-ts-config"
|
|
6
|
+
import { writeFileIfNotExists } from "lib/shared/write-file-if-not-exists"
|
|
51
7
|
|
|
52
8
|
export const registerInit = (program: Command) => {
|
|
53
9
|
program
|
|
54
10
|
.command("init")
|
|
55
|
-
.description(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
11
|
+
.description(
|
|
12
|
+
"Initialize a new TSCircuit project in the specified directory (or current directory if none is provided)",
|
|
13
|
+
)
|
|
14
|
+
.argument(
|
|
15
|
+
"[directory]",
|
|
16
|
+
"Directory name (optional, defaults to current directory)",
|
|
17
|
+
)
|
|
18
|
+
.action((directory?: string) => {
|
|
19
|
+
const projectDir = directory
|
|
20
|
+
? path.resolve(process.cwd(), directory)
|
|
21
|
+
: process.cwd()
|
|
22
|
+
|
|
23
|
+
// Ensure the directory exists
|
|
24
|
+
fs.mkdirSync(projectDir, { recursive: true })
|
|
60
25
|
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
26
|
+
// Create essential project files
|
|
27
|
+
writeFileIfNotExists(
|
|
28
|
+
path.join(projectDir, "index.tsx"),
|
|
29
|
+
`
|
|
30
|
+
import "@tscircuit/core";
|
|
64
31
|
|
|
65
32
|
export default () => (
|
|
66
33
|
<board width="10mm" height="10mm">
|
|
67
|
-
<resistor
|
|
68
|
-
|
|
69
|
-
footprint="0402"
|
|
70
|
-
name="R1"
|
|
71
|
-
schX={3}
|
|
72
|
-
pcbX={3}
|
|
73
|
-
/>
|
|
74
|
-
<capacitor
|
|
75
|
-
capacitance="1000pF"
|
|
76
|
-
footprint="0402"
|
|
77
|
-
name="C1"
|
|
78
|
-
schX={-3}
|
|
79
|
-
pcbX={-3}
|
|
80
|
-
/>
|
|
34
|
+
<resistor resistance="1k" footprint="0402" name="R1" schX={3} pcbX={3} />
|
|
35
|
+
<capacitor capacitance="1000pF" footprint="0402" name="C1" schX={-3} pcbX={-3} />
|
|
81
36
|
<trace from=".R1 > .pin1" to=".C1 > .pin1" />
|
|
82
37
|
</board>
|
|
83
38
|
);
|
|
84
|
-
|
|
39
|
+
`,
|
|
40
|
+
)
|
|
85
41
|
|
|
86
|
-
|
|
87
|
-
|
|
42
|
+
writeFileIfNotExists(
|
|
43
|
+
path.join(projectDir, ".npmrc"),
|
|
44
|
+
`
|
|
88
45
|
@tsci:registry=https://npm.tscircuit.com
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
// Create index.tsx if it doesn't exist
|
|
92
|
-
if (!fs.existsSync(indexFilePath)) {
|
|
93
|
-
fs.writeFileSync(indexFilePath, indexContent.trimStart())
|
|
94
|
-
console.log(`Created: ${indexFilePath}`)
|
|
95
|
-
} else {
|
|
96
|
-
console.log(`Skipped: ${indexFilePath} already exists`)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Create .npmrc if it doesn't exist
|
|
100
|
-
if (!fs.existsSync(npmrcFilePath)) {
|
|
101
|
-
fs.writeFileSync(npmrcFilePath, npmrcContent.trimStart())
|
|
102
|
-
console.log(`Created: ${npmrcFilePath}`)
|
|
103
|
-
} else {
|
|
104
|
-
console.log(`Skipped: ${npmrcFilePath} already exists`)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Detect the package manager
|
|
108
|
-
const packageManager = detectPackageManager()
|
|
109
|
-
console.log(`Detected package manager: ${packageManager}`)
|
|
46
|
+
`,
|
|
47
|
+
)
|
|
110
48
|
|
|
111
|
-
//
|
|
112
|
-
const dependencies = "@types/react @tscircuit/core"
|
|
49
|
+
// Setup project dependencies
|
|
113
50
|
try {
|
|
114
|
-
|
|
115
|
-
const installCommand =
|
|
116
|
-
packageManager === "yarn"
|
|
117
|
-
? `yarn add -D ${dependencies}`
|
|
118
|
-
: packageManager === "pnpm"
|
|
119
|
-
? `pnpm add -D ${dependencies}`
|
|
120
|
-
: packageManager === "bun"
|
|
121
|
-
? `bun add -D ${dependencies}`
|
|
122
|
-
: `npm install -D ${dependencies}`
|
|
123
|
-
execSync(installCommand, { stdio: "inherit" })
|
|
124
|
-
console.log("Dependencies installed successfully.")
|
|
51
|
+
setupTsciProject(projectDir)
|
|
125
52
|
} catch (error) {
|
|
126
53
|
console.error("Failed to install dependencies:", error)
|
|
54
|
+
process.exit(1)
|
|
127
55
|
}
|
|
128
56
|
|
|
129
57
|
// Generate tsconfig.json
|
|
130
|
-
|
|
58
|
+
try {
|
|
59
|
+
generateTsConfig(projectDir)
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error("Failed to generate tsconfig.json:", error)
|
|
62
|
+
process.exit(1)
|
|
63
|
+
}
|
|
131
64
|
|
|
132
|
-
console.
|
|
133
|
-
|
|
65
|
+
console.info(
|
|
66
|
+
`🎉 Initialization complete! Run ${directory ? `"cd ${directory}" & ` : ""}"tsci dev" to start developing.`,
|
|
134
67
|
)
|
|
68
|
+
process.exit(0)
|
|
135
69
|
})
|
|
136
70
|
}
|