UNPKG

xpm

Version:

The xPack project manager command line tool

398 lines (289 loc) • 12.1 kB
# 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/