bearnie 0.1.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 +295 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +663 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# Bearnie CLI
|
|
2
|
+
|
|
3
|
+
A command-line interface for adding Bearnie UI components to your Astro project.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# 1. Navigate to your Astro project
|
|
9
|
+
cd my-astro-project
|
|
10
|
+
|
|
11
|
+
# 2. Initialize Bearnie
|
|
12
|
+
npx bearnie init
|
|
13
|
+
|
|
14
|
+
# 3. Add components
|
|
15
|
+
npx bearnie add button card input
|
|
16
|
+
|
|
17
|
+
# 4. Use in your Astro pages
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```astro
|
|
21
|
+
---
|
|
22
|
+
import Button from "@/components/ui/button/Button.astro";
|
|
23
|
+
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
<Card>
|
|
27
|
+
<CardHeader>
|
|
28
|
+
<CardTitle>Welcome</CardTitle>
|
|
29
|
+
</CardHeader>
|
|
30
|
+
<CardContent>
|
|
31
|
+
<Button>Click me</Button>
|
|
32
|
+
</CardContent>
|
|
33
|
+
</Card>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Install globally
|
|
40
|
+
npm install -g bearnie
|
|
41
|
+
|
|
42
|
+
# Or use npx (recommended)
|
|
43
|
+
npx bearnie add button
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Commands
|
|
47
|
+
|
|
48
|
+
### `init`
|
|
49
|
+
|
|
50
|
+
Initialize Bearnie in your project. This sets up the necessary configuration and utilities.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npx bearnie init
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This will:
|
|
57
|
+
|
|
58
|
+
- Create a `bearnie.json` configuration file
|
|
59
|
+
- Set up the `src/components/bearnie` directory
|
|
60
|
+
- Create the `cn()` utility function
|
|
61
|
+
- Install `clsx`, `tailwind-merge`, and `tailwindcss` dependencies
|
|
62
|
+
|
|
63
|
+
**Options:**
|
|
64
|
+
|
|
65
|
+
- `-y, --yes` - Skip confirmation prompts and use defaults
|
|
66
|
+
- `-c, --cwd <path>` - Set the working directory (defaults to current directory)
|
|
67
|
+
|
|
68
|
+
### `add`
|
|
69
|
+
|
|
70
|
+
Add components to your project.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Add a single component
|
|
74
|
+
npx bearnie add button
|
|
75
|
+
|
|
76
|
+
# Add multiple components
|
|
77
|
+
npx bearnie add button card input
|
|
78
|
+
|
|
79
|
+
# Add all available components
|
|
80
|
+
npx bearnie add --all
|
|
81
|
+
|
|
82
|
+
# Interactive component selection
|
|
83
|
+
npx bearnie add
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Options:**
|
|
87
|
+
|
|
88
|
+
- `-y, --yes` - Skip confirmation prompts
|
|
89
|
+
- `-a, --all` - Add all available components
|
|
90
|
+
- `-c, --cwd <path>` - Set the working directory
|
|
91
|
+
|
|
92
|
+
### `list`
|
|
93
|
+
|
|
94
|
+
List all available components.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Display formatted list
|
|
98
|
+
npx bearnie list
|
|
99
|
+
|
|
100
|
+
# Output as JSON
|
|
101
|
+
npx bearnie list --json
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Options:**
|
|
105
|
+
|
|
106
|
+
- `--json` - Output as JSON
|
|
107
|
+
|
|
108
|
+
## Configuration
|
|
109
|
+
|
|
110
|
+
After running `init`, a `bearnie.json` file is created in your project root:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"componentsDir": "src/components/bearnie",
|
|
115
|
+
"utilsDir": "src/utils",
|
|
116
|
+
"typescript": true
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Configuration Options
|
|
121
|
+
|
|
122
|
+
| Option | Type | Default | Description |
|
|
123
|
+
| --------------- | --------- | --------------------- | -------------------------------------------- |
|
|
124
|
+
| `componentsDir` | `string` | `"src/components/bearnie"` | Directory where components will be installed |
|
|
125
|
+
| `utilsDir` | `string` | `"src/utils"` | Directory for utility functions |
|
|
126
|
+
| `typescript` | `boolean` | `true` | Whether to use TypeScript |
|
|
127
|
+
|
|
128
|
+
## Environment Variables
|
|
129
|
+
|
|
130
|
+
| Variable | Description |
|
|
131
|
+
| ---------------------- | ------------------------------------------------ |
|
|
132
|
+
| `BEARNIE_REGISTRY_URL` | Custom registry URL (for self-hosted registries) |
|
|
133
|
+
| `BEARNIE_REGISTRY_PATH` | Local file path to registry (for development) |
|
|
134
|
+
|
|
135
|
+
## Local Development
|
|
136
|
+
|
|
137
|
+
For local development and testing:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
# Clone the repository
|
|
141
|
+
git clone https://github.com/michael-andreuzza/bearnie.git
|
|
142
|
+
cd bearnie
|
|
143
|
+
|
|
144
|
+
# Install CLI dependencies
|
|
145
|
+
cd packages/cli
|
|
146
|
+
npm install
|
|
147
|
+
|
|
148
|
+
# Build the CLI
|
|
149
|
+
npm run build
|
|
150
|
+
|
|
151
|
+
# Link for local testing
|
|
152
|
+
npm link
|
|
153
|
+
|
|
154
|
+
# Now you can use it
|
|
155
|
+
bearnie add button
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Testing with Local Registry
|
|
159
|
+
|
|
160
|
+
1. Generate the registry files:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npm run generate-registry
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
2. Use the local registry path:
|
|
167
|
+
```bash
|
|
168
|
+
BEARNIE_REGISTRY_PATH=/path/to/bearnie/public/registry bearnie add button
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Or start the dev server and use the URL:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
npm run dev
|
|
175
|
+
BEARNIE_REGISTRY_URL=http://localhost:4321/registry bearnie add button
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Available Components
|
|
179
|
+
|
|
180
|
+
**Form:** button, input, textarea, label, checkbox, radio, select, switch
|
|
181
|
+
|
|
182
|
+
**Layout:** card, separator, scroll-area, aspect-ratio
|
|
183
|
+
|
|
184
|
+
**Navigation:** breadcrumb, tabs, dropdown-menu
|
|
185
|
+
|
|
186
|
+
**Feedback:** alert, badge, progress, skeleton, spinner, toast, tooltip
|
|
187
|
+
|
|
188
|
+
**Disclosure:** accordion, collapsible, dialog, popover
|
|
189
|
+
|
|
190
|
+
**Display:** avatar, table
|
|
191
|
+
|
|
192
|
+
## Usage Examples
|
|
193
|
+
|
|
194
|
+
### Basic Button
|
|
195
|
+
|
|
196
|
+
```astro
|
|
197
|
+
---
|
|
198
|
+
import Button from "@/components/ui/button/Button.astro";
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
<Button>Default</Button>
|
|
202
|
+
<Button variant="secondary">Secondary</Button>
|
|
203
|
+
<Button variant="outline">Outline</Button>
|
|
204
|
+
<Button variant="destructive">Delete</Button>
|
|
205
|
+
<Button size="sm">Small</Button>
|
|
206
|
+
<Button size="lg">Large</Button>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Form with Input and Label
|
|
210
|
+
|
|
211
|
+
```astro
|
|
212
|
+
---
|
|
213
|
+
import Input from "@/components/ui/input/Input.astro";
|
|
214
|
+
import Label from "@/components/ui/label/Label.astro";
|
|
215
|
+
import Button from "@/components/ui/button/Button.astro";
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
<form class="space-y-4">
|
|
219
|
+
<div>
|
|
220
|
+
<Label for="email">Email</Label>
|
|
221
|
+
<Input type="email" id="email" placeholder="you@example.com" />
|
|
222
|
+
</div>
|
|
223
|
+
<div>
|
|
224
|
+
<Label for="password">Password</Label>
|
|
225
|
+
<Input type="password" id="password" />
|
|
226
|
+
</div>
|
|
227
|
+
<Button type="submit">Sign In</Button>
|
|
228
|
+
</form>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Card with Content
|
|
232
|
+
|
|
233
|
+
```astro
|
|
234
|
+
---
|
|
235
|
+
import Card from "@/components/ui/card/Card.astro";
|
|
236
|
+
import CardHeader from "@/components/ui/card/CardHeader.astro";
|
|
237
|
+
import CardTitle from "@/components/ui/card/CardTitle.astro";
|
|
238
|
+
import CardDescription from "@/components/ui/card/CardDescription.astro";
|
|
239
|
+
import CardContent from "@/components/ui/card/CardContent.astro";
|
|
240
|
+
import CardFooter from "@/components/ui/card/CardFooter.astro";
|
|
241
|
+
import Button from "@/components/ui/button/Button.astro";
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
<Card class="w-96">
|
|
245
|
+
<CardHeader>
|
|
246
|
+
<CardTitle>Create Account</CardTitle>
|
|
247
|
+
<CardDescription>Enter your details below</CardDescription>
|
|
248
|
+
</CardHeader>
|
|
249
|
+
<CardContent>
|
|
250
|
+
<!-- Form fields here -->
|
|
251
|
+
</CardContent>
|
|
252
|
+
<CardFooter>
|
|
253
|
+
<Button class="w-full">Submit</Button>
|
|
254
|
+
</CardFooter>
|
|
255
|
+
</Card>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Alert Messages
|
|
259
|
+
|
|
260
|
+
```astro
|
|
261
|
+
---
|
|
262
|
+
import Alert from "@/components/ui/alert/Alert.astro";
|
|
263
|
+
import AlertTitle from "@/components/ui/alert/AlertTitle.astro";
|
|
264
|
+
import AlertDescription from "@/components/ui/alert/AlertDescription.astro";
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
<Alert>
|
|
268
|
+
<AlertTitle>Heads up!</AlertTitle>
|
|
269
|
+
<AlertDescription>This is an informational message.</AlertDescription>
|
|
270
|
+
</Alert>
|
|
271
|
+
|
|
272
|
+
<Alert variant="destructive">
|
|
273
|
+
<AlertTitle>Error</AlertTitle>
|
|
274
|
+
<AlertDescription>Something went wrong.</AlertDescription>
|
|
275
|
+
</Alert>
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Path Aliases
|
|
279
|
+
|
|
280
|
+
Bearnie components use the `@/` path alias. Make sure your `tsconfig.json` has:
|
|
281
|
+
|
|
282
|
+
```json
|
|
283
|
+
{
|
|
284
|
+
"compilerOptions": {
|
|
285
|
+
"baseUrl": ".",
|
|
286
|
+
"paths": {
|
|
287
|
+
"@/*": ["src/*"]
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## License
|
|
294
|
+
|
|
295
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,663 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import chalk5 from "chalk";
|
|
6
|
+
|
|
7
|
+
// src/commands/init.ts
|
|
8
|
+
import chalk2 from "chalk";
|
|
9
|
+
import ora from "ora";
|
|
10
|
+
import prompts from "prompts";
|
|
11
|
+
import path2 from "path";
|
|
12
|
+
import fs2 from "fs-extra";
|
|
13
|
+
import { execa } from "execa";
|
|
14
|
+
|
|
15
|
+
// src/utils/config.ts
|
|
16
|
+
import path from "path";
|
|
17
|
+
import fs from "fs-extra";
|
|
18
|
+
var DEFAULT_CONFIG = {
|
|
19
|
+
componentsDir: "src/components/bearnie",
|
|
20
|
+
utilsDir: "src/utils",
|
|
21
|
+
tailwindConfig: "tailwind.config.mjs",
|
|
22
|
+
typescript: true
|
|
23
|
+
};
|
|
24
|
+
var CONFIG_FILE = "bearnie.json";
|
|
25
|
+
async function getProjectConfig(cwd) {
|
|
26
|
+
const configPath = path.join(cwd, CONFIG_FILE);
|
|
27
|
+
if (await fs.pathExists(configPath)) {
|
|
28
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
29
|
+
return JSON.parse(content);
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
async function saveProjectConfig(cwd, config) {
|
|
34
|
+
const configPath = path.join(cwd, CONFIG_FILE);
|
|
35
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
36
|
+
}
|
|
37
|
+
async function isAstroProject(cwd) {
|
|
38
|
+
const packageJsonPath = path.join(cwd, "package.json");
|
|
39
|
+
if (!await fs.pathExists(packageJsonPath)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
43
|
+
const deps = {
|
|
44
|
+
...packageJson.dependencies,
|
|
45
|
+
...packageJson.devDependencies
|
|
46
|
+
};
|
|
47
|
+
return "astro" in deps;
|
|
48
|
+
}
|
|
49
|
+
async function hasTailwindInstalled(cwd) {
|
|
50
|
+
const packageJsonPath = path.join(cwd, "package.json");
|
|
51
|
+
if (!await fs.pathExists(packageJsonPath)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
55
|
+
const deps = {
|
|
56
|
+
...packageJson.dependencies,
|
|
57
|
+
...packageJson.devDependencies
|
|
58
|
+
};
|
|
59
|
+
return "tailwindcss" in deps;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/utils/ui.ts
|
|
63
|
+
import chalk from "chalk";
|
|
64
|
+
var brand = {
|
|
65
|
+
primary: chalk.hex("#F59E0B"),
|
|
66
|
+
// Amber (bear/honey color)
|
|
67
|
+
secondary: chalk.hex("#FCD34D"),
|
|
68
|
+
// Light amber
|
|
69
|
+
accent: chalk.hex("#FBBF24"),
|
|
70
|
+
// Mid amber
|
|
71
|
+
muted: chalk.dim,
|
|
72
|
+
success: chalk.green,
|
|
73
|
+
warning: chalk.yellow,
|
|
74
|
+
error: chalk.red,
|
|
75
|
+
info: chalk.cyan
|
|
76
|
+
};
|
|
77
|
+
var banner = `
|
|
78
|
+
${brand.primary(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E")}
|
|
79
|
+
${brand.primary(" \u2502")} ${brand.secondary("\u{1F43B}")} ${chalk.bold.white("bearnie")} ${brand.primary("\u2502")}
|
|
80
|
+
${brand.primary(" \u2502")} ${brand.muted("UI components for Astro")} ${brand.primary("\u2502")}
|
|
81
|
+
${brand.primary(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F")}
|
|
82
|
+
`;
|
|
83
|
+
var logo = `${brand.secondary("\u{1F43B}")} ${chalk.bold.white("bearnie")}`;
|
|
84
|
+
var messages = {
|
|
85
|
+
// Greetings
|
|
86
|
+
greeting: () => `${brand.primary("Hey!")} Let's build something beautiful.`,
|
|
87
|
+
// Init messages
|
|
88
|
+
initStart: () => `${brand.primary("Hey!")} Let's set up your project.`,
|
|
89
|
+
initSuccess: () => `${brand.success("\u2713")} ${chalk.bold("You're all set!")} Time to add some components.`,
|
|
90
|
+
alreadyInit: () => `Looks like Bearnie is already set up here.`,
|
|
91
|
+
// Add messages
|
|
92
|
+
addStart: () => `${brand.primary("Hey!")} Let's add some components.`,
|
|
93
|
+
fetching: () => `Fetching the good stuff...`,
|
|
94
|
+
foundComponents: (count) => `Found ${chalk.bold(count)} components ready to use`,
|
|
95
|
+
resolving: () => `Gathering what you need...`,
|
|
96
|
+
installing: (name) => `Adding ${chalk.cyan(name)}...`,
|
|
97
|
+
installed: (name) => `${brand.success("\u2713")} ${name} is ready`,
|
|
98
|
+
installingDeps: () => `Installing dependencies...`,
|
|
99
|
+
// List messages
|
|
100
|
+
listHeader: (version) => `${logo} ${brand.muted(`v${version}`)}`,
|
|
101
|
+
listFooter: (count) => `${brand.accent("\u{1F43B}")} ${count} components available`,
|
|
102
|
+
// Success messages (randomized for fun)
|
|
103
|
+
success: () => {
|
|
104
|
+
const messages2 = [
|
|
105
|
+
`${brand.success("\u{1F389}")} ${chalk.bold("All done!")} Happy building!`,
|
|
106
|
+
`${brand.success("\u2728")} ${chalk.bold("Looking good!")} Your components are ready.`,
|
|
107
|
+
`${brand.success("\u{1F43B}")} ${chalk.bold("Done!")} Time to make something awesome.`,
|
|
108
|
+
`${brand.success("\u{1F680}")} ${chalk.bold("Ready to go!")} Have fun building!`
|
|
109
|
+
];
|
|
110
|
+
return messages2[Math.floor(Math.random() * messages2.length)];
|
|
111
|
+
},
|
|
112
|
+
// Error messages (friendly, not scary)
|
|
113
|
+
notAstro: () => `Hmm, this doesn't look like an Astro project.`,
|
|
114
|
+
notAstroHelp: () => `Make sure you're in the root of an Astro project with a package.json.`,
|
|
115
|
+
networkError: () => `Couldn't reach the component registry.`,
|
|
116
|
+
networkErrorHelp: () => `Check your internet connection and try again.`,
|
|
117
|
+
unknownComponent: (names) => `Couldn't find: ${names.map((n) => chalk.yellow(n)).join(", ")}`,
|
|
118
|
+
// Hints
|
|
119
|
+
hint: (text) => brand.muted(` \u{1F4A1} ${text}`),
|
|
120
|
+
nextStep: (step) => ` ${brand.muted("\u2192")} ${step}`
|
|
121
|
+
};
|
|
122
|
+
var print = {
|
|
123
|
+
banner: () => console.log(banner),
|
|
124
|
+
logo: () => console.log(`
|
|
125
|
+
${logo}
|
|
126
|
+
`),
|
|
127
|
+
greeting: () => console.log(`
|
|
128
|
+
${messages.greeting()}
|
|
129
|
+
`),
|
|
130
|
+
success: () => console.log(`
|
|
131
|
+
${messages.success()}
|
|
132
|
+
`),
|
|
133
|
+
newline: () => console.log(),
|
|
134
|
+
// Formatted output
|
|
135
|
+
step: (text) => console.log(` ${text}`),
|
|
136
|
+
hint: (text) => console.log(messages.hint(text)),
|
|
137
|
+
error: (text) => console.log(` ${brand.error("\u2717")} ${text}`),
|
|
138
|
+
warning: (text) => console.log(` ${brand.warning("\u26A0")} ${text}`),
|
|
139
|
+
info: (text) => console.log(` ${brand.info("\u2139")} ${text}`),
|
|
140
|
+
// Next steps box
|
|
141
|
+
nextSteps: (steps) => {
|
|
142
|
+
console.log();
|
|
143
|
+
console.log(` ${chalk.bold("Next steps:")}`);
|
|
144
|
+
steps.forEach((step, i) => {
|
|
145
|
+
console.log(` ${brand.muted(`${i + 1}.`)} ${step}`);
|
|
146
|
+
});
|
|
147
|
+
console.log();
|
|
148
|
+
},
|
|
149
|
+
// Footer
|
|
150
|
+
footer: () => {
|
|
151
|
+
const link2 = (text, url) => `\x1B]8;;${url}\x07${chalk.cyan(text)}\x1B]8;;\x07`;
|
|
152
|
+
console.log(` ${brand.muted("Built with")} ${brand.secondary("\u{1F43B}")} ${brand.muted("by")} ${link2("Michael Andreuzza", "https://michaelandreuzza.com")}`);
|
|
153
|
+
console.log();
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
var promptsTheme = {
|
|
157
|
+
prefix: brand.secondary("?"),
|
|
158
|
+
highlight: brand.accent,
|
|
159
|
+
submit: brand.success("\u2713")
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// src/commands/init.ts
|
|
163
|
+
async function init(options) {
|
|
164
|
+
const cwd = path2.resolve(options.cwd);
|
|
165
|
+
print.logo();
|
|
166
|
+
console.log(` ${messages.initStart()}`);
|
|
167
|
+
print.newline();
|
|
168
|
+
const isAstro = await isAstroProject(cwd);
|
|
169
|
+
if (!isAstro) {
|
|
170
|
+
print.error(messages.notAstro());
|
|
171
|
+
print.hint(messages.notAstroHelp());
|
|
172
|
+
print.newline();
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
const configPath = path2.join(cwd, CONFIG_FILE);
|
|
176
|
+
if (await fs2.pathExists(configPath)) {
|
|
177
|
+
print.warning(messages.alreadyInit());
|
|
178
|
+
print.newline();
|
|
179
|
+
const { overwrite } = options.yes ? { overwrite: true } : await prompts({
|
|
180
|
+
type: "confirm",
|
|
181
|
+
name: "overwrite",
|
|
182
|
+
message: "Want to start fresh?",
|
|
183
|
+
initial: false
|
|
184
|
+
});
|
|
185
|
+
if (!overwrite) {
|
|
186
|
+
print.hint("No changes made. Your config is safe!");
|
|
187
|
+
print.newline();
|
|
188
|
+
process.exit(0);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const hasTailwind = await hasTailwindInstalled(cwd);
|
|
192
|
+
if (!hasTailwind) {
|
|
193
|
+
print.warning("Tailwind CSS isn't installed yet.");
|
|
194
|
+
const { installTailwind } = options.yes ? { installTailwind: true } : await prompts({
|
|
195
|
+
type: "confirm",
|
|
196
|
+
name: "installTailwind",
|
|
197
|
+
message: "Want me to install it for you?",
|
|
198
|
+
initial: true
|
|
199
|
+
});
|
|
200
|
+
if (installTailwind) {
|
|
201
|
+
const spinner2 = ora({
|
|
202
|
+
text: "Installing Tailwind CSS...",
|
|
203
|
+
color: "green"
|
|
204
|
+
}).start();
|
|
205
|
+
try {
|
|
206
|
+
await execa(
|
|
207
|
+
"npm",
|
|
208
|
+
["install", "-D", "tailwindcss", "@tailwindcss/vite"],
|
|
209
|
+
{ cwd }
|
|
210
|
+
);
|
|
211
|
+
spinner2.succeed(brand.success("Tailwind CSS is ready"));
|
|
212
|
+
} catch (error) {
|
|
213
|
+
spinner2.fail("Couldn't install Tailwind CSS");
|
|
214
|
+
print.hint(
|
|
215
|
+
"Try manually: npm install -D tailwindcss @tailwindcss/vite"
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
let config = { ...DEFAULT_CONFIG };
|
|
221
|
+
if (!options.yes) {
|
|
222
|
+
print.newline();
|
|
223
|
+
console.log(` ${chalk2.bold("Where should things go?")}`);
|
|
224
|
+
print.newline();
|
|
225
|
+
const responses = await prompts([
|
|
226
|
+
{
|
|
227
|
+
type: "text",
|
|
228
|
+
name: "componentsDir",
|
|
229
|
+
message: "Components directory",
|
|
230
|
+
initial: DEFAULT_CONFIG.componentsDir
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
type: "text",
|
|
234
|
+
name: "utilsDir",
|
|
235
|
+
message: "Utilities directory",
|
|
236
|
+
initial: DEFAULT_CONFIG.utilsDir
|
|
237
|
+
}
|
|
238
|
+
]);
|
|
239
|
+
config = {
|
|
240
|
+
...config,
|
|
241
|
+
...responses
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
const spinner = ora({
|
|
245
|
+
text: "Setting things up...",
|
|
246
|
+
color: "green"
|
|
247
|
+
}).start();
|
|
248
|
+
try {
|
|
249
|
+
await fs2.ensureDir(path2.join(cwd, config.componentsDir));
|
|
250
|
+
await fs2.ensureDir(path2.join(cwd, config.utilsDir));
|
|
251
|
+
spinner.text = "Creating directories...";
|
|
252
|
+
} catch (error) {
|
|
253
|
+
spinner.fail("Couldn't create directories");
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
const cnUtilContent = `import { type ClassValue, clsx } from "clsx";
|
|
257
|
+
import { twMerge } from "tailwind-merge";
|
|
258
|
+
|
|
259
|
+
export function cn(...inputs: ClassValue[]) {
|
|
260
|
+
return twMerge(clsx(inputs));
|
|
261
|
+
}
|
|
262
|
+
`;
|
|
263
|
+
try {
|
|
264
|
+
const utilPath = path2.join(cwd, config.utilsDir, "cn.ts");
|
|
265
|
+
if (!await fs2.pathExists(utilPath)) {
|
|
266
|
+
await fs2.writeFile(utilPath, cnUtilContent);
|
|
267
|
+
const packageJson = await fs2.readJson(path2.join(cwd, "package.json"));
|
|
268
|
+
const deps = {
|
|
269
|
+
...packageJson.dependencies,
|
|
270
|
+
...packageJson.devDependencies
|
|
271
|
+
};
|
|
272
|
+
const toInstall = [];
|
|
273
|
+
if (!("clsx" in deps)) toInstall.push("clsx");
|
|
274
|
+
if (!("tailwind-merge" in deps)) toInstall.push("tailwind-merge");
|
|
275
|
+
if (toInstall.length > 0) {
|
|
276
|
+
spinner.text = "Installing utilities...";
|
|
277
|
+
await execa("npm", ["install", ...toInstall], { cwd });
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
} catch (error) {
|
|
281
|
+
spinner.fail("Couldn't create utility files");
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
await saveProjectConfig(cwd, config);
|
|
285
|
+
spinner.succeed(brand.success("Everything is set up"));
|
|
286
|
+
} catch (error) {
|
|
287
|
+
spinner.fail("Couldn't save configuration");
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
print.newline();
|
|
291
|
+
console.log(` ${messages.initSuccess()}`);
|
|
292
|
+
print.nextSteps([
|
|
293
|
+
`Add your first component: ${chalk2.cyan("npx bearnie add button")}`,
|
|
294
|
+
`Browse all components: ${chalk2.cyan("npx bearnie list")}`,
|
|
295
|
+
`Add CSS variables: ${chalk2.cyan("npx bearnie add styles")}`
|
|
296
|
+
]);
|
|
297
|
+
print.footer();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// src/commands/add.ts
|
|
301
|
+
import chalk3 from "chalk";
|
|
302
|
+
import ora2 from "ora";
|
|
303
|
+
import prompts2 from "prompts";
|
|
304
|
+
import path4 from "path";
|
|
305
|
+
import fs4 from "fs-extra";
|
|
306
|
+
import { execa as execa2 } from "execa";
|
|
307
|
+
|
|
308
|
+
// src/utils/registry.ts
|
|
309
|
+
import fs3 from "fs-extra";
|
|
310
|
+
import path3 from "path";
|
|
311
|
+
var REGISTRY_URL = process.env.BEARNIE_REGISTRY_URL || "https://bearnie.dev/registry";
|
|
312
|
+
var REGISTRY_PATH = process.env.BEARNIE_REGISTRY_PATH;
|
|
313
|
+
var REGISTRY_INDEX_URL = `${REGISTRY_URL}/index.json`;
|
|
314
|
+
async function getRegistryIndex() {
|
|
315
|
+
if (REGISTRY_PATH) {
|
|
316
|
+
const indexPath = path3.join(REGISTRY_PATH, "index.json");
|
|
317
|
+
if (await fs3.pathExists(indexPath)) {
|
|
318
|
+
return fs3.readJson(indexPath);
|
|
319
|
+
}
|
|
320
|
+
throw new Error(`Registry index not found at: ${indexPath}`);
|
|
321
|
+
}
|
|
322
|
+
const response = await fetch(REGISTRY_INDEX_URL);
|
|
323
|
+
if (!response.ok) {
|
|
324
|
+
throw new Error(`Failed to fetch registry index: ${response.statusText}`);
|
|
325
|
+
}
|
|
326
|
+
return response.json();
|
|
327
|
+
}
|
|
328
|
+
async function getComponent(name) {
|
|
329
|
+
if (REGISTRY_PATH) {
|
|
330
|
+
const componentPath = path3.join(REGISTRY_PATH, `${name}.json`);
|
|
331
|
+
if (await fs3.pathExists(componentPath)) {
|
|
332
|
+
return fs3.readJson(componentPath);
|
|
333
|
+
}
|
|
334
|
+
throw new Error(`Component "${name}" not found at: ${componentPath}`);
|
|
335
|
+
}
|
|
336
|
+
const url = `${REGISTRY_URL}/${name}.json`;
|
|
337
|
+
const response = await fetch(url);
|
|
338
|
+
if (!response.ok) {
|
|
339
|
+
throw new Error(`Component "${name}" not found in registry`);
|
|
340
|
+
}
|
|
341
|
+
return response.json();
|
|
342
|
+
}
|
|
343
|
+
async function resolveComponentDependencies(names, resolved = /* @__PURE__ */ new Set()) {
|
|
344
|
+
const result = [];
|
|
345
|
+
for (const name of names) {
|
|
346
|
+
if (resolved.has(name)) continue;
|
|
347
|
+
resolved.add(name);
|
|
348
|
+
const component = await getComponent(name);
|
|
349
|
+
if (component.registryDependencies?.length) {
|
|
350
|
+
const deps = await resolveComponentDependencies(
|
|
351
|
+
component.registryDependencies,
|
|
352
|
+
resolved
|
|
353
|
+
);
|
|
354
|
+
result.push(...deps);
|
|
355
|
+
}
|
|
356
|
+
result.push(name);
|
|
357
|
+
}
|
|
358
|
+
return result;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// src/commands/add.ts
|
|
362
|
+
async function add(components, options) {
|
|
363
|
+
const cwd = path4.resolve(options.cwd);
|
|
364
|
+
print.logo();
|
|
365
|
+
console.log(` ${messages.addStart()}`);
|
|
366
|
+
print.newline();
|
|
367
|
+
let config = await getProjectConfig(cwd);
|
|
368
|
+
if (!config) {
|
|
369
|
+
print.warning(
|
|
370
|
+
`Project not initialized. Run ${chalk3.cyan("npx bearnie init")} first.`
|
|
371
|
+
);
|
|
372
|
+
print.newline();
|
|
373
|
+
const { proceed } = options.yes ? { proceed: true } : await prompts2({
|
|
374
|
+
type: "confirm",
|
|
375
|
+
name: "proceed",
|
|
376
|
+
message: "Want to use default settings for now?",
|
|
377
|
+
initial: true
|
|
378
|
+
});
|
|
379
|
+
if (!proceed) {
|
|
380
|
+
process.exit(0);
|
|
381
|
+
}
|
|
382
|
+
config = DEFAULT_CONFIG;
|
|
383
|
+
}
|
|
384
|
+
const indexSpinner = ora2({
|
|
385
|
+
text: messages.fetching(),
|
|
386
|
+
color: "green"
|
|
387
|
+
}).start();
|
|
388
|
+
let registryIndex;
|
|
389
|
+
try {
|
|
390
|
+
registryIndex = await getRegistryIndex();
|
|
391
|
+
indexSpinner.succeed(messages.foundComponents(registryIndex.components.length));
|
|
392
|
+
} catch (error) {
|
|
393
|
+
indexSpinner.fail(messages.networkError());
|
|
394
|
+
print.hint(messages.networkErrorHelp());
|
|
395
|
+
process.exit(1);
|
|
396
|
+
}
|
|
397
|
+
let selectedComponents = [];
|
|
398
|
+
if (options.all) {
|
|
399
|
+
selectedComponents = registryIndex.components.map((c) => c.name);
|
|
400
|
+
} else if (components.length === 0) {
|
|
401
|
+
print.newline();
|
|
402
|
+
const { selected } = await prompts2({
|
|
403
|
+
type: "multiselect",
|
|
404
|
+
name: "selected",
|
|
405
|
+
message: "What would you like to add?",
|
|
406
|
+
choices: registryIndex.components.map((c) => ({
|
|
407
|
+
title: c.name,
|
|
408
|
+
description: c.description,
|
|
409
|
+
value: c.name
|
|
410
|
+
})),
|
|
411
|
+
hint: "Space to select, Enter to confirm",
|
|
412
|
+
instructions: false
|
|
413
|
+
});
|
|
414
|
+
if (!selected || selected.length === 0) {
|
|
415
|
+
print.hint("No components selected.");
|
|
416
|
+
print.newline();
|
|
417
|
+
process.exit(0);
|
|
418
|
+
}
|
|
419
|
+
selectedComponents = selected;
|
|
420
|
+
} else {
|
|
421
|
+
const availableNames = registryIndex.components.map((c) => c.name);
|
|
422
|
+
const invalid = components.filter((c) => !availableNames.includes(c));
|
|
423
|
+
if (invalid.length > 0) {
|
|
424
|
+
print.error(messages.unknownComponent(invalid));
|
|
425
|
+
print.hint(`Run ${chalk3.cyan("npx bearnie list")} to see what's available.`);
|
|
426
|
+
print.newline();
|
|
427
|
+
process.exit(1);
|
|
428
|
+
}
|
|
429
|
+
selectedComponents = components;
|
|
430
|
+
}
|
|
431
|
+
const resolveSpinner = ora2({
|
|
432
|
+
text: messages.resolving(),
|
|
433
|
+
color: "green"
|
|
434
|
+
}).start();
|
|
435
|
+
let allComponents;
|
|
436
|
+
try {
|
|
437
|
+
allComponents = await resolveComponentDependencies(selectedComponents);
|
|
438
|
+
const extraDeps = allComponents.length - selectedComponents.length;
|
|
439
|
+
if (extraDeps > 0) {
|
|
440
|
+
resolveSpinner.succeed(
|
|
441
|
+
`Adding ${chalk3.bold(selectedComponents.length)} component${selectedComponents.length > 1 ? "s" : ""} ${brand.muted(`(+${extraDeps} dependencies)`)}`
|
|
442
|
+
);
|
|
443
|
+
} else {
|
|
444
|
+
resolveSpinner.succeed(
|
|
445
|
+
`Adding ${chalk3.bold(allComponents.length)} component${allComponents.length > 1 ? "s" : ""}`
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
} catch (error) {
|
|
449
|
+
resolveSpinner.fail("Couldn't resolve dependencies");
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
452
|
+
print.newline();
|
|
453
|
+
const npmDeps = /* @__PURE__ */ new Set();
|
|
454
|
+
const npmDevDeps = /* @__PURE__ */ new Set();
|
|
455
|
+
const writtenFiles = [];
|
|
456
|
+
const skippedFiles = [];
|
|
457
|
+
for (const componentName of allComponents) {
|
|
458
|
+
const spinner = ora2({
|
|
459
|
+
text: messages.installing(componentName),
|
|
460
|
+
color: "green"
|
|
461
|
+
}).start();
|
|
462
|
+
try {
|
|
463
|
+
const component = await getComponent(componentName);
|
|
464
|
+
component.dependencies?.forEach((d) => npmDeps.add(d));
|
|
465
|
+
component.devDependencies?.forEach((d) => npmDevDeps.add(d));
|
|
466
|
+
for (const file of component.files) {
|
|
467
|
+
const filePath = path4.join(cwd, config.componentsDir, file.path);
|
|
468
|
+
const exists = await fs4.pathExists(filePath);
|
|
469
|
+
if (exists && !options.yes) {
|
|
470
|
+
skippedFiles.push(file.path);
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
await fs4.ensureDir(path4.dirname(filePath));
|
|
474
|
+
await fs4.writeFile(filePath, file.content);
|
|
475
|
+
writtenFiles.push(file.path);
|
|
476
|
+
}
|
|
477
|
+
spinner.succeed(messages.installed(componentName));
|
|
478
|
+
} catch (error) {
|
|
479
|
+
spinner.fail(`Couldn't add ${componentName}`);
|
|
480
|
+
print.hint(`${error}`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const allDeps = [...npmDeps];
|
|
484
|
+
const allDevDeps = [...npmDevDeps];
|
|
485
|
+
if (allDeps.length > 0 || allDevDeps.length > 0) {
|
|
486
|
+
const depsSpinner = ora2({
|
|
487
|
+
text: messages.installingDeps(),
|
|
488
|
+
color: "green"
|
|
489
|
+
}).start();
|
|
490
|
+
try {
|
|
491
|
+
const packageJson = await fs4.readJson(path4.join(cwd, "package.json"));
|
|
492
|
+
const existingDeps = {
|
|
493
|
+
...packageJson.dependencies,
|
|
494
|
+
...packageJson.devDependencies
|
|
495
|
+
};
|
|
496
|
+
const newDeps = allDeps.filter((d) => !(d in existingDeps));
|
|
497
|
+
const newDevDeps = allDevDeps.filter((d) => !(d in existingDeps));
|
|
498
|
+
if (newDeps.length > 0) {
|
|
499
|
+
await execa2("npm", ["install", ...newDeps], { cwd });
|
|
500
|
+
}
|
|
501
|
+
if (newDevDeps.length > 0) {
|
|
502
|
+
await execa2("npm", ["install", "-D", ...newDevDeps], { cwd });
|
|
503
|
+
}
|
|
504
|
+
depsSpinner.succeed("Dependencies installed");
|
|
505
|
+
} catch (error) {
|
|
506
|
+
depsSpinner.fail("Some dependencies couldn't be installed");
|
|
507
|
+
print.hint("You might need to install them manually.");
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
print.newline();
|
|
511
|
+
if (writtenFiles.length > 0) {
|
|
512
|
+
console.log(
|
|
513
|
+
` ${brand.success("\u2713")} Created ${chalk3.bold(writtenFiles.length)} file${writtenFiles.length > 1 ? "s" : ""}:`
|
|
514
|
+
);
|
|
515
|
+
writtenFiles.slice(0, 5).forEach((f) => {
|
|
516
|
+
console.log(brand.muted(` ${f}`));
|
|
517
|
+
});
|
|
518
|
+
if (writtenFiles.length > 5) {
|
|
519
|
+
console.log(brand.muted(` ...and ${writtenFiles.length - 5} more`));
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
if (skippedFiles.length > 0) {
|
|
523
|
+
print.newline();
|
|
524
|
+
console.log(
|
|
525
|
+
` ${brand.warning("\u26A0")} Skipped ${skippedFiles.length} existing file${skippedFiles.length > 1 ? "s" : ""}`
|
|
526
|
+
);
|
|
527
|
+
print.hint(`Use ${chalk3.cyan("--yes")} to overwrite.`);
|
|
528
|
+
}
|
|
529
|
+
print.newline();
|
|
530
|
+
print.success();
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// src/commands/list.ts
|
|
534
|
+
import chalk4 from "chalk";
|
|
535
|
+
import ora3 from "ora";
|
|
536
|
+
async function list(options) {
|
|
537
|
+
const spinner = ora3({
|
|
538
|
+
text: messages.fetching(),
|
|
539
|
+
color: "green"
|
|
540
|
+
}).start();
|
|
541
|
+
try {
|
|
542
|
+
const registry = await getRegistryIndex();
|
|
543
|
+
spinner.stop();
|
|
544
|
+
if (options.json) {
|
|
545
|
+
console.log(JSON.stringify(registry.components, null, 2));
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
print.newline();
|
|
549
|
+
console.log(` ${messages.listHeader(registry.version)}`);
|
|
550
|
+
print.newline();
|
|
551
|
+
const categories = /* @__PURE__ */ new Map();
|
|
552
|
+
for (const component of registry.components) {
|
|
553
|
+
const cat = component.category || "other";
|
|
554
|
+
if (!categories.has(cat)) {
|
|
555
|
+
categories.set(cat, []);
|
|
556
|
+
}
|
|
557
|
+
categories.get(cat).push(component);
|
|
558
|
+
}
|
|
559
|
+
const categoryConfig = {
|
|
560
|
+
foundation: { emoji: "\u{1F3A8}", label: "Foundation" },
|
|
561
|
+
form: { emoji: "\u{1F4DD}", label: "Form" },
|
|
562
|
+
layout: { emoji: "\u{1F4D0}", label: "Layout" },
|
|
563
|
+
navigation: { emoji: "\u{1F9ED}", label: "Navigation" },
|
|
564
|
+
feedback: { emoji: "\u{1F4AC}", label: "Feedback" },
|
|
565
|
+
disclosure: { emoji: "\u{1F4C2}", label: "Disclosure" },
|
|
566
|
+
display: { emoji: "\u2728", label: "Display" },
|
|
567
|
+
other: { emoji: "\u{1F4E6}", label: "Other" }
|
|
568
|
+
};
|
|
569
|
+
const categoryOrder = [
|
|
570
|
+
"foundation",
|
|
571
|
+
"form",
|
|
572
|
+
"layout",
|
|
573
|
+
"navigation",
|
|
574
|
+
"feedback",
|
|
575
|
+
"disclosure",
|
|
576
|
+
"display",
|
|
577
|
+
"other"
|
|
578
|
+
];
|
|
579
|
+
for (const category of categoryOrder) {
|
|
580
|
+
const components = categories.get(category);
|
|
581
|
+
if (!components || components.length === 0) continue;
|
|
582
|
+
const config = categoryConfig[category] || { emoji: "\u{1F4E6}", label: category };
|
|
583
|
+
console.log(` ${config.emoji} ${chalk4.bold(config.label)}`);
|
|
584
|
+
print.newline();
|
|
585
|
+
for (const component of components) {
|
|
586
|
+
const name = brand.accent(component.name.padEnd(18));
|
|
587
|
+
const desc = brand.muted(component.description);
|
|
588
|
+
console.log(` ${name} ${desc}`);
|
|
589
|
+
}
|
|
590
|
+
print.newline();
|
|
591
|
+
}
|
|
592
|
+
console.log(` ${messages.listFooter(registry.components.length)}`);
|
|
593
|
+
print.newline();
|
|
594
|
+
console.log(` ${chalk4.bold("Quick commands:")}`);
|
|
595
|
+
console.log(` ${brand.muted("\u2192")} Add a component: ${chalk4.cyan("npx bearnie add button")}`);
|
|
596
|
+
console.log(` ${brand.muted("\u2192")} Add everything: ${chalk4.cyan("npx bearnie add --all")}`);
|
|
597
|
+
console.log(` ${brand.muted("\u2192")} Add CSS variables: ${chalk4.cyan("npx bearnie add styles")}`);
|
|
598
|
+
print.newline();
|
|
599
|
+
} catch (error) {
|
|
600
|
+
spinner.fail(messages.networkError());
|
|
601
|
+
print.hint(messages.networkErrorHelp());
|
|
602
|
+
process.exit(1);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// src/index.ts
|
|
607
|
+
var amber = chalk5.hex("#F59E0B");
|
|
608
|
+
var logo2 = `${amber("\u{1F43B}")} ${chalk5.bold("bearnie")}`;
|
|
609
|
+
var link = (text, url) => `\x1B]8;;${url}\x07${chalk5.cyan(text)}\x1B]8;;\x07`;
|
|
610
|
+
var program = new Command();
|
|
611
|
+
program.name("bearnie").description("UI components for Astro").version("0.1.0").configureOutput({
|
|
612
|
+
writeOut: (str) => process.stdout.write(str),
|
|
613
|
+
writeErr: (str) => process.stdout.write(str),
|
|
614
|
+
outputError: (str, write) => {
|
|
615
|
+
write(`
|
|
616
|
+
${logo2}
|
|
617
|
+
|
|
618
|
+
`);
|
|
619
|
+
write(` ${chalk5.red("Oops!")} ${str.replace("error: ", "")}
|
|
620
|
+
`);
|
|
621
|
+
}
|
|
622
|
+
}).addHelpText("beforeAll", `
|
|
623
|
+
${logo2}
|
|
624
|
+
`).addHelpText(
|
|
625
|
+
"afterAll",
|
|
626
|
+
`
|
|
627
|
+
${chalk5.dim("Built with")} ${amber("\u{1F43B}")} ${chalk5.dim("by")} ${link("Michael Andreuzza", "https://michaelandreuzza.com")}
|
|
628
|
+
`
|
|
629
|
+
);
|
|
630
|
+
program.command("init").description("Set up Bearnie in your project").option("-y, --yes", "Skip prompts and use defaults").option("--cwd <path>", "Working directory", process.cwd()).action(init);
|
|
631
|
+
program.command("add").description("Add components to your project").argument("[components...]", "Components to add").option("-y, --yes", "Skip prompts and overwrite files").option("-a, --all", "Add all components").option("--cwd <path>", "Working directory", process.cwd()).action(add);
|
|
632
|
+
program.command("list").description("Browse available components").option("--json", "Output as JSON").action(list);
|
|
633
|
+
program.action(() => {
|
|
634
|
+
console.log(`
|
|
635
|
+
${logo2}
|
|
636
|
+
|
|
637
|
+
${amber("Hey!")} UI components for Astro.
|
|
638
|
+
Built with Tailwind CSS, no frameworks required.
|
|
639
|
+
|
|
640
|
+
${chalk5.bold("Commands:")}
|
|
641
|
+
${chalk5.cyan("init")} Set up Bearnie in your project
|
|
642
|
+
${chalk5.cyan("add")} ${chalk5.dim("<name>")} Add a component
|
|
643
|
+
${chalk5.cyan("list")} Browse all components
|
|
644
|
+
|
|
645
|
+
${chalk5.bold("Examples:")}
|
|
646
|
+
${chalk5.dim("$")} npx bearnie init
|
|
647
|
+
${chalk5.dim("$")} npx bearnie add button card
|
|
648
|
+
${chalk5.dim("$")} npx bearnie add --all
|
|
649
|
+
|
|
650
|
+
${chalk5.dim("Run")} ${chalk5.cyan("bearnie <command> --help")} ${chalk5.dim("for more info")}
|
|
651
|
+
`);
|
|
652
|
+
});
|
|
653
|
+
program.parse();
|
|
654
|
+
program.on("command:*", () => {
|
|
655
|
+
console.log(`
|
|
656
|
+
${logo2}
|
|
657
|
+
|
|
658
|
+
${chalk5.yellow("Hmm,")} I don't know that command: ${chalk5.red(program.args.join(" "))}
|
|
659
|
+
|
|
660
|
+
Run ${chalk5.cyan("npx bearnie --help")} to see what I can do.
|
|
661
|
+
`);
|
|
662
|
+
process.exit(1);
|
|
663
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bearnie",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for installing Bearnie UI components",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"bin": "dist/index.js",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
13
|
+
"dev": "tsup src/index.ts --format esm --watch",
|
|
14
|
+
"typecheck": "tsc --noEmit"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"chalk": "^5.3.0",
|
|
18
|
+
"commander": "^12.1.0",
|
|
19
|
+
"execa": "^9.3.1",
|
|
20
|
+
"fs-extra": "^11.2.0",
|
|
21
|
+
"node-fetch": "^3.3.2",
|
|
22
|
+
"ora": "^8.0.1",
|
|
23
|
+
"prompts": "^2.4.2"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/fs-extra": "^11.0.4",
|
|
27
|
+
"@types/node": "^20.14.9",
|
|
28
|
+
"@types/prompts": "^2.4.9",
|
|
29
|
+
"tsup": "^8.1.0",
|
|
30
|
+
"typescript": "^5.5.3"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"author": "Michael Andreuzza",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/michael-andreuzza/bearnie.git",
|
|
39
|
+
"directory": "packages/cli"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://bearnie.dev",
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/michael-andreuzza/bearnie/issues"
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"astro",
|
|
47
|
+
"ui",
|
|
48
|
+
"components",
|
|
49
|
+
"cli",
|
|
50
|
+
"tailwindcss",
|
|
51
|
+
"bearnie"
|
|
52
|
+
]
|
|
53
|
+
}
|