Skip to content

Commit

Permalink
Merge pull request #13 from tscircuit/clone
Browse files Browse the repository at this point in the history
Create clone command for quicklyl downloading snippets
  • Loading branch information
seveibar authored Jan 6, 2025
2 parents daf4b62 + 7e8daa3 commit 18de102
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 5 deletions.
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
# @tscircuit/snippets-cli
# tscircuit command line interface (CLI)

A CLI for developing, managing and publishing snippets on [tscircuit.com](https://tscircuit.com).
A CLI for developing, managing and publishing tscircuit code (the "npm for tscircuit")

## Usage

```bash
# Start a local server that watches for changes in
# this file or it's dependencies and updates the
# browser preview
snippets dev ./path/to/file.tsx
tsci dev ./path/to/file.tsx

# Clone a snippet from the registry
tsci clone author/snippetName
```

> Note: The snippets CLI uses the same configuration files as the [@tscircuit/cli](https://github.com/tscircuit/cli), so you may need to also install `npm install -g @tscircuit/cli` and run `tsci login` to authenticate!
## Installation

```bash
npm install -g @tscircuit/snippets-cli
npm install -g @tscircuit/cli
```

## Development
Expand Down Expand Up @@ -47,5 +50,5 @@ runframe each time you'd like to load a new version of runframe.
```bash
export RUNFRAME_STANDALONE_FILE_PATH=../runframe/dist/standalone.min.js
cd ../runframe && bun run build
cd ../snippets-cli && bun run dev
cd ../cli && bun run dev
```
99 changes: 99 additions & 0 deletions cli/clone/register.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import type { Command } from "commander"
import { getKy } from "lib/registry-api/get-ky"
import * as fs from "node:fs"
import * as path from "node:path"

export const registerClone = (program: Command) => {
program
.command("clone")
.description("Clone a snippet from the registry")
.argument("<snippet>", "Snippet to clone (e.g. author/snippetName)")
.action(async (snippetPath: string) => {
let author: string
let snippetName: string
if (!snippetPath.startsWith("@tsci/") && snippetPath.includes("/")) {
;[author, snippetName] = snippetPath.split("/")
} else {
const trimmedPath = snippetPath.replace("@tsci/", "")
const firstDotIndex = trimmedPath.indexOf(".")
author = trimmedPath.slice(0, firstDotIndex)
snippetName = trimmedPath.slice(firstDotIndex + 1)
}

if (!author || !snippetName) {
console.error(
"Invalid snippet path. Use format: author/snippetName, author.snippetName or @tsci/author.snippetName",
)
process.exit(1)
}

const ky = getKy()

try {
console.log(`Cloning ${author}/${snippetName}...`)

const packageFileList = await ky
.post<{
package_files: Array<{
package_file_id: string
package_release_id: string
file_path: string
created_at: string
}>
}>("package_files/list", {
json: {
package_name: `${author}/${snippetName}`,
use_latest_version: true,
},
})
.json()

// Create directory if it doesn't exist
const dirPath = `./${author}.${snippetName}`
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath)
}

// Download each file that doesn't start with dist/
for (const fileInfo of packageFileList.package_files) {
const filePath = fileInfo.file_path.startsWith("/")
? fileInfo.file_path.slice(1)
: fileInfo.file_path

if (filePath.startsWith("dist/")) continue

const fileContent = await ky
.post<{
package_file: {
content_text: string
}
}>("package_files/get", {
json: {
package_name: `${author}/${snippetName}`,
file_path: fileInfo.file_path,
},
})
.json()

const fullPath = path.join(dirPath, filePath)
const dirName = path.dirname(fullPath)

// Create nested directories if they don't exist
if (!fs.existsSync(dirName)) {
fs.mkdirSync(dirName, { recursive: true })
}

fs.writeFileSync(fullPath, fileContent.package_file.content_text)
}

console.log(`Successfully cloned to ./${author}.${snippetName}/`)
} catch (error) {
if (error instanceof Error) {
console.error("Failed to clone snippet:", error.message)
} else {
console.error("Failed to clone snippet:", error)
}
process.exit(1)
}
})
}
2 changes: 2 additions & 0 deletions cli/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { registerAuthLogout } from "./auth/logout/register"
import { registerAuth } from "./auth/register"
import { registerConfig } from "./config/register"
import { registerConfigPrint } from "./config/print/register"
import { registerClone } from "./clone/register"
import { perfectCli } from "perfect-cli"

const program = new Command()
Expand All @@ -16,6 +17,7 @@ program
.version("1.0.0")

registerDev(program)
registerClone(program)

registerAuth(program)
registerAuthLogin(program)
Expand Down

0 comments on commit 18de102

Please sign in to comment.