Command Module Optimization Pattern
Performance Impact
Before optimization: ~1567ms
After optimization: ~916ms
Improvement: 651ms (41.5% faster!)
The Problem
Command modules were importing the full base.js at module level just to access metadata utilities like:
base.bundle.getText()- for internationalized stringsbase.getBuilder()- for yargs builder configuration
This caused heavy dependencies to load even for simple --help commands:
sap-hdb-promisfied(180ms)@inquirer/prompts(176ms)terminal-kit(138ms)ora(70ms)
The Solution
Separate command metadata (loaded early) from implementation (loaded on execution):
1. Use base-lite.js for Metadata
javascript
// OLD - loads all heavy dependencies
import * as base from '../utils/base.js'
export const describe = base.bundle.getText("tables")
export const builder = base.getBuilder({ /* options */ })
// NEW - only loads essentials
import * as baseLite from '../utils/base-lite.js'
export const describe = baseLite.bundle.getText("tables")
export const builder = baseLite.getBuilder({ /* options */ })2. Lazy-Load Heavy Modules in Handler
javascript
// OLD - imported at module level
import * as base from '../utils/base.js'
import dbClientClass from "../utils/database/index.js"
export async function handler(argv) {
base.promptHandler(argv, getTables, inputPrompts)
}
// NEW - imported only when handler executes
export async function handler(argv) {
const base = await import('../utils/base.js')
base.promptHandler(argv, getTables, inputPrompts)
}3. Lazy-Load in Implementation Functions
javascript
// OLD - uses module-level imports
export async function getTables(prompts) {
base.debug('getTables')
const dbClient = await dbClientClass.getNewClient(prompts)
// ...
}
// NEW - imports only when function executes
export async function getTables(prompts) {
const base = await import('../utils/base.js')
const dbClientModule = await import("../utils/database/index.js")
const dbClientClass = dbClientModule.default
base.debug('getTables')
const dbClient = await dbClientClass.getNewClient(prompts)
// ...
}Complete Example: bin/tables.js
See bin/tables.js for the complete optimized implementation.
When to Apply This Pattern
Apply this optimization to any command module that:
- Uses
base.bundle.getText()orbase.getBuilder() - Imports heavy dependencies like database clients, UI libraries, etc.
- Is frequently accessed (common commands benefit most)
Benefits
- Faster CLI startup - Only loads what's needed for the requested operation
- Lower memory footprint - Heavy modules stay unloaded for simple operations
- Better user experience - Especially noticeable on Windows where Node.js startup is slower
- Backwards compatible - No changes to command behavior or API
Files Modified
Core Files
utils/base-lite.js- Lightweight utilities (bundle, getBuilder, colors, debug)bin/cli.js- Lazy command loading based on requested commandbin/index.js- Proper promise resolution for command modules
Example Command (Pattern Template)
bin/tables.js- Reference implementation showing the optimization pattern