shadcn-scaffold 1.0.0

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/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # cli-tool
2
+
3
+ To install dependencies:
4
+
5
+ ```bash
6
+ bun install
7
+ ```
8
+
9
+ To run:
10
+
11
+ ```bash
12
+ bun run index.ts
13
+ ```
14
+
15
+ This project was created using `bun init` in bun v1.3.10. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
package/dist/create.js ADDED
@@ -0,0 +1,95 @@
1
+ import ora from "ora";
2
+ import { execa } from "execa";
3
+ import chalk from "chalk";
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import { appTsxCode, indexCss, mainTsxCode, modeToggleCode, themeProviderCode, viteConfig, } from "./templates";
7
+ export async function create(projectName, packages) {
8
+ const spinner = ora("Scaffolding Vite React app...").start();
9
+ try {
10
+ await execa("bun", [
11
+ "create",
12
+ "vite@latest",
13
+ projectName,
14
+ "--template",
15
+ "react-ts",
16
+ ]);
17
+ spinner.succeed("Vite React app created");
18
+ spinner.start("Installing Tailwind CSS...");
19
+ await execa("bun", ["add", "tailwindcss", "@tailwindcss/vite"], {
20
+ cwd: projectName,
21
+ });
22
+ spinner.succeed("Tailwind CSS installed");
23
+ spinner.start("Configuring Tailwind CSS...");
24
+ fs.writeFileSync(path.join(projectName, "vite.config.ts"), viteConfig);
25
+ fs.writeFileSync(path.join(projectName, "src", "index.css"), indexCss);
26
+ fs.unlinkSync(path.join(projectName, "src", "App.css"));
27
+ spinner.succeed("Tailwind CSS configured");
28
+ const tsconfigAppPath = path.join(projectName, "tsconfig.app.json");
29
+ let tsconfigAppContent = fs.readFileSync(tsconfigAppPath, "utf-8");
30
+ tsconfigAppContent = tsconfigAppContent.replace('"compilerOptions": {', `"compilerOptions": {\n "ignoreDeprecations": "6.0",\n "baseUrl": ".",\n "paths": {\n "@/*": [\n "./src/*"\n ]\n },`);
31
+ fs.writeFileSync(tsconfigAppPath, tsconfigAppContent);
32
+ const tsconfigPath = path.join(projectName, "tsconfig.json");
33
+ const tsconfig = JSON.parse(fs.readFileSync(tsconfigPath, "utf-8"));
34
+ tsconfig.compilerOptions = {
35
+ baseUrl: ".",
36
+ ignoreDeprecations: "6.0",
37
+ paths: {
38
+ "@/*": ["./src/*"],
39
+ },
40
+ };
41
+ fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2));
42
+ spinner.start("Initializing shadcn/ui...");
43
+ await execa("bunx", [
44
+ "--bun",
45
+ "shadcn@latest",
46
+ "init",
47
+ "-t",
48
+ "vite",
49
+ "-d",
50
+ "-y",
51
+ "--cwd",
52
+ projectName,
53
+ ]);
54
+ spinner.succeed("shadcn/ui initialized");
55
+ spinner.start("Adding shadcn button component...");
56
+ await execa("bunx", [
57
+ "--bun",
58
+ "shadcn@latest",
59
+ "add",
60
+ "button",
61
+ "-y",
62
+ "--cwd",
63
+ projectName,
64
+ ]);
65
+ spinner.succeed("shadcn button added");
66
+ spinner.start("Setting up custom project structure and theme toggle...");
67
+ fs.mkdirSync(path.join(projectName, "src", "providers"), {
68
+ recursive: true,
69
+ });
70
+ fs.mkdirSync(path.join(projectName, "src", "features", "theme"), {
71
+ recursive: true,
72
+ });
73
+ fs.writeFileSync(path.join(projectName, "src", "providers", "theme-provider.tsx"), themeProviderCode);
74
+ fs.writeFileSync(path.join(projectName, "src", "features", "theme", "mode-toggle.tsx"), modeToggleCode);
75
+ fs.writeFileSync(path.join(projectName, "src", "main.tsx"), mainTsxCode);
76
+ fs.writeFileSync(path.join(projectName, "src", "App.tsx"), appTsxCode);
77
+ spinner.succeed("Project structure and theme configured");
78
+ if (packages.length > 0) {
79
+ spinner.start(`Installing extra packages: ${packages.join(", ")}...`);
80
+ await execa("bun", ["add", ...packages], {
81
+ cwd: projectName,
82
+ });
83
+ spinner.succeed("Extra packages installed");
84
+ }
85
+ console.log(`\n${chalk.green("✔")} Successfully created ${chalk.cyan(projectName)}!`);
86
+ console.log(`\nNext steps:`);
87
+ console.log(` ${chalk.cyan(`cd ${projectName}`)}`);
88
+ console.log(` ${chalk.cyan("bun run dev")}\n`);
89
+ }
90
+ catch (error) {
91
+ spinner.fail("An error occurred during setup");
92
+ console.error(error);
93
+ throw error;
94
+ }
95
+ }
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import { Command } from "commander";
5
+ import { App } from "./ui.js";
6
+ import { render } from "ink";
7
+ const program = new Command();
8
+ program
9
+ .name("scaffold")
10
+ .description("Sets up your personal React stack instantly")
11
+ .version("1.0.0")
12
+ .argument("<project-name>", "Name of your project")
13
+ .action((projectName) => {
14
+ render(_jsx(App, { projectName: projectName }));
15
+ });
16
+ program.parse();
@@ -0,0 +1,161 @@
1
+ export const viteConfig = `import path from "path";
2
+ import { fileURLToPath } from "url";
3
+ import tailwindcss from "@tailwindcss/vite";
4
+ import react from "@vitejs/plugin-react";
5
+ import { defineConfig } from "vite";
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+
9
+ export default defineConfig({
10
+ plugins: [react(), tailwindcss()],
11
+ resolve: {
12
+ alias: {
13
+ "@": path.resolve(__dirname, "./src"),
14
+ },
15
+ },
16
+ });
17
+ `;
18
+ export const indexCss = `@import "tailwindcss";
19
+ `;
20
+ export const themeProviderCode = `import { createContext, useContext, useEffect, useState } from "react"
21
+
22
+ type Theme = "dark" | "light" | "system"
23
+
24
+ type ThemeProviderProps = {
25
+ children: React.ReactNode
26
+ defaultTheme?: Theme
27
+ storageKey?: string
28
+ }
29
+
30
+ type ThemeProviderState = {
31
+ theme: Theme
32
+ setTheme: (theme: Theme) => void
33
+ }
34
+
35
+ const ThemeProviderContext = createContext<ThemeProviderState | undefined>(
36
+ undefined
37
+ )
38
+
39
+ export function ThemeProvider({
40
+ children,
41
+ defaultTheme = "system",
42
+ storageKey = "vite-ui-theme",
43
+ ...props
44
+ }: ThemeProviderProps) {
45
+ const [theme, setTheme] = useState<Theme>(
46
+ () => (localStorage.getItem(storageKey) as Theme) || defaultTheme
47
+ )
48
+
49
+ useEffect(() => {
50
+ const root = window.document.documentElement
51
+
52
+ root.classList.remove("light", "dark")
53
+
54
+ if (theme === "system") {
55
+ const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
56
+ .matches
57
+ ? "dark"
58
+ : "light"
59
+
60
+ root.classList.add(systemTheme)
61
+ return
62
+ }
63
+
64
+ root.classList.add(theme)
65
+ }, [theme])
66
+
67
+ const value = {
68
+ theme,
69
+ setTheme: (theme: Theme) => {
70
+ localStorage.setItem(storageKey, theme)
71
+ setTheme(theme)
72
+ },
73
+ }
74
+
75
+ return (
76
+ <ThemeProviderContext.Provider {...props} value={value}>
77
+ {children}
78
+ </ThemeProviderContext.Provider>
79
+ )
80
+ }
81
+
82
+ export const useTheme = () => {
83
+ const context = useContext(ThemeProviderContext)
84
+
85
+ if (!context)
86
+ throw new Error("useTheme must be used within a ThemeProvider")
87
+
88
+ return context
89
+ }
90
+ `;
91
+ export const mainTsxCode = `import { StrictMode } from 'react'
92
+ import { createRoot } from 'react-dom/client'
93
+ import './index.css'
94
+ import App from './App.tsx'
95
+ import { ThemeProvider } from "@/providers/theme-provider"
96
+
97
+ createRoot(document.getElementById('root')!).render(
98
+ <StrictMode>
99
+ <ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
100
+ <App />
101
+ </ThemeProvider>
102
+ </StrictMode>,
103
+ )
104
+ `;
105
+ export const modeToggleCode = `import { Moon, Sun } from "lucide-react"
106
+
107
+ import { Button } from "@/components/ui/button"
108
+ import { useTheme } from "@/providers/theme-provider"
109
+
110
+ export function ModeToggle() {
111
+ const { theme, setTheme } = useTheme()
112
+
113
+ return (
114
+ <Button
115
+ variant="outline"
116
+ size="icon"
117
+ onClick={() => setTheme(theme === "light" ? "dark" : "light")}
118
+ >
119
+ <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
120
+ <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
121
+ <span className="sr-only">Toggle theme</span>
122
+ </Button>
123
+ )
124
+ }
125
+ `;
126
+ export const appTsxCode = `import { ModeToggle } from "@/features/theme/mode-toggle"
127
+ import { Button } from "@/components/ui/button"
128
+
129
+ function App() {
130
+ return (
131
+ <div className="flex min-h-screen flex-col items-center justify-center p-24 gap-4">
132
+ <div className="absolute top-4 right-4">
133
+ <ModeToggle />
134
+ </div>
135
+ <h1 className="text-4xl font-bold">Hello World</h1>
136
+ <Button>Click me</Button>
137
+ </div>
138
+ )
139
+ }
140
+
141
+ export default App
142
+ `;
143
+ export const tsconfigCode = `{
144
+ "files": [],
145
+ "references": [
146
+ {
147
+ "path": "./tsconfig.app.json"
148
+ },
149
+ {
150
+ "path": "./tsconfig.node.json"
151
+ }
152
+ ],
153
+ "compilerOptions": {
154
+ "baseUrl": ".",
155
+ "ignoreDeprecations": "6.0",
156
+ "paths": {
157
+ "@/*": ["./src/*"]
158
+ }
159
+ }
160
+ }
161
+ `;
package/dist/ui.js ADDED
@@ -0,0 +1,32 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import React, { useState } from "react";
3
+ import { Text, Box, useApp } from "ink";
4
+ import { MultiSelect } from "@inkjs/ui";
5
+ import { create } from "./create.js";
6
+ export function App({ projectName }) {
7
+ const { exit } = useApp();
8
+ const [isDone, setIsDone] = useState(false);
9
+ const options = [
10
+ { label: "Zustand (State Management)", value: "zustand" },
11
+ { label: "React Router DOM (Routing)", value: "react-router-dom" },
12
+ { label: "TanStack Query (Data Fetching/Caching)", value: "@tanstack/react-query" },
13
+ { label: "Axios (HTTP Client)", value: "axios" },
14
+ { label: "Framer Motion (Animations)", value: "framer-motion" },
15
+ { label: "React Hook Form (Forms)", value: "react-hook-form" },
16
+ { label: "Zod (Schema Validation)", value: "zod" },
17
+ ];
18
+ const handleSubmit = async (selectedValues) => {
19
+ setIsDone(true);
20
+ exit();
21
+ try {
22
+ await create(projectName, selectedValues);
23
+ }
24
+ catch {
25
+ process.exitCode = 1;
26
+ }
27
+ };
28
+ if (isDone) {
29
+ return null;
30
+ }
31
+ return (_jsxs(Box, { flexDirection: "column", gap: 1, padding: 1, children: [_jsxs(Text, { color: "orange", children: ["Select extra packages for ", projectName, " (Space to select, Enter to submit);"] }), _jsx(MultiSelect, { options: options, onSubmit: handleSubmit })] }));
32
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "shadcn-scaffold",
3
+ "version": "1.0.0",
4
+ "bin": {
5
+ "scaffold": "./dist/index.js"
6
+ },
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "devDependencies": {
14
+ "@types/bun": "latest",
15
+ "@types/node": "^25.7.0",
16
+ "@types/react": "^19.2.14",
17
+ "tsx": "^4.21.0",
18
+ "typescript": "^6.0.3"
19
+ },
20
+ "dependencies": {
21
+ "@inkjs/ui": "^2.0.0",
22
+ "chalk": "^5.6.2",
23
+ "commander": "^14.0.3",
24
+ "execa": "^9.6.1",
25
+ "ink": "^7.0.3",
26
+ "ora": "^9.4.0",
27
+ "react": "^19.2.6"
28
+ },
29
+ "scripts": {
30
+ "dev": "tsx src/index.tsx",
31
+ "build": "tsc -p tsconfig.build.json",
32
+ "start": "node dist/index.js",
33
+ "prepublishOnly": "npm run build"
34
+ }
35
+ }