cliUtils Service
Interface
The service creates an object with utility methods for command-line interface operations:
this.cliUtils.normalizeFields(fields)
Methods
normalizeFields(fields)
- Parses and normalizes field specification strings into structured field objects
Parameters
fields
(string|array) - Field specification string or array to be normalized
Return Value
Returns an array of normalized field objects, each containing:
name
(string) - Camelized field nametype
(string) - Field type (defaults to 'string')mandatory
(boolean) - Whether the field is required (defaults to false)
Description
The cliUtils
service provides utilities for parsing and processing command-line arguments, particularly field specifications used in code generation commands. It's primarily designed to work with CLI commands that generate database migrations, models, and other structured code files.
The service's main function is to parse field specification strings that follow a specific syntax pattern and convert them into structured objects that can be used by code generators.
Key Features
- Field Specification Parsing: Converts string-based field definitions into structured objects
- Type Detection: Automatically detects and assigns field types from colon-separated specifications
- Mandatory Field Support: Recognizes
^
prefix for required/mandatory fields - Inflection Integration: Automatically camelizes field names using the inflector service
- Default Value Assignment: Provides sensible defaults for missing properties
Field Specification Syntax
The normalizeFields
method supports a specific syntax for defining fields:
- Basic field:
fieldName
- Creates a string field - Typed field:
fieldName:type
- Creates a field with specific type - Mandatory field:
^fieldName
- Creates a required string field - Mandatory typed field:
^fieldName:type
- Creates a required field with specific type - Multiple fields:
field1 ^field2:integer field3:text
- Space-separated field definitions
Examples
Basic Field Parsing
// Simple field names
const fields = this.cliUtils.normalizeFields('name email');
// Result: [
// { name: 'name', type: 'string', mandatory: false },
// { name: 'email', type: 'string', mandatory: false }
// ]
// Empty input
const empty = this.cliUtils.normalizeFields('');
// Result: []
Typed Fields
// Fields with types
const typedFields = this.cliUtils.normalizeFields('name:string age:integer active:boolean');
// Result: [
// { name: 'name', type: 'string', mandatory: false },
// { name: 'age', type: 'integer', mandatory: false },
// { name: 'active', type: 'boolean', mandatory: false }
// ]
Mandatory Fields
// Required fields with ^ prefix
const mandatoryFields = this.cliUtils.normalizeFields('^title content:text ^author_id:integer');
// Result: [
// { name: 'title', type: 'string', mandatory: true },
// { name: 'content', type: 'text', mandatory: false },
// { name: 'authorId', type: 'integer', mandatory: true }
// ]
Database Migration Generation
// Generate migration command usage
export default {
async run(){
const { fields = '' } = this.params;
const normalizedFields = this.cliUtils.normalizeFields(fields);
// Generate migration with normalized fields
await generateFile(`lib/migrations/${timestamp}_${suffix}.js`, ({ line, indent }) => {
line('export default {');
indent(({ line, indent }) => {
line('async migrate(){');
indent(({ line, indent }) => {
if(table && normalizedFields.length){
line(`await this.database.table('${table}', async ${table} => {`);
indent(({ line }) => {
normalizedFields.forEach(({ name, type }) => {
line(`await ${table}.addColumn('${name}', '${type}');`);
});
});
line('});');
}
});
line('}');
});
line('};');
});
}
}
Model Generation
// Model generation with fields
export default {
async run(){
const { name, fields = '' } = this.params;
const normalizedFields = this.cliUtils.normalizeFields(fields);
// Create migration first
if(!await this.database[collectionName]){
await this.runCommand('generate-migration', {
suffix: `create_${name}`,
fields,
table: collectionName
});
}
// Generate model file
await generateFile(`lib/models/${name}.js`, ({ line, indent }) => {
line('export default {');
indent(({ line, indent }) => {
if(normalizedFields.length){
line('validations(){');
indent(({ line }) => {
normalizedFields.forEach(({ name, type, mandatory }) => {
if(mandatory){
line(`this.validates('${name}', { presence: true });`);
}
if(type === 'email'){
line(`this.validates('${name}', { format: 'email' });`);
}
});
});
line('}');
}
});
line('};');
});
}
}
Form Generation
// Generate form fields from CLI specification
export default {
async run(){
const { fields = '' } = this.params;
const normalizedFields = this.cliUtils.normalizeFields(fields);
const formFields = normalizedFields.map(({ name, type, mandatory }) => ({
name,
type: mapCliTypeToFormType(type),
required: mandatory,
label: this.inflector.humanize(name)
}));
return this.renderForm({ fields: formFields });
}
}
function mapCliTypeToFormType(cliType){
const typeMap = {
'string': 'text',
'text': 'textarea',
'integer': 'number',
'boolean': 'checkbox',
'email': 'email',
'password': 'password'
};
return typeMap[cliType] || 'text';
}
Complex Field Processing
// Advanced field processing with validation
export default {
async processFields(fieldString){
const normalizedFields = this.cliUtils.normalizeFields(fieldString);
// Validate field specifications
const validTypes = ['string', 'text', 'integer', 'float', 'boolean', 'date', 'email'];
const invalidFields = normalizedFields.filter(field =>
!validTypes.includes(field.type)
);
if(invalidFields.length){
throw new Error(`Invalid field types: ${invalidFields.map(f => f.type).join(', ')}`);
}
// Process fields for different contexts
return {
databaseColumns: normalizedFields.map(({ name, type }) => ({
name: this.inflector.snakeify(name),
type: mapToSqlType(type)
})),
validationRules: normalizedFields
.filter(({ mandatory }) => mandatory)
.map(({ name }) => ({ field: name, rule: 'presence' })),
formFields: normalizedFields.map(({ name, type, mandatory }) => ({
name,
type: mapToInputType(type),
required: mandatory,
label: this.inflector.titleize(name)
}))
};
}
}
Common Use Cases
Code Generation
- Database migrations: Parse field specifications for creating database tables
- Model generation: Define model attributes and validations
- Form creation: Generate form fields from command-line specifications
- API scaffolding: Create structured API endpoints with typed parameters
CLI Command Processing
- Parameter parsing: Convert command-line field arguments to structured objects
- Validation setup: Extract mandatory field requirements
- Type conversion: Map CLI types to application-specific types
- Template generation: Use normalized fields in code templates
Development Workflow
- Rapid prototyping: Quickly define data structures from command line
- Schema evolution: Add fields to existing models and migrations
- Testing setup: Generate test fixtures with properly typed fields
- Documentation: Create field specifications for API documentation
Integration Patterns
The cliUtils
service is typically used within:
- Command classes for processing CLI arguments
- Code generators for creating structured files
- Migration systems for database schema changes
- Template engines for generating boilerplate code
Performance Notes
- Field parsing uses efficient regex matching for syntax analysis
- String processing is optimized for CLI-scale input (typically small strings)
- Output objects are lightweight with minimal memory overhead
- Camelization is handled by the inflector service for consistency
Error Handling
The service handles edge cases gracefully:
- Empty or undefined input returns empty array
- Invalid syntax is parsed with best-effort approach
- Missing type specifications default to 'string'
- Malformed field names are processed through inflector
Return Value Details
Each normalized field object contains:
{
name: 'camelizedFieldName', // Camelized version of the input name
type: 'string', // Specified type or 'string' default
mandatory: false // true if prefixed with ^, false otherwise
}
The service ensures consistent field naming through automatic camelization and provides sensible defaults for all properties.