Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 09:04:14 +08:00
commit 70c36b5eff
248 changed files with 47482 additions and 0 deletions

View File

@@ -0,0 +1,115 @@
# Basic Gluegun CLI Example
A simple example demonstrating core Gluegun CLI patterns.
## Structure
```
basic-cli/
├── src/
│ ├── cli.ts # CLI entry point
│ ├── commands/
│ │ ├── hello.ts # Simple command
│ │ └── generate.ts # Template generator
│ └── extensions/
│ └── helpers.ts # Custom toolbox extension
├── templates/
│ └── component.ts.ejs # Example template
├── package.json
└── tsconfig.json
```
## Features
- Basic command structure
- Template generation
- Custom toolbox extensions
- TypeScript support
## Installation
```bash
npm install
npm run build
```
## Usage
```bash
# Run hello command
./bin/cli hello World
# Generate from template
./bin/cli generate MyComponent
# Show help
./bin/cli --help
```
## Commands
### hello
Simple greeting command demonstrating parameters.
```bash
./bin/cli hello [name]
```
### generate
Generate files from templates.
```bash
./bin/cli generate <name>
```
## Key Patterns
### 1. Command Structure
```typescript
const command: GluegunCommand = {
name: 'hello',
run: async (toolbox) => {
const { print, parameters } = toolbox
const name = parameters.first || 'World'
print.success(`Hello, ${name}!`)
}
}
```
### 2. Template Generation
```typescript
await template.generate({
template: 'component.ts.ejs',
target: `src/components/${name}.ts`,
props: { name }
})
```
### 3. Custom Extensions
```typescript
toolbox.helpers = {
formatName: (name: string) => {
return name.charAt(0).toUpperCase() + name.slice(1)
}
}
```
## Learning Path
1. Start with `src/cli.ts` - CLI initialization
2. Review `src/commands/hello.ts` - Simple command
3. Study `src/commands/generate.ts` - Template usage
4. Explore `templates/component.ts.ejs` - EJS templates
5. Check `src/extensions/helpers.ts` - Custom toolbox
## Next Steps
- Add more commands
- Create complex templates
- Implement plugin system
- Add interactive prompts

View File

@@ -0,0 +1,27 @@
import { build } from 'gluegun'
/**
* Create the CLI and kick it off
*/
async function run(argv: string[] = process.argv) {
// Create a CLI runtime
const cli = build()
.brand('mycli')
.src(__dirname)
.plugins('./node_modules', { matching: 'mycli-*', hidden: true })
.help() // provides default --help command
.version() // provides default --version command
.create()
// Enable the following method if you'd like to skip loading one of these core extensions
// this can improve performance if they're not necessary for your project:
// .exclude(['meta', 'strings', 'print', 'filesystem', 'semver', 'system', 'prompt', 'http', 'template', 'patching', 'package-manager'])
// Run the CLI
const toolbox = await cli.run(argv)
// Send it back (for testing, mostly)
return toolbox
}
module.exports = { run }

View File

@@ -0,0 +1,88 @@
import { GluegunCommand } from 'gluegun'
/**
* Generate Command
* Demonstrates template generation and filesystem operations
*/
const command: GluegunCommand = {
name: 'generate',
alias: ['g', 'create'],
description: 'Generate a new component from template',
run: async (toolbox) => {
const { template, print, parameters, filesystem, strings } = toolbox
// Get component name
const name = parameters.first
if (!name) {
print.error('Component name is required')
print.info('Usage: mycli generate <ComponentName>')
return
}
// Convert to different cases
const pascalName = strings.pascalCase(name)
const kebabName = strings.kebabCase(name)
// Target directory
const targetDir = 'src/components'
const targetFile = `${targetDir}/${kebabName}.ts`
// Ensure directory exists
await filesystem.dir(targetDir)
// Check if file already exists
if (filesystem.exists(targetFile)) {
const overwrite = await toolbox.prompt.confirm(
`${targetFile} already exists. Overwrite?`
)
if (!overwrite) {
print.warning('Generation cancelled')
return
}
}
// Show spinner while generating
const spinner = print.spin(`Generating ${pascalName} component...`)
try {
// Generate from template
await template.generate({
template: 'component.ts.ejs',
target: targetFile,
props: {
name: pascalName,
kebabName,
timestamp: new Date().toISOString()
}
})
spinner.succeed(`Generated ${targetFile}`)
// Add to index if it exists
const indexPath = `${targetDir}/index.ts`
if (filesystem.exists(indexPath)) {
await filesystem.append(
indexPath,
`export { ${pascalName} } from './${kebabName}'\n`
)
print.info(`Added export to ${indexPath}`)
}
// Success message with details
print.success('Component generated successfully!')
print.info('')
print.info('Next steps:')
print.info(` 1. Edit ${targetFile}`)
print.info(` 2. Import in your app: import { ${pascalName} } from './components/${kebabName}'`)
} catch (error) {
spinner.fail('Generation failed')
print.error(error.message)
}
}
}
module.exports = command

View File

@@ -0,0 +1,41 @@
import { GluegunCommand } from 'gluegun'
/**
* Hello Command
* Demonstrates basic parameter handling and print utilities
*/
const command: GluegunCommand = {
name: 'hello',
alias: ['hi', 'greet'],
description: 'Say hello to someone',
run: async (toolbox) => {
const { print, parameters } = toolbox
// Get name from first parameter
const name = parameters.first || 'World'
// Get options
const options = parameters.options
const loud = options.loud || options.l
// Format message
let message = `Hello, ${name}!`
if (loud) {
message = message.toUpperCase()
}
// Display with appropriate style
print.success(message)
// Show some additional info if verbose
if (options.verbose || options.v) {
print.info('Command executed successfully')
print.info(`Parameters: ${JSON.stringify(parameters.array)}`)
print.info(`Options: ${JSON.stringify(options)}`)
}
}
}
module.exports = command

View File

@@ -0,0 +1,23 @@
/**
* <%= name %> Component
* Generated: <%= timestamp %>
*/
export interface <%= name %>Props {
// Add your props here
}
export class <%= name %> {
private props: <%= name %>Props
constructor(props: <%= name %>Props) {
this.props = props
}
public render(): void {
console.log('<%= name %> rendered')
}
}
// Export for convenience
export default <%= name %>