xpm
Version:
The xPack project manager command line tool
398 lines (289 loc) ⢠12.1 kB
Markdown
# Source Code Documentation
This folder contains the TypeScript source files that provide the core functionality of the xpm-lib library.
## Overview
**Total Lines:** ~9,826 lines across 15 TypeScript files
**Build Output:** Compiled by `tsc` to ES6 modules in the `dist/` folder
**Module System:** ES modules with `.js` extensions in imports
## Architecture
The library implements a sophisticated **two-phase lazy evaluation pattern** for optimal performance:
### Phase 1: Collection Initialisation
When `Actions` or `BuildConfigurations` collections are initialised, only template names are expanded using matrix parameters. Template content remains unevaluated.
```typescript
const buildConfigurations = new BuildConfigurations({ ... })
await buildConfigurations.initialise() // Expands names only
```
### Phase 2: Item Retrieval & Initialisation
Individual items are fully evaluated only when accessed and explicitly initialised:
```typescript
const config = buildConfigurations.get('release-x64')
await config.initialise() // Now processes content, inheritance, etc.
```
**Benefits:**
- ā” Performance: Only used configurations incur processing cost
- š¾ Memory: Deferred object creation until needed
- š Flexibility: Template expansion without content evaluation
## Folder Structure
```
src/
āāā classes/ # Core domain classes
ā āāā actions.ts # Action collections and individual actions
ā āāā build-configurations.ts # Build configuration management
ā āāā combinations-generator.ts # Cartesian product for matrices
ā āāā data-model.ts # Package data model and variable hierarchy
ā āāā errors.ts # Custom error type hierarchy
ā āāā init-template-base.ts # Base class for init templates
ā āāā liquid-drop.ts # Liquid template variable drops
ā āāā liquid-engine.ts # Custom Liquid engine with filters
ā āāā package.ts # Package.json management
ā āāā platform-detector.ts # OS and architecture detection
ā āāā policies.ts # Version compatibility policies
ā āāā template-expander.ts # Generic template expansion engine
ā
āāā data/ # Static data and constants
ā āāā substitutions-variables.ts # Base substitution variables
ā
āāā functions/ # Pure utility functions
ā āāā chmod-recursively.ts # File permission utilities
ā āāā filter-paths.ts # Path sanitisation filters
ā āāā is-something.ts # Type guard utilities
ā āāā matrix-expander.ts # Matrix parameter processing
ā āāā perform-substitutions.ts # Liquid template substitution
ā āāā utils.ts # General utility functions
ā
āāā types/ # TypeScript type definitions
ā āāā json.ts # JSON structure type definitions
ā āāā xpm-init-template.ts # Init template types
ā āāā xpm.ts # Core xpm types
ā
āāā index.ts # Public API exports
```
## Key Components
### Classes
#### Core Domain Classes
- **`BuildConfigurations`** / **`BuildConfiguration`** - Manages build configurations with inheritance, template expansion, and property merging
- **`Actions`** / **`Action`** - Manages executable actions with Liquid template support
- **`DataModel`** - Top-level coordinator for package processing and variable hierarchy
- **`Package`** - Package.json loading, validation, and capability detection
#### Template Processing
- **`TemplateExpander`** - Generic template expansion with matrix parameters
- **`CombinationsGenerator`** - Cartesian product computation for matrix expansion
- **`LiquidEngine`** - Custom Liquid engine with xpm-specific filters
- **`LiquidPropertiesDrop`** / **`LiquidMatrixDrop`** - Liquid variable access with strict validation
#### Utilities
- **`PlatformDetector`** - Cross-platform OS and architecture detection
- **`Policies`** - Version compatibility and feature flags
- **`InitTemplateBase`** - Abstract base for project initialisation templates
### Functions
Pure utility functions organised by purpose:
- **Type Guards:** `isString()`, `isNumber()`, `isJsonObject()`, etc.
- **Template Detection:** `hasLiquidSyntax()`, `performSubstitutions()`
- **Path Operations:** `filterPath()`, `chmodRecursively()`
- **Matrix Processing:** `processMatrixForExpansion()`
- **Error Handling:** `getErrorMessage()`
### Types
Comprehensive TypeScript definitions:
- **`json.ts`** - All JSON structure types for package.json and xpack section
- **`xpm.ts`** - Core xpm types (Context, etc.)
- **`xpm-init-template.ts`** - Init template property definitions
## Error Hierarchy
Well-defined error types for semantic error handling:
```typescript
// Parsing errors
JsonSyntaxError // Malformed JSON
// Configuration errors
ConfigurationError // Invalid configuration content
TemplateError // Template evaluation failures
// User input errors
InputError // Invalid command-line arguments or user input
PrerequisitesError // Missing dependencies or incompatible versions
// Output errors
OutputError // File generation or output failures
```
## Design Patterns
### 1. Lazy Initialisation
Classes use two-step initialisation (constructor + `initialise()` method) to defer expensive operations:
```typescript
class BuildConfigurations {
protected _isInitialised = false
async initialise(): Promise<boolean> {
if (this._isInitialised) return true
// Perform expensive operations
this._isInitialised = true
return true
}
}
```
### 2. Template Inheritance
Build configurations support multiple inheritance with circular reference detection:
```typescript
{
"release-x64": {
"inherits": ["common", "release-base"],
"properties": { /* overrides */ }
}
}
```
### 3. Matrix Expansion
Templates generate multiple concrete instances from matrix parameters:
```typescript
{
"release-{{ matrix.arch }}": {
"matrix": {
"arch": ["x64", "arm64", "ia32"]
},
"template": { /* configuration */ }
}
}
// Generates: release-x64, release-arm64, release-ia32
```
### 4. Dependency Injection
All classes accept dependencies via constructor parameters:
```typescript
constructor({
engine,
substitutionsVariables,
log,
}: BuildConfigurationsConstructorParameters) {
// No hard dependencies on singletons
}
```
## Variable Hierarchy
Substitution variables follow a clear hierarchy:
```
liquidSubstitutionsVariablesBase (sealed)
āāā env: { ... } # Environment variables
āāā os: { platform, arch, ... } # Platform detection
āāā path: { sep, delimiter, ... } # Path utilities
ā
DataModel.substitutionsVariables (sealed)
āāā [base variables]
āāā package: { name, version, ... } # Package metadata
āāā properties: { ... } # User-defined properties
ā
BuildConfiguration._substitutionsVariables
āāā [package variables]
āāā configuration: { name, ... } # Configuration context
āāā properties: { ... } # Merged inherited properties
āāā matrix: { ... } # Template matrix parameters
```
## Coding Conventions
### TypeScript
- Use strict type checking
- Prefer interfaces for public APIs
- Use type guards for runtime validation
- Avoid `any` (use `unknown` when type is truly unknown)
### Naming
- **Classes:** PascalCase (`BuildConfiguration`)
- **Interfaces:** PascalCase with descriptive suffixes (`BuildConfigurationConstructorParameters`)
- **Functions:** camelCase (`hasLiquidSyntax`)
- **Constants:** SCREAMING_SNAKE_CASE or descriptive names
- **Protected/Private:** Prefix with underscore (`_isInitialised`)
### Async Patterns
- Always use `async/await` (no callbacks)
- Return `Promise<boolean>` from `initialise()` methods
- Use `Promise<void>` for operations without return values
### Error Handling
- Use specific error types from error hierarchy
- Include context in error messages
- Throw synchronously when possible (no wrapping in Promise.reject)
### Documentation
- Comprehensive TSDoc for all public APIs
- Use `@remarks` for detailed explanations
- Document `@throws` conditions
- Keep line length ā¤80 characters in comments
## File Size Guidelines
**Current State:**
- ā
Most files: 200-800 lines (appropriate)
- ā ļø `build-configurations.ts`: 2,155 lines (consider splitting)
- ā ļø `actions.ts`: 1,160 lines (consider splitting)
- ā ļø `init-template-base.ts`: 1,027 lines (manageable)
**Recommendations:**
- Target: <800 lines per file
- Split at: >1,000 lines
- Extract: Complex subsystems into separate files
See [CODE-REVIEW.md](./CODE-REVIEW.md) for detailed analysis and refactoring recommendations.
## Development Workflow
### Building
```bash
npm run compile # Compile TypeScript to dist/
npm run compile-clean # Clean and compile
npm run watch # Watch mode for development
```
### Testing
```bash
npm test # Run all tests
npm run test-coverage # Generate coverage report
```
### Linting
```bash
npm run lint # ESLint check
npm run lint-fix # Auto-fix linting issues
```
### API Documentation
```bash
cd website
npm run build-api # Generate API reference
```
## Common Tasks
### Adding a New Class
1. Create file in appropriate folder (`classes/`, `functions/`, etc.)
2. Add comprehensive TSDoc comments
3. Export from `index.ts`
4. Add corresponding tests in `tests/tap/`
5. Update API documentation if needed
### Adding a Liquid Filter
In `liquid-engine.ts`:
```typescript
this.registerFilter('filterName', (value: string, arg?: string) => {
// Implementation
return processedValue
})
```
Then add tests in `tests/tap/classes/liquid-engine.ts`.
### Adding Type Definitions
1. Add types to appropriate file in `types/`
2. Use descriptive names with `Json` prefix for JSON structures
3. Export from type file (already re-exported by index.ts)
4. Add type validation in runtime code
## Performance Considerations
### Lazy Loading ā”
- Collections expand template names without evaluating content
- Items initialised only when accessed
- Caching of expanded names and instances
### Efficient Data Structures
- Maps for O(1) lookups of configurations and actions
- Sets for O(1) duplicate detection
- Cached arrays for repeated access to names
### Memory Management
- Sealed substitution variable objects (no accidental modification)
- References to parent collections (no deep cloning)
- Deferred instantiation of configurations and actions
## Security Considerations
### Template Injection
- Liquid templates are sourced from package.json (user's own code)
- Liquid engine is sandboxed (no filesystem access by default)
- Consider template size limits for production use
### Path Traversal
- All paths resolved relative to package folder
- No user-provided paths accepted without validation
- Build folder paths sanitised
### Command Execution
- Action commands are user-defined (trusted code)
- If accepting commands from external sources, sanitisation required
## Related Documentation
- **[Code Review](./CODE-REVIEW.md)** - Comprehensive analysis with improvement recommendations
- **[API Reference](../website/docs/api/)** - Generated API documentation
- **[Test Suite](../tests/tap/README.md)** - Test organisation and conventions
- **[Main README](../README.md)** - Project overview and usage
## Contributing
When contributing to the source code:
1. Follow existing code style and conventions
2. Add comprehensive TSDoc comments
3. Write tests for all new functionality
4. Ensure all tests pass (`npm test`)
5. Run linter (`npm run lint-fix`)
6. Update documentation as needed
## Questions?
- **Issues:** https://github.com/xpack/xpm-lib-ts/issues
- **Discussions:** https://github.com/xpack/xpm-lib-ts/discussions
- **Documentation:** https://xpack.github.io/xpm-lib/