/*
* This file is part of the xPack project (http://xpack.github.io).
* Copyright (c) 2021-2026 Liviu Ionescu. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software
* for any purpose is hereby granted, under the terms of the MIT license.
*
* If a copy of the license was not distributed with this file, it can
* be obtained from https://opensource.org/license/mit.
*/
// ----------------------------------------------------------------------------
import { Logger } from '@xpack/logger'
// ----------------------------------------------------------------------------
import { LiquidSubstitutionsVariables } from '../data/substitutions-variables.js'
import { processMatrixForExpansion } from '../functions/matrix-expander.js'
import { performSubstitutions } from '../functions/perform-substitutions.js'
import { getErrorMessage } from '../functions/utils.js'
import { CombinationsGenerator } from './combinations-generator.js'
import { ConfigurationError } from './errors.js'
import { LiquidEngine } from './liquid-engine.js'
import { JsonTemplateMatrix } from '../types/json.js'
// ============================================================================
/**
* Configuration parameters for constructing a template expander instance.
*
* @remarks
* This interface defines the required configuration for creating an
* instance of {@link TemplateExpander}. All properties are mandatory.
*
* The parameters provide the Liquid templating engine, substitution
* variables hierarchy, and logger for diagnostic output during template
* expansion operations.
*/
export interface TemplateExpanderConstructorParameters {
/**
* The Liquid templating engine for variable substitution.
*/
engine: LiquidEngine
/**
* The variables available for substitution in templates.
*/
substitutionsVariables: LiquidSubstitutionsVariables
/**
* The logger instance for output and diagnostics.
*/
log: Logger
}
/**
* Callback function type for creating instances from template expansions.
*
* @remarks
* This function is invoked for each combination generated during template
* expansion. It receives the expanded name, matrix combination parameters,
* and the template content, and must return a new instance of type
* TInstance.
*
* The callback is responsible for:
*
*
* - Creating the appropriate instance type (e.g.,
Action or
* BuildConfiguration).
* - Passing the expanded name to the instance constructor.
* - Storing the matrix parameters for later template evaluation.
* - Associating the template content with the instance.
*
*
* @typeParam TTemplate - The type of the template content (e.g.,
* JsonActionContent or
* JsonBuildConfigurationContent).
* @typeParam TInstance - The type of instance to create (e.g.,
* Action or BuildConfiguration).
*
* @param expandedName - The name after Liquid substitution with matrix
* parameters (e.g., test-x64 from
* test-\{\{ matrix.arch \}\}).
* @param combination - The matrix parameter values for this combination
* (e.g., \{ arch: 'x64', platform: 'linux' \}).
* @param templateContent - The template content to associate with the
* instance.
* @param originalTemplateName - The original template name before expansion
* (e.g., test-\{\{ matrix.arch \}\}).
* @returns The newly created instance.
*/
export type InstanceFactoryCallback = (
expandedName: string,
combination: Record,
templateContent: TTemplate,
originalTemplateName: string
) => TInstance
/**
* A generic template expansion engine for matrix-based template processing.
*
* @remarks
* This class provides shared functionality for expanding template names
* and creating multiple instances from a single template definition with
* matrix parameters. It eliminates code duplication between
* {@link Actions} and {@link BuildConfigurations} classes by extracting
* the common template expansion logic.
*
* Template expansion process:
*
*
* - Matrix processing: Validates matrix structure and performs
* Liquid substitutions on matrix values.
* - Combination generation: Computes the Cartesian product of all
* matrix parameter values.
* - Name expansion: For each combination, substitutes matrix values
* into the template name.
* - Instance creation: Invokes the factory callback to create
* instances with expanded names and matrix parameters.
*
*
* The class is generic to support different template and instance types
* whilst maintaining type safety throughout the expansion process.
*
* @typeParam TTemplate - The type of template content (e.g.,
* JsonActionContent or
* JsonBuildConfigurationContent).
* @typeParam TInstance - The type of instance to create (e.g.,
* Action or BuildConfiguration).
*/
export class TemplateExpander {
// --------------------------------------------------------------------------
// Public Members.
/**
* The Liquid templating engine for variable substitution.
*/
readonly engine: LiquidEngine
/**
* The variables available for substitution in templates.
*/
readonly substitutionsVariables: LiquidSubstitutionsVariables
/**
* The logger instance for output and diagnostics.
*/
readonly log: Logger
// --------------------------------------------------------------------------
// Constructor.
/**
* Constructs a template expander instance.
*
* @param engine - The Liquid templating engine for variable substitution.
* @param substitutionsVariables - The variables available for substitution
* in templates.
* @param log - The logger instance for output and diagnostics.
*/
constructor({
engine,
substitutionsVariables,
log,
}: TemplateExpanderConstructorParameters) {
this.engine = engine
this.substitutionsVariables = substitutionsVariables
this.log = log
}
// --------------------------------------------------------------------------
// Public Methods.
/**
* Expands a template into multiple instances based on matrix parameters.
*
* @remarks
* This method orchestrates the template expansion process by validating
* the matrix, generating all parameter combinations, expanding the
* template name for each combination, and creating instances via the
* factory callback.
*
* Processing steps:
*
*
* - Processes the matrix to extract and validate parameter keys and
* values.
* - Generates all combinations using the Cartesian product.
* - For each combination:
*
* - Performs Liquid substitution on the template name with matrix
* parameters.
* - Invokes the factory callback to create an instance.
* - Stores the instance in the result map.
*
*
*
*
* The factory callback is responsible for creating the appropriate
* instance type and associating it with the expanded name and matrix
* parameters.
*
* @param templateName - The template name containing Liquid variables
* (e.g., test-\{\{ matrix.arch \}\}).
* @param matrix - The matrix object containing parameter definitions.
* @param templateContent - The template content to pass to the factory.
* @param templateType - A descriptive name for the template type (e.g.,
* "action" or "buildConfiguration"), used in error messages.
* @param instanceFactory - A callback function that creates instances from
* expanded names and matrix combinations.
* @returns A promise that resolves to a map of expanded names to their
* corresponding instances.
*
* @throws {@link ConfigurationError}
* If the matrix structure is invalid, template name substitution fails,
* or the factory callback throws an error.
*/
async expandTemplate({
templateName,
matrix,
templateContent,
templateType,
instanceFactory,
}: {
templateName: string
matrix: JsonTemplateMatrix
templateContent: TTemplate
templateType: string
instanceFactory: InstanceFactoryCallback
}): Promise