create-bunli 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 +302 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +310 -0
- package/dist/create-project.d.ts +13 -0
- package/dist/create.d.ts +13 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +217 -0
- package/dist/template-engine.d.ts +27 -0
- package/dist/templates/advanced/README.md +114 -0
- package/dist/templates/advanced/package.json +36 -0
- package/dist/templates/advanced/src/commands/config.ts +145 -0
- package/dist/templates/advanced/src/commands/init.ts +153 -0
- package/dist/templates/advanced/src/commands/serve.ts +176 -0
- package/dist/templates/advanced/src/commands/validate.ts +116 -0
- package/dist/templates/advanced/src/index.ts +44 -0
- package/dist/templates/advanced/src/utils/config.ts +83 -0
- package/dist/templates/advanced/src/utils/constants.ts +12 -0
- package/dist/templates/advanced/src/utils/glob.ts +49 -0
- package/dist/templates/advanced/src/utils/validator.ts +131 -0
- package/dist/templates/advanced/template.json +37 -0
- package/dist/templates/advanced/test/commands.test.ts +34 -0
- package/dist/templates/advanced/tsconfig.json +23 -0
- package/dist/templates/basic/README.md +41 -0
- package/dist/templates/basic/package.json +29 -0
- package/dist/templates/basic/src/commands/hello.ts +29 -0
- package/dist/templates/basic/src/index.ts +13 -0
- package/dist/templates/basic/template.json +31 -0
- package/dist/templates/basic/test/hello.test.ts +26 -0
- package/dist/templates/basic/tsconfig.json +19 -0
- package/dist/templates/monorepo/README.md +74 -0
- package/dist/templates/monorepo/package.json +28 -0
- package/dist/templates/monorepo/packages/cli/package.json +34 -0
- package/dist/templates/monorepo/packages/cli/src/index.ts +22 -0
- package/dist/templates/monorepo/packages/cli/tsconfig.json +15 -0
- package/dist/templates/monorepo/packages/core/package.json +32 -0
- package/dist/templates/monorepo/packages/core/scripts/build.ts +18 -0
- package/dist/templates/monorepo/packages/core/src/commands/analyze.ts +84 -0
- package/dist/templates/monorepo/packages/core/src/commands/process.ts +64 -0
- package/dist/templates/monorepo/packages/core/src/index.ts +3 -0
- package/dist/templates/monorepo/packages/core/src/types.ts +21 -0
- package/dist/templates/monorepo/packages/core/tsconfig.json +15 -0
- package/dist/templates/monorepo/packages/utils/package.json +26 -0
- package/dist/templates/monorepo/packages/utils/scripts/build.ts +17 -0
- package/dist/templates/monorepo/packages/utils/src/format.ts +27 -0
- package/dist/templates/monorepo/packages/utils/src/index.ts +3 -0
- package/dist/templates/monorepo/packages/utils/src/json.ts +11 -0
- package/dist/templates/monorepo/packages/utils/src/logger.ts +19 -0
- package/dist/templates/monorepo/packages/utils/tsconfig.json +12 -0
- package/dist/templates/monorepo/template.json +24 -0
- package/dist/templates/monorepo/tsconfig.json +14 -0
- package/dist/templates/monorepo/turbo.json +28 -0
- package/dist/types.d.ts +48 -0
- package/package.json +57 -0
- package/templates/advanced/README.md +114 -0
- package/templates/advanced/package.json +36 -0
- package/templates/advanced/src/commands/config.ts +145 -0
- package/templates/advanced/src/commands/init.ts +153 -0
- package/templates/advanced/src/commands/serve.ts +176 -0
- package/templates/advanced/src/commands/validate.ts +116 -0
- package/templates/advanced/src/index.ts +44 -0
- package/templates/advanced/src/utils/config.ts +83 -0
- package/templates/advanced/src/utils/constants.ts +12 -0
- package/templates/advanced/src/utils/glob.ts +49 -0
- package/templates/advanced/src/utils/validator.ts +131 -0
- package/templates/advanced/template.json +37 -0
- package/templates/advanced/test/commands.test.ts +34 -0
- package/templates/advanced/tsconfig.json +23 -0
- package/templates/basic/README.md +41 -0
- package/templates/basic/package.json +29 -0
- package/templates/basic/src/commands/hello.ts +29 -0
- package/templates/basic/src/index.ts +13 -0
- package/templates/basic/template.json +31 -0
- package/templates/basic/test/hello.test.ts +26 -0
- package/templates/basic/tsconfig.json +19 -0
- package/templates/monorepo/README.md +74 -0
- package/templates/monorepo/package.json +28 -0
- package/templates/monorepo/packages/cli/package.json +34 -0
- package/templates/monorepo/packages/cli/src/index.ts +22 -0
- package/templates/monorepo/packages/cli/tsconfig.json +15 -0
- package/templates/monorepo/packages/core/package.json +32 -0
- package/templates/monorepo/packages/core/scripts/build.ts +18 -0
- package/templates/monorepo/packages/core/src/commands/analyze.ts +84 -0
- package/templates/monorepo/packages/core/src/commands/process.ts +64 -0
- package/templates/monorepo/packages/core/src/index.ts +3 -0
- package/templates/monorepo/packages/core/src/types.ts +21 -0
- package/templates/monorepo/packages/core/tsconfig.json +15 -0
- package/templates/monorepo/packages/utils/package.json +26 -0
- package/templates/monorepo/packages/utils/scripts/build.ts +17 -0
- package/templates/monorepo/packages/utils/src/format.ts +27 -0
- package/templates/monorepo/packages/utils/src/index.ts +3 -0
- package/templates/monorepo/packages/utils/src/json.ts +11 -0
- package/templates/monorepo/packages/utils/src/logger.ts +19 -0
- package/templates/monorepo/packages/utils/tsconfig.json +12 -0
- package/templates/monorepo/template.json +24 -0
- package/templates/monorepo/tsconfig.json +14 -0
- package/templates/monorepo/turbo.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# create-bunli
|
|
2
|
+
|
|
3
|
+
Scaffold new Bunli CLI projects with ease.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Using bunx (recommended)
|
|
9
|
+
bunx create-bunli my-cli
|
|
10
|
+
|
|
11
|
+
# Using npm
|
|
12
|
+
npm create bunli@latest my-cli
|
|
13
|
+
|
|
14
|
+
# Using yarn
|
|
15
|
+
yarn create bunli my-cli
|
|
16
|
+
|
|
17
|
+
# Using pnpm
|
|
18
|
+
pnpm create bunli my-cli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- 🚀 **Fast scaffolding** - Get started in seconds
|
|
24
|
+
- 📦 **Multiple templates** - Choose from basic, advanced, or monorepo setups
|
|
25
|
+
- 🔧 **TypeScript ready** - Full TypeScript support out of the box
|
|
26
|
+
- 🧪 **Testing included** - Comes with @bunli/test for CLI testing
|
|
27
|
+
- 🎨 **Best practices** - Follows Bunli conventions and patterns
|
|
28
|
+
- 🌐 **Flexible sources** - Use bundled templates or any GitHub repository
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
### Basic Usage
|
|
33
|
+
|
|
34
|
+
Create a new project with the default template:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
bunx create-bunli my-cli
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Using Templates
|
|
41
|
+
|
|
42
|
+
Choose from bundled templates:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Basic single-command CLI
|
|
46
|
+
bunx create-bunli my-cli --template basic
|
|
47
|
+
|
|
48
|
+
# Advanced multi-command CLI with subcommands
|
|
49
|
+
bunx create-bunli my-cli --template advanced
|
|
50
|
+
|
|
51
|
+
# Monorepo setup with Turborepo
|
|
52
|
+
bunx create-bunli my-cli --template monorepo
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Using External Templates
|
|
56
|
+
|
|
57
|
+
Use any GitHub repository as a template:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# GitHub repository
|
|
61
|
+
bunx create-bunli my-cli --template username/repo
|
|
62
|
+
|
|
63
|
+
# With full GitHub URL
|
|
64
|
+
bunx create-bunli my-cli --template github:username/repo
|
|
65
|
+
|
|
66
|
+
# Specific branch or tag
|
|
67
|
+
bunx create-bunli my-cli --template username/repo#branch
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Options
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
bunx create-bunli [name] [options]
|
|
74
|
+
|
|
75
|
+
Options:
|
|
76
|
+
-t, --template <template> Project template (default: "basic")
|
|
77
|
+
-d, --dir <dir> Directory to create project in
|
|
78
|
+
-p, --package-manager <pm> Package manager (bun, npm, yarn, pnpm) (default: "bun")
|
|
79
|
+
-g, --git Initialize git repository (default: true)
|
|
80
|
+
-i, --install Install dependencies (default: true)
|
|
81
|
+
--offline Use cached templates when available
|
|
82
|
+
-h, --help Display help
|
|
83
|
+
-v, --version Display version
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Examples
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Create in current directory
|
|
90
|
+
bunx create-bunli .
|
|
91
|
+
|
|
92
|
+
# Create with specific package manager
|
|
93
|
+
bunx create-bunli my-cli --package-manager pnpm
|
|
94
|
+
|
|
95
|
+
# Create without installing dependencies
|
|
96
|
+
bunx create-bunli my-cli --no-install
|
|
97
|
+
|
|
98
|
+
# Create in custom directory
|
|
99
|
+
bunx create-bunli my-cli --dir ~/projects/my-cli
|
|
100
|
+
|
|
101
|
+
# Use external template
|
|
102
|
+
bunx create-bunli my-cli --template pvtnbr/bunli-starter
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Templates
|
|
106
|
+
|
|
107
|
+
### Basic Template
|
|
108
|
+
|
|
109
|
+
Perfect for simple CLI tools with a single command.
|
|
110
|
+
|
|
111
|
+
**Features:**
|
|
112
|
+
- Single command setup
|
|
113
|
+
- TypeScript configuration
|
|
114
|
+
- Test setup with @bunli/test
|
|
115
|
+
- Build script using bunli
|
|
116
|
+
|
|
117
|
+
**Structure:**
|
|
118
|
+
```
|
|
119
|
+
my-cli/
|
|
120
|
+
├── src/
|
|
121
|
+
│ ├── index.ts # CLI entry point
|
|
122
|
+
│ └── commands/
|
|
123
|
+
│ └── hello.ts # Example command
|
|
124
|
+
├── test/
|
|
125
|
+
│ └── hello.test.ts # Example test
|
|
126
|
+
├── package.json
|
|
127
|
+
├── tsconfig.json
|
|
128
|
+
└── README.md
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Advanced Template
|
|
132
|
+
|
|
133
|
+
For complex CLIs with multiple commands and features.
|
|
134
|
+
|
|
135
|
+
**Features:**
|
|
136
|
+
- Multiple commands with subcommands
|
|
137
|
+
- Configuration management
|
|
138
|
+
- File validation system
|
|
139
|
+
- Built-in development server
|
|
140
|
+
- Advanced command examples
|
|
141
|
+
|
|
142
|
+
**Commands included:**
|
|
143
|
+
- `init` - Initialize configuration
|
|
144
|
+
- `validate` - Validate files with rules
|
|
145
|
+
- `serve` - Start development server
|
|
146
|
+
- `config` - Manage configuration
|
|
147
|
+
|
|
148
|
+
**Structure:**
|
|
149
|
+
```
|
|
150
|
+
my-cli/
|
|
151
|
+
├── src/
|
|
152
|
+
│ ├── index.ts # CLI entry point
|
|
153
|
+
│ ├── commands/ # Command implementations
|
|
154
|
+
│ │ ├── init.ts
|
|
155
|
+
│ │ ├── validate.ts
|
|
156
|
+
│ │ ├── serve.ts
|
|
157
|
+
│ │ └── config.ts
|
|
158
|
+
│ └── utils/ # Utility functions
|
|
159
|
+
│ ├── config.ts
|
|
160
|
+
│ ├── validator.ts
|
|
161
|
+
│ └── glob.ts
|
|
162
|
+
├── test/
|
|
163
|
+
├── package.json
|
|
164
|
+
├── tsconfig.json
|
|
165
|
+
└── README.md
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Monorepo Template
|
|
169
|
+
|
|
170
|
+
For large projects with multiple packages.
|
|
171
|
+
|
|
172
|
+
**Features:**
|
|
173
|
+
- Turborepo configuration
|
|
174
|
+
- Multiple packages setup
|
|
175
|
+
- Shared dependencies
|
|
176
|
+
- Changeset support
|
|
177
|
+
- Parallel builds
|
|
178
|
+
|
|
179
|
+
**Structure:**
|
|
180
|
+
```
|
|
181
|
+
my-cli/
|
|
182
|
+
├── packages/
|
|
183
|
+
│ ├── cli/ # Main CLI package
|
|
184
|
+
│ ├── core/ # Core functionality
|
|
185
|
+
│ └── utils/ # Shared utilities
|
|
186
|
+
├── turbo.json # Turborepo config
|
|
187
|
+
├── package.json # Root package.json
|
|
188
|
+
├── tsconfig.json # Root TypeScript config
|
|
189
|
+
└── README.md
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Creating Custom Templates
|
|
193
|
+
|
|
194
|
+
Templates can include a `template.json` manifest:
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"name": "my-template",
|
|
199
|
+
"description": "My custom template",
|
|
200
|
+
"variables": [
|
|
201
|
+
{
|
|
202
|
+
"name": "projectName",
|
|
203
|
+
"message": "Project name",
|
|
204
|
+
"type": "string",
|
|
205
|
+
"default": "my-project"
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"name": "license",
|
|
209
|
+
"message": "License",
|
|
210
|
+
"type": "select",
|
|
211
|
+
"choices": ["MIT", "Apache-2.0", "GPL-3.0"]
|
|
212
|
+
}
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Template Variables
|
|
218
|
+
|
|
219
|
+
Use these variables in your template files:
|
|
220
|
+
|
|
221
|
+
- `{{projectName}}` - The project name
|
|
222
|
+
- `{{description}}` - Project description
|
|
223
|
+
- `{{author}}` - Author name
|
|
224
|
+
- `{{license}}` - License type
|
|
225
|
+
- `{{year}}` - Current year
|
|
226
|
+
- `{{packageManager}}` - Selected package manager
|
|
227
|
+
|
|
228
|
+
Variables can be used in file contents and filenames:
|
|
229
|
+
- `__projectName__.config.js` → `my-app.config.js`
|
|
230
|
+
|
|
231
|
+
## Programmatic Usage
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { createProject } from 'create-bunli'
|
|
235
|
+
|
|
236
|
+
await createProject({
|
|
237
|
+
name: 'my-cli',
|
|
238
|
+
template: 'advanced',
|
|
239
|
+
packageManager: 'bun',
|
|
240
|
+
install: true,
|
|
241
|
+
git: true
|
|
242
|
+
})
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Development
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
# Clone the repository
|
|
249
|
+
git clone https://github.com/AryaLabsHQ/bunli.git
|
|
250
|
+
cd bunli/packages/create-bunli
|
|
251
|
+
|
|
252
|
+
# Install dependencies
|
|
253
|
+
bun install
|
|
254
|
+
|
|
255
|
+
# Run in development
|
|
256
|
+
bun dev
|
|
257
|
+
|
|
258
|
+
# Run tests
|
|
259
|
+
bun test
|
|
260
|
+
|
|
261
|
+
# Build
|
|
262
|
+
bun run build
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Troubleshooting
|
|
266
|
+
|
|
267
|
+
### Template not found
|
|
268
|
+
|
|
269
|
+
If you get a "template not found" error, ensure:
|
|
270
|
+
- The template name is correct
|
|
271
|
+
- For GitHub templates, the repository exists and is public
|
|
272
|
+
- You have internet connection (for external templates)
|
|
273
|
+
|
|
274
|
+
### Installation fails
|
|
275
|
+
|
|
276
|
+
If dependency installation fails:
|
|
277
|
+
- Check your internet connection
|
|
278
|
+
- Ensure the package manager is installed
|
|
279
|
+
- Try running with `--no-install` and install manually
|
|
280
|
+
|
|
281
|
+
### Permission errors
|
|
282
|
+
|
|
283
|
+
If you get permission errors:
|
|
284
|
+
- Ensure you have write access to the target directory
|
|
285
|
+
- Try running in a different directory
|
|
286
|
+
- Check disk space availability
|
|
287
|
+
|
|
288
|
+
## Contributing
|
|
289
|
+
|
|
290
|
+
Contributions are welcome! Please read our [Contributing Guide](../../CONTRIBUTING.md) for details.
|
|
291
|
+
|
|
292
|
+
### Adding a New Template
|
|
293
|
+
|
|
294
|
+
1. Create a new directory in `templates/`
|
|
295
|
+
2. Add all template files
|
|
296
|
+
3. Create a `template.json` manifest
|
|
297
|
+
4. Test the template thoroughly
|
|
298
|
+
5. Submit a pull request
|
|
299
|
+
|
|
300
|
+
## License
|
|
301
|
+
|
|
302
|
+
MIT © [Arya Labs, Inc.](../../LICENSE)
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
#!/usr/bin/env bun
|
|
3
|
+
// @bun
|
|
4
|
+
|
|
5
|
+
// src/cli.ts
|
|
6
|
+
import { createCLI, defineCommand, option } from "@bunli/core";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
|
|
9
|
+
// src/template-engine.ts
|
|
10
|
+
import { downloadTemplate } from "giget";
|
|
11
|
+
import { readdir } from "fs/promises";
|
|
12
|
+
import { join } from "path";
|
|
13
|
+
async function processTemplate(options) {
|
|
14
|
+
const { source, dir, offline, variables = {} } = options;
|
|
15
|
+
let templateDir;
|
|
16
|
+
if (source.startsWith("/") || source.startsWith("./") || source.startsWith("../")) {
|
|
17
|
+
const sourceDir = source.startsWith("/") ? source : join(process.cwd(), source);
|
|
18
|
+
await Bun.spawn(["cp", "-r", sourceDir + "/.", dir], {
|
|
19
|
+
stdout: "inherit",
|
|
20
|
+
stderr: "inherit"
|
|
21
|
+
}).exited;
|
|
22
|
+
templateDir = dir;
|
|
23
|
+
} else {
|
|
24
|
+
const result = await downloadTemplate(source, {
|
|
25
|
+
dir,
|
|
26
|
+
offline,
|
|
27
|
+
preferOffline: true,
|
|
28
|
+
force: true
|
|
29
|
+
});
|
|
30
|
+
templateDir = result.dir;
|
|
31
|
+
}
|
|
32
|
+
const manifest = await loadTemplateManifest(templateDir);
|
|
33
|
+
if (manifest?.files || Object.keys(variables).length > 0) {
|
|
34
|
+
await processTemplateFiles(templateDir, variables, manifest);
|
|
35
|
+
}
|
|
36
|
+
if (manifest?.hooks?.postInstall) {
|
|
37
|
+
await runPostInstallHooks(templateDir, manifest.hooks.postInstall);
|
|
38
|
+
}
|
|
39
|
+
return { dir: templateDir, manifest };
|
|
40
|
+
}
|
|
41
|
+
async function loadTemplateManifest(dir) {
|
|
42
|
+
const possiblePaths = [
|
|
43
|
+
join(dir, "template.json"),
|
|
44
|
+
join(dir, ".template.json"),
|
|
45
|
+
join(dir, "template.yaml"),
|
|
46
|
+
join(dir, ".template.yaml")
|
|
47
|
+
];
|
|
48
|
+
for (const path of possiblePaths) {
|
|
49
|
+
const file = Bun.file(path);
|
|
50
|
+
if (await file.exists()) {
|
|
51
|
+
const content = await file.text();
|
|
52
|
+
if (path.endsWith(".json")) {
|
|
53
|
+
const manifest = JSON.parse(content);
|
|
54
|
+
try {
|
|
55
|
+
await Bun.spawn(["rm", "-f", path], {
|
|
56
|
+
stdout: "ignore",
|
|
57
|
+
stderr: "ignore"
|
|
58
|
+
}).exited;
|
|
59
|
+
} catch {}
|
|
60
|
+
return manifest;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
async function processTemplateFiles(dir, variables, manifest) {
|
|
67
|
+
const files = await getFilesToProcess(dir, manifest);
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
const filePath = join(dir, file);
|
|
70
|
+
const content = await Bun.file(filePath).text();
|
|
71
|
+
let processedContent = content;
|
|
72
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
73
|
+
processedContent = processedContent.replaceAll(`{{${key}}}`, value).replaceAll(`<%= ${key} %>`, value).replaceAll(`$${key}`, value).replaceAll(`__${key}__`, value);
|
|
74
|
+
}
|
|
75
|
+
let newFilePath = filePath;
|
|
76
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
77
|
+
newFilePath = newFilePath.replaceAll(`__${key}__`, value);
|
|
78
|
+
}
|
|
79
|
+
await Bun.write(newFilePath, processedContent);
|
|
80
|
+
if (newFilePath !== filePath) {
|
|
81
|
+
await Bun.spawn(["rm", filePath]).exited;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function getFilesToProcess(dir, manifest) {
|
|
86
|
+
if (manifest?.files?.include) {
|
|
87
|
+
return manifest.files.include;
|
|
88
|
+
}
|
|
89
|
+
const files = [];
|
|
90
|
+
async function walk(currentDir, prefix = "") {
|
|
91
|
+
const entries = await readdir(currentDir, { withFileTypes: true });
|
|
92
|
+
for (const entry of entries) {
|
|
93
|
+
const path = join(prefix, entry.name);
|
|
94
|
+
if (entry.isDirectory()) {
|
|
95
|
+
if (!["node_modules", ".git", ".next", "dist", "build"].includes(entry.name)) {
|
|
96
|
+
await walk(join(currentDir, entry.name), path);
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
if (!path.match(/^(template\.json|\.template\.json|\.DS_Store|Thumbs\.db)$/)) {
|
|
100
|
+
files.push(path);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
await walk(dir);
|
|
106
|
+
return files;
|
|
107
|
+
}
|
|
108
|
+
async function runPostInstallHooks(dir, hooks) {
|
|
109
|
+
for (const hook of hooks) {
|
|
110
|
+
const proc = Bun.spawn(hook.split(" "), {
|
|
111
|
+
cwd: dir,
|
|
112
|
+
stdout: "inherit",
|
|
113
|
+
stderr: "inherit"
|
|
114
|
+
});
|
|
115
|
+
await proc.exited;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function resolveTemplateSource(template) {
|
|
119
|
+
const specialTemplates = {
|
|
120
|
+
basic: "github:bunli/templates/basic",
|
|
121
|
+
advanced: "github:bunli/templates/advanced",
|
|
122
|
+
monorepo: "github:bunli/templates/monorepo"
|
|
123
|
+
};
|
|
124
|
+
if (specialTemplates[template]) {
|
|
125
|
+
return specialTemplates[template];
|
|
126
|
+
}
|
|
127
|
+
if (template.startsWith("npm:")) {
|
|
128
|
+
return template.replace("npm:", "npm:/");
|
|
129
|
+
}
|
|
130
|
+
if (template.includes("/") && !template.includes(":")) {
|
|
131
|
+
return `github:${template}`;
|
|
132
|
+
}
|
|
133
|
+
return template;
|
|
134
|
+
}
|
|
135
|
+
function getBundledTemplatePath(name) {
|
|
136
|
+
return join(import.meta.dir, "..", "templates", name);
|
|
137
|
+
}
|
|
138
|
+
async function isLocalTemplate(template) {
|
|
139
|
+
if (template.startsWith("file:") || template.startsWith("./") || template.startsWith("../")) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
const bundledPath = getBundledTemplatePath(template);
|
|
143
|
+
return await Bun.file(join(bundledPath, "package.json")).exists();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/create-project.ts
|
|
147
|
+
async function createProject(options) {
|
|
148
|
+
const { name, dir, template, git, install, packageManager, prompt, spinner, colors, shell, offline } = options;
|
|
149
|
+
try {
|
|
150
|
+
await shell`test -d ${dir}`.quiet();
|
|
151
|
+
const overwrite = await prompt.confirm(`Directory ${dir} already exists. Overwrite?`, { default: false });
|
|
152
|
+
if (!overwrite) {
|
|
153
|
+
console.log(colors.red("Cancelled"));
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
await shell`rm -rf ${dir}`;
|
|
157
|
+
} catch {}
|
|
158
|
+
const spin = spinner("Creating project structure...");
|
|
159
|
+
spin.start();
|
|
160
|
+
await shell`mkdir -p ${dir}`;
|
|
161
|
+
try {
|
|
162
|
+
let templateSource = template;
|
|
163
|
+
if (await isLocalTemplate(template)) {
|
|
164
|
+
templateSource = getBundledTemplatePath(template);
|
|
165
|
+
} else {
|
|
166
|
+
templateSource = resolveTemplateSource(template);
|
|
167
|
+
}
|
|
168
|
+
const { manifest } = await processTemplate({
|
|
169
|
+
source: templateSource,
|
|
170
|
+
dir,
|
|
171
|
+
offline,
|
|
172
|
+
variables: {
|
|
173
|
+
projectName: name,
|
|
174
|
+
description: `A CLI built with Bunli`,
|
|
175
|
+
author: "",
|
|
176
|
+
packageManager: packageManager || "bun",
|
|
177
|
+
year: new Date().getFullYear().toString()
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
spin.succeed("Project structure created");
|
|
181
|
+
if (git) {
|
|
182
|
+
const gitSpin = spinner("Initializing git repository...");
|
|
183
|
+
gitSpin.start();
|
|
184
|
+
try {
|
|
185
|
+
await shell`cd ${dir} && git init`.quiet();
|
|
186
|
+
await shell`cd ${dir} && git add .`.quiet();
|
|
187
|
+
await shell`cd ${dir} && git commit -m "Initial commit"`.quiet();
|
|
188
|
+
gitSpin.succeed("Git repository initialized");
|
|
189
|
+
} catch (error) {
|
|
190
|
+
gitSpin.fail("Failed to initialize git repository");
|
|
191
|
+
console.error(colors.dim(` ${error}`));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (install) {
|
|
195
|
+
const installSpin = spinner(`Installing dependencies with ${packageManager}...`);
|
|
196
|
+
installSpin.start();
|
|
197
|
+
try {
|
|
198
|
+
const installCmd = packageManager === "bun" ? "bun install" : packageManager === "pnpm" ? "pnpm install" : packageManager === "yarn" ? "yarn install" : "npm install";
|
|
199
|
+
await shell`cd ${dir} && ${installCmd}`;
|
|
200
|
+
installSpin.succeed("Dependencies installed");
|
|
201
|
+
} catch (error) {
|
|
202
|
+
installSpin.fail("Failed to install dependencies");
|
|
203
|
+
console.error(colors.dim(` You can install them manually by running: ${packageManager} install`));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
} catch (error) {
|
|
207
|
+
spin.fail("Failed to create project");
|
|
208
|
+
console.error(colors.red(`Error: ${error}`));
|
|
209
|
+
try {
|
|
210
|
+
await shell`rm -rf ${dir}`.quiet();
|
|
211
|
+
} catch {}
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/create.ts
|
|
217
|
+
import path from "path";
|
|
218
|
+
async function create(context) {
|
|
219
|
+
const { flags, positional, prompt, spinner, colors, shell } = context;
|
|
220
|
+
let projectName = positional[0] || flags.name;
|
|
221
|
+
if (!projectName) {
|
|
222
|
+
projectName = await prompt("Project name:", {
|
|
223
|
+
validate: (value) => {
|
|
224
|
+
if (!value)
|
|
225
|
+
return "Project name is required";
|
|
226
|
+
if (!/^[a-z0-9-]+$/.test(value)) {
|
|
227
|
+
return "Project name must only contain lowercase letters, numbers, and hyphens";
|
|
228
|
+
}
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
} else if (!/^[a-z0-9-]+$/.test(projectName)) {
|
|
233
|
+
console.error(colors.red("Project name must only contain lowercase letters, numbers, and hyphens"));
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
const projectDir = flags.dir || path.join(process.cwd(), projectName);
|
|
237
|
+
console.log();
|
|
238
|
+
console.log(colors.bold("Creating Bunli project:"));
|
|
239
|
+
console.log(colors.dim(" Name: ") + colors.cyan(projectName));
|
|
240
|
+
console.log(colors.dim(" Template: ") + colors.cyan(flags.template));
|
|
241
|
+
console.log(colors.dim(" Location: ") + colors.cyan(projectDir));
|
|
242
|
+
console.log(colors.dim(" Git: ") + colors.cyan(flags.git ? "Yes" : "No"));
|
|
243
|
+
console.log(colors.dim(" Install: ") + colors.cyan(flags.install ? "Yes" : "No"));
|
|
244
|
+
console.log(colors.dim(" Package: ") + colors.cyan(flags["package-manager"]));
|
|
245
|
+
console.log();
|
|
246
|
+
const confirmed = await prompt.confirm("Continue?", { default: true });
|
|
247
|
+
if (!confirmed) {
|
|
248
|
+
console.log(colors.red("Cancelled"));
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
console.log();
|
|
252
|
+
await createProject({
|
|
253
|
+
name: projectName,
|
|
254
|
+
dir: projectDir,
|
|
255
|
+
template: flags.template,
|
|
256
|
+
git: flags.git,
|
|
257
|
+
install: flags.install,
|
|
258
|
+
packageManager: flags["package-manager"],
|
|
259
|
+
offline: flags.offline,
|
|
260
|
+
prompt,
|
|
261
|
+
spinner,
|
|
262
|
+
colors,
|
|
263
|
+
shell
|
|
264
|
+
});
|
|
265
|
+
console.log();
|
|
266
|
+
console.log(colors.green("\u2728 Project created successfully!"));
|
|
267
|
+
console.log();
|
|
268
|
+
console.log("Next steps:");
|
|
269
|
+
console.log(colors.gray(` cd ${path.relative(process.cwd(), projectDir)}`));
|
|
270
|
+
if (!flags.install) {
|
|
271
|
+
console.log(colors.gray(` ${flags["package-manager"]} install`));
|
|
272
|
+
}
|
|
273
|
+
if (flags.template === "monorepo") {
|
|
274
|
+
console.log(colors.gray(` ${flags["package-manager"]} run dev`));
|
|
275
|
+
} else {
|
|
276
|
+
console.log(colors.gray(` ${flags["package-manager"] === "bun" ? "bun" : flags["package-manager"] + " run"} dev`));
|
|
277
|
+
}
|
|
278
|
+
console.log();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// src/cli.ts
|
|
282
|
+
async function run() {
|
|
283
|
+
const args = process.argv.slice(2);
|
|
284
|
+
if (args.length === 0 || args[0]?.startsWith("-")) {
|
|
285
|
+
process.argv.splice(2, 0, "create");
|
|
286
|
+
} else if (args[0] && !args[0].startsWith("-") && args[0] !== "create") {
|
|
287
|
+
process.argv.splice(2, 0, "create");
|
|
288
|
+
}
|
|
289
|
+
const cli = createCLI({
|
|
290
|
+
name: "create-bunli",
|
|
291
|
+
version: "0.1.0",
|
|
292
|
+
description: "Scaffold new Bunli CLI projects"
|
|
293
|
+
});
|
|
294
|
+
cli.command(defineCommand({
|
|
295
|
+
name: "create",
|
|
296
|
+
description: "Create a new Bunli CLI project",
|
|
297
|
+
options: {
|
|
298
|
+
name: option(z.string().optional(), { description: "Project name" }),
|
|
299
|
+
template: option(z.string().default("basic"), { short: "t", description: "Project template (basic, advanced, monorepo, or github:user/repo)" }),
|
|
300
|
+
dir: option(z.string().optional(), { short: "d", description: "Directory to create project in" }),
|
|
301
|
+
git: option(z.boolean().default(true), { short: "g", description: "Initialize git repository" }),
|
|
302
|
+
install: option(z.boolean().default(true), { short: "i", description: "Install dependencies" }),
|
|
303
|
+
"package-manager": option(z.enum(["bun", "pnpm", "yarn", "npm"]).default("bun"), { short: "p", description: "Package manager to use" }),
|
|
304
|
+
offline: option(z.boolean().default(false), { description: "Use cached templates when available" })
|
|
305
|
+
},
|
|
306
|
+
handler: create
|
|
307
|
+
}));
|
|
308
|
+
await cli.run();
|
|
309
|
+
}
|
|
310
|
+
await run();
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BunliUtils } from '@bunli/utils';
|
|
2
|
+
import type { CreateOptions } from './types.js';
|
|
3
|
+
interface CreateProjectOptions extends CreateOptions {
|
|
4
|
+
name: string;
|
|
5
|
+
dir: string;
|
|
6
|
+
template: string;
|
|
7
|
+
prompt: BunliUtils['prompt'];
|
|
8
|
+
spinner: BunliUtils['spinner'];
|
|
9
|
+
colors: BunliUtils['colors'];
|
|
10
|
+
shell: typeof Bun.$;
|
|
11
|
+
}
|
|
12
|
+
export declare function createProject(options: CreateProjectOptions): Promise<void>;
|
|
13
|
+
export {};
|
package/dist/create.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HandlerArgs } from '@bunli/core';
|
|
2
|
+
import type { PackageManager } from './types.js';
|
|
3
|
+
interface CreateOptions {
|
|
4
|
+
name?: string;
|
|
5
|
+
template: string;
|
|
6
|
+
dir?: string;
|
|
7
|
+
git: boolean;
|
|
8
|
+
install: boolean;
|
|
9
|
+
'package-manager': PackageManager;
|
|
10
|
+
offline?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function create(context: HandlerArgs<CreateOptions>): Promise<void>;
|
|
13
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createProject } from './create-project.js';
|
|
2
|
+
export { processTemplate, resolveTemplateSource, isLocalTemplate } from './template-engine.js';
|
|
3
|
+
export type { CreateOptions, ProjectConfig, TemplateManifest, TemplateVariable } from './types.js';
|
|
4
|
+
export declare const version = "0.1.0";
|