Initial commit
This commit is contained in:
115
skills/gluegun-patterns/examples/basic-cli/README.md
Normal file
115
skills/gluegun-patterns/examples/basic-cli/README.md
Normal 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
|
||||
27
skills/gluegun-patterns/examples/basic-cli/src/cli.ts
Normal file
27
skills/gluegun-patterns/examples/basic-cli/src/cli.ts
Normal 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 }
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 %>
|
||||
252
skills/gluegun-patterns/examples/plugin-system/README.md
Normal file
252
skills/gluegun-patterns/examples/plugin-system/README.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# Gluegun Plugin System Example
|
||||
|
||||
Demonstrates how to build an extensible CLI with plugin architecture.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
plugin-system/
|
||||
├── src/
|
||||
│ ├── cli.ts # CLI with plugin loading
|
||||
│ ├── commands/
|
||||
│ │ └── plugin.ts # Plugin management command
|
||||
│ └── plugins/
|
||||
│ ├── custom-plugin.ts # Example plugin
|
||||
│ └── validator-plugin.ts # Validation plugin
|
||||
├── package.json
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Plugin discovery and loading
|
||||
- Custom toolbox extensions via plugins
|
||||
- Plugin-provided commands
|
||||
- Plugin configuration management
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Load Plugins
|
||||
|
||||
```bash
|
||||
# CLI automatically loads plugins from:
|
||||
# - ./plugins/
|
||||
# - ./node_modules/mycli-*
|
||||
```
|
||||
|
||||
### Use Plugin Commands
|
||||
|
||||
```bash
|
||||
# Commands added by plugins
|
||||
./bin/cli validate
|
||||
./bin/cli custom-action
|
||||
```
|
||||
|
||||
### Use Plugin Extensions
|
||||
|
||||
```typescript
|
||||
// In any command
|
||||
const { myPlugin } = toolbox
|
||||
myPlugin.doSomething()
|
||||
```
|
||||
|
||||
## Creating a Plugin
|
||||
|
||||
### Basic Plugin Structure
|
||||
|
||||
```typescript
|
||||
module.exports = (toolbox) => {
|
||||
const { print } = toolbox
|
||||
|
||||
// Add to toolbox
|
||||
toolbox.myPlugin = {
|
||||
version: '1.0.0',
|
||||
doSomething: () => {
|
||||
print.info('Plugin action executed')
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Plugin with Commands
|
||||
|
||||
```typescript
|
||||
module.exports = (toolbox) => {
|
||||
const { runtime } = toolbox
|
||||
|
||||
runtime.addPlugin({
|
||||
name: 'my-plugin',
|
||||
commands: [
|
||||
{
|
||||
name: 'my-command',
|
||||
run: async (toolbox) => {
|
||||
// Command implementation
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Plugin Discovery
|
||||
|
||||
The CLI looks for plugins in:
|
||||
|
||||
1. **Local plugins directory**: `./plugins/*.js`
|
||||
2. **Node modules**: `./node_modules/mycli-*`
|
||||
3. **Scoped packages**: `@scope/mycli-*`
|
||||
|
||||
## Plugin Naming Convention
|
||||
|
||||
- Local plugins: Any `.js` or `.ts` file in `plugins/`
|
||||
- NPM plugins: Must match pattern `mycli-*`
|
||||
- Example: `mycli-validator`, `@myorg/mycli-helper`
|
||||
|
||||
## Key Patterns
|
||||
|
||||
### 1. Load Plugins from Directory
|
||||
|
||||
```typescript
|
||||
cli.plugins('./plugins', { matching: '*.js' })
|
||||
```
|
||||
|
||||
### 2. Load NPM Plugins
|
||||
|
||||
```typescript
|
||||
cli.plugins('./node_modules', {
|
||||
matching: 'mycli-*',
|
||||
hidden: true
|
||||
})
|
||||
```
|
||||
|
||||
### 3. Add Toolbox Extension
|
||||
|
||||
```typescript
|
||||
toolbox.validator = {
|
||||
validate: (data) => { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Register Commands
|
||||
|
||||
```typescript
|
||||
runtime.addPlugin({
|
||||
name: 'my-plugin',
|
||||
commands: [/* commands */]
|
||||
})
|
||||
```
|
||||
|
||||
## Example Plugins
|
||||
|
||||
### custom-plugin.ts
|
||||
|
||||
Adds custom utilities to toolbox.
|
||||
|
||||
```typescript
|
||||
toolbox.custom = {
|
||||
formatDate: (date) => { /* ... */ },
|
||||
parseConfig: (file) => { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
### validator-plugin.ts
|
||||
|
||||
Adds validation command and utilities.
|
||||
|
||||
```typescript
|
||||
toolbox.validator = {
|
||||
validateFile: (path) => { /* ... */ },
|
||||
validateSchema: (data) => { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
## Publishing Plugins
|
||||
|
||||
### 1. Create NPM Package
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "mycli-myplugin",
|
||||
"main": "dist/index.js",
|
||||
"keywords": ["mycli", "plugin"]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Export Plugin
|
||||
|
||||
```typescript
|
||||
module.exports = (toolbox) => {
|
||||
// Plugin implementation
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Publish
|
||||
|
||||
```bash
|
||||
npm publish
|
||||
```
|
||||
|
||||
### 4. Install and Use
|
||||
|
||||
```bash
|
||||
npm install mycli-myplugin
|
||||
# Automatically loaded by CLI
|
||||
```
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### Conditional Plugin Loading
|
||||
|
||||
```typescript
|
||||
if (config.enablePlugin) {
|
||||
cli.plugins('./plugins/optional')
|
||||
}
|
||||
```
|
||||
|
||||
### Plugin Configuration
|
||||
|
||||
```typescript
|
||||
toolbox.myPlugin = {
|
||||
config: await filesystem.read('.mypluginrc', 'json'),
|
||||
// Plugin methods
|
||||
}
|
||||
```
|
||||
|
||||
### Plugin Dependencies
|
||||
|
||||
```typescript
|
||||
module.exports = (toolbox) => {
|
||||
// Check for required plugins
|
||||
if (!toolbox.otherPlugin) {
|
||||
throw new Error('Requires other-plugin')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Namespace Extensions**: Use unique names for toolbox extensions
|
||||
2. **Document APIs**: Provide clear documentation for plugin methods
|
||||
3. **Handle Errors**: Validate inputs and handle failures gracefully
|
||||
4. **Version Plugins**: Use semantic versioning
|
||||
5. **Test Plugins**: Write tests for plugin functionality
|
||||
|
||||
## Testing Plugins
|
||||
|
||||
```typescript
|
||||
import { build } from 'gluegun'
|
||||
|
||||
test('plugin loads correctly', async () => {
|
||||
const cli = build().src(__dirname).plugins('./plugins').create()
|
||||
const toolbox = await cli.run()
|
||||
|
||||
expect(toolbox.myPlugin).toBeDefined()
|
||||
})
|
||||
```
|
||||
322
skills/gluegun-patterns/examples/template-generator/README.md
Normal file
322
skills/gluegun-patterns/examples/template-generator/README.md
Normal file
@@ -0,0 +1,322 @@
|
||||
# Template Generator Example
|
||||
|
||||
Advanced template patterns for Gluegun CLI generators.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
template-generator/
|
||||
├── src/
|
||||
│ └── commands/
|
||||
│ ├── new-app.ts # Multi-file generator
|
||||
│ ├── new-feature.ts # Feature scaffold
|
||||
│ └── new-config.ts # Config generator
|
||||
├── templates/
|
||||
│ ├── app/
|
||||
│ │ ├── package.json.ejs
|
||||
│ │ ├── tsconfig.json.ejs
|
||||
│ │ ├── src/
|
||||
│ │ │ └── index.ts.ejs
|
||||
│ │ └── README.md.ejs
|
||||
│ ├── feature/
|
||||
│ │ ├── component.tsx.ejs
|
||||
│ │ ├── test.spec.ts.ejs
|
||||
│ │ └── styles.css.ejs
|
||||
│ └── config/
|
||||
│ └── config.json.ejs
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Multi-file generation from template sets
|
||||
- Conditional template sections
|
||||
- Nested directory creation
|
||||
- Template composition
|
||||
- Helper functions for common operations
|
||||
|
||||
## Patterns
|
||||
|
||||
### 1. Multi-File Generation
|
||||
|
||||
Generate complete project structure:
|
||||
|
||||
```typescript
|
||||
await template.generate({
|
||||
template: 'app/package.json.ejs',
|
||||
target: `${name}/package.json`,
|
||||
props: { name, version }
|
||||
})
|
||||
|
||||
await template.generate({
|
||||
template: 'app/src/index.ts.ejs',
|
||||
target: `${name}/src/index.ts`,
|
||||
props: { name }
|
||||
})
|
||||
```
|
||||
|
||||
### 2. Conditional Templates
|
||||
|
||||
Templates with conditional sections:
|
||||
|
||||
```ejs
|
||||
<% if (includeTests) { %>
|
||||
import { test } from 'vitest'
|
||||
|
||||
test('<%= name %> works', () => {
|
||||
// Test implementation
|
||||
})
|
||||
<% } %>
|
||||
```
|
||||
|
||||
### 3. Template Loops
|
||||
|
||||
Generate repetitive structures:
|
||||
|
||||
```ejs
|
||||
<% features.forEach(feature => { %>
|
||||
export { <%= feature.name %> } from './<%= feature.path %>'
|
||||
<% }) %>
|
||||
```
|
||||
|
||||
### 4. Nested Templates
|
||||
|
||||
Organize templates in directories:
|
||||
|
||||
```
|
||||
templates/
|
||||
├── react-app/
|
||||
│ ├── components/
|
||||
│ │ └── Component.tsx.ejs
|
||||
│ ├── pages/
|
||||
│ │ └── Page.tsx.ejs
|
||||
│ └── layouts/
|
||||
│ └── Layout.tsx.ejs
|
||||
```
|
||||
|
||||
### 5. Template Helpers
|
||||
|
||||
Use helper functions in templates:
|
||||
|
||||
```ejs
|
||||
/**
|
||||
* <%= helpers.titleCase(name) %> Component
|
||||
* File: <%= helpers.kebabCase(name) %>.ts
|
||||
*/
|
||||
|
||||
export class <%= helpers.pascalCase(name) %> {
|
||||
// Implementation
|
||||
}
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
### new-app
|
||||
|
||||
Create complete application structure:
|
||||
|
||||
```bash
|
||||
./bin/cli new-app my-project --typescript --tests
|
||||
```
|
||||
|
||||
Options:
|
||||
- `--typescript`: Include TypeScript configuration
|
||||
- `--tests`: Include test setup
|
||||
- `--git`: Initialize git repository
|
||||
- `--install`: Run npm install
|
||||
|
||||
### new-feature
|
||||
|
||||
Scaffold a new feature with tests and styles:
|
||||
|
||||
```bash
|
||||
./bin/cli new-feature UserProfile --redux --tests
|
||||
```
|
||||
|
||||
Options:
|
||||
- `--redux`: Include Redux setup
|
||||
- `--tests`: Generate test files
|
||||
- `--styles`: Include CSS/SCSS files
|
||||
|
||||
### new-config
|
||||
|
||||
Generate configuration files:
|
||||
|
||||
```bash
|
||||
./bin/cli new-config --eslint --prettier --jest
|
||||
```
|
||||
|
||||
Options:
|
||||
- `--eslint`: ESLint configuration
|
||||
- `--prettier`: Prettier configuration
|
||||
- `--jest`: Jest configuration
|
||||
- `--typescript`: TypeScript configuration
|
||||
|
||||
## Template Variables
|
||||
|
||||
### Common Props
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: string // Component/project name
|
||||
description: string // Description
|
||||
author: string // Author name
|
||||
version: string // Version number
|
||||
timestamp: string // ISO timestamp
|
||||
year: number // Current year
|
||||
}
|
||||
```
|
||||
|
||||
### Feature-Specific Props
|
||||
|
||||
```typescript
|
||||
{
|
||||
// React component
|
||||
componentType: 'class' | 'function'
|
||||
withHooks: boolean
|
||||
withState: boolean
|
||||
|
||||
// Configuration
|
||||
includeESLint: boolean
|
||||
includePrettier: boolean
|
||||
includeTests: boolean
|
||||
|
||||
// Dependencies
|
||||
dependencies: string[]
|
||||
devDependencies: string[]
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced Techniques
|
||||
|
||||
### 1. Template Composition
|
||||
|
||||
Combine multiple templates:
|
||||
|
||||
```typescript
|
||||
// Base component template
|
||||
await template.generate({
|
||||
template: 'base/component.ts.ejs',
|
||||
target: 'src/components/Base.ts',
|
||||
props: baseProps
|
||||
})
|
||||
|
||||
// Extended component using base
|
||||
await template.generate({
|
||||
template: 'extended/component.ts.ejs',
|
||||
target: 'src/components/Extended.ts',
|
||||
props: { ...baseProps, extends: 'Base' }
|
||||
})
|
||||
```
|
||||
|
||||
### 2. Dynamic Template Selection
|
||||
|
||||
Choose templates based on user input:
|
||||
|
||||
```typescript
|
||||
const framework = await prompt.select('Framework:', [
|
||||
'React',
|
||||
'Vue',
|
||||
'Angular'
|
||||
])
|
||||
|
||||
const templatePath = `${framework.toLowerCase()}/component.ejs`
|
||||
await template.generate({
|
||||
template: templatePath,
|
||||
target: `src/components/${name}.tsx`,
|
||||
props: { name }
|
||||
})
|
||||
```
|
||||
|
||||
### 3. Template Preprocessing
|
||||
|
||||
Modify props before generation:
|
||||
|
||||
```typescript
|
||||
const props = {
|
||||
name,
|
||||
pascalName: strings.pascalCase(name),
|
||||
kebabName: strings.kebabCase(name),
|
||||
dependencies: dependencies.sort(),
|
||||
imports: generateImports(dependencies)
|
||||
}
|
||||
|
||||
await template.generate({
|
||||
template: 'component.ts.ejs',
|
||||
target: `src/${props.kebabName}.ts`,
|
||||
props
|
||||
})
|
||||
```
|
||||
|
||||
### 4. Post-Generation Actions
|
||||
|
||||
Run actions after template generation:
|
||||
|
||||
```typescript
|
||||
// Generate files
|
||||
await template.generate({ /* ... */ })
|
||||
|
||||
// Format generated code
|
||||
await system.run('npm run format')
|
||||
|
||||
// Run initial build
|
||||
await system.run('npm run build')
|
||||
|
||||
// Initialize git
|
||||
if (options.git) {
|
||||
await system.run('git init')
|
||||
await system.run('git add .')
|
||||
await system.run('git commit -m "Initial commit"')
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Template Validation
|
||||
|
||||
Validate templates before generation:
|
||||
|
||||
```typescript
|
||||
// Check if template exists
|
||||
if (!filesystem.exists(`templates/${templatePath}`)) {
|
||||
print.error(`Template not found: ${templatePath}`)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate template syntax
|
||||
const templateContent = await filesystem.read(`templates/${templatePath}`)
|
||||
if (!validateEJS(templateContent)) {
|
||||
print.error('Invalid EJS syntax in template')
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Organize Templates**: Group related templates in directories
|
||||
2. **Use Helpers**: Extract common logic into helper functions
|
||||
3. **Validate Input**: Check user input before generation
|
||||
4. **Provide Feedback**: Show progress with spinners
|
||||
5. **Handle Errors**: Gracefully handle missing templates
|
||||
6. **Document Variables**: Comment template variables
|
||||
7. **Test Templates**: Generate samples to verify output
|
||||
|
||||
## Testing Generated Code
|
||||
|
||||
```bash
|
||||
# Generate sample
|
||||
./bin/cli new-app test-project --all
|
||||
|
||||
# Verify structure
|
||||
cd test-project
|
||||
npm install
|
||||
npm test
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Template Development Workflow
|
||||
|
||||
1. Create template file with `.ejs` extension
|
||||
2. Add template variables with `<%= %>` syntax
|
||||
3. Test template with sample props
|
||||
4. Add conditional sections with `<% if %>` blocks
|
||||
5. Validate generated output
|
||||
6. Document template variables and options
|
||||
Reference in New Issue
Block a user