xpm
Version:
The xPack project manager command line tool
391 lines (381 loc) • 12.9 kB
text/typescript
/*
* 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 * as os from 'node:os'
import * as path from 'node:path'
// ============================================================================
/**
* Represents a map of substitution values used by Liquid templates.
*
* @remarks
* Values can be strings for simple substitutions or arrays for multi-line
* content. Array values are typically joined with newlines when rendered.
*
* Common use cases:
*
* <ul>
* <li><b>Properties:</b> User-defined configuration values from
* <code>xpack.properties.</code></li>
* <li><b>Matrix parameters:</b> Template expansion variables from
* <code>matrix</code> definitions.</li>
* <li><b>Configuration data:</b> Build-specific settings and metadata.</li>
* </ul>
*
* Templates access these values via namespaces like `properties.foo`,
* `matrix.arch`, etc., with the Liquid Drop pattern providing lazy
* evaluation and nested substitution support.
*/
export type LiquidSubstitutionsStrings = Record<string, string | string[]>
/**
* Defines the substitution variables available to Liquid templates.
*
* @remarks
* This interface mirrors a subset of Node.js environment, operating
* system, and path information, along with package-specific configuration
* values.
*
* Variable hierarchy and scoping:
*
* <ol>
* <li><b>Base variables</b> (<code>env</code>, <code>os</code>,
* <code>path</code>): Available globally, initialized once
* from Node.js runtime at startup.</li>
* <li><b>Package variables:</b> Added when processing
* <code>package.json</code>, contains
* package metadata accessible via <code>package.name</code>,
* <code>package.version</code>,
* etc.</li>
* <li><b>Properties:</b> User-defined values from
* <code>xpack.properties</code>, accessible via
* <code>properties.key</code>.</li>
* <li><b>Configuration:</b> Build configuration metadata, available when
* processing configuration-specific templates via
* <code>configuration.name</code>,
* etc.</li>
* <li><b>Matrix:</b> Template expansion parameters, scoped to individual
* expanded instances, accessible via <code>matrix.key</code>.</li>
* </ol>
*
* Variables are inherited and extended through the hierarchy:
*
* <ul>
* <li>package actions use package properties</li>
* <li>configuration actions use:
* <ul>
* <li>package actions</li>
* <li>actions inherited from parent configurations, recursively,
* in order of inheritance</li>
* <li>configuration properties</li>
* </ul>
* </li>
* </ul>
*
* This ensures templates have access to appropriate
* context without exposing unrelated data.
*/
export interface LiquidSubstitutionsVariables {
/**
* Process environment variables from the current execution context.
*
* @remarks
* Provides access to all environment variables via `env.VARIABLE_NAME`
* in templates. Common uses include accessing `PATH`, `HOME`, `USER`, or
* custom variables set by build scripts.
*
* See {@link https://nodejs.org/dist/latest-v16.x/docs/api/process.html#process_process_env | Node.js process.env documentation}
*/
env: NodeJS.ProcessEnv
/**
* Operating system information from Node.js os module.
*
* @remarks
* Provides platform detection and system information for cross-platform
* template logic. Common uses include conditional compilation, path
* construction, and platform-specific configuration.
*
* Key properties for cross-platform templates:
*
* <ul>
* <li><code>os.platform</code>: Detect OS ('darwin', 'linux', 'win32').</li>
* <li><code>os.arch</code>: Detect CPU architecture ('x64', 'arm64',
* etc.).</li>
* <li><code>os.EOL</code>: Use correct line endings for generated files.</li>
* <li><code>os.homedir</code>: Reference user's home folder portably.</li>
* </ul>
*
* See {@link https://nodejs.org/dist/latest-v16.x/docs/api/os.html | Node.js os module documentation}
*/
os: {
/**
* The operating system-specific end-of-line marker.
* <ul>
* <li><code>\\n</code> on POSIX</li>
* <li><code>\\r\\n</code> on Windows</li>
* </ul>
*/
EOL: string
/**
* Possible values are 'arm', 'arm64', 'ia32', 'mips', 'mipsel',
* 'ppc', 'ppc64', 's390', 's390x', 'x32', and 'x64'.
*/
arch: string
/**
* Contains commonly used operating system-specific constants
* for error codes, process signals, and so on. The specific
* constants defined are described in
* {@link https://nodejs.org/dist/latest-v16.x/docs/api/os.html#os_os_constants_1 | OS constants}
*/
constants: {
signals: Record<string, number>
errno: Record<string, number>
}
/**
* An array of objects containing information about
* each logical CPU core.
*/
cpus: os.CpuInfo[]
/**
* A string identifying the endianness of the CPU
* for which the Node.js binary was compiled.
*
* Possible values are 'BE' for big endian and 'LE' for little endian.
*/
endianness: 'BE' | 'LE'
/**
* The string path of the current user's home folder.
*/
homedir: string
/**
* The host name of the operating system as a string.
*/
hostname: string
/**
* A string identifying the operating system platform.
* Possible values are 'aix', 'darwin', 'freebsd', 'linux', 'openbsd',
* 'sunos', and 'win32'.
*/
platform: NodeJS.Platform
/**
* The operating system as a string.
*/
release: string
/**
* Returns the operating system's default folder for
* temporary files as a string.
*/
tmpdir: string
/**
* Returns the operating system name as returned by uname(3).
* For example, it returns 'Linux' on Linux, 'Darwin' on macOS,
* and 'Windows_NT' on Windows.
*/
type: string
/**
* Returns a string identifying the kernel version.
*
* On POSIX systems, the operating system release is determined
* by calling `uname(3)`. On Windows, `RtlGetVersion()` is used,
* and if it is not available, `GetVersionExW()` will be used.
*/
version: string
}
/**
* Path separators and delimiters from Node.js path module.
*
* @remarks
* Provides platform-specific path constants for building file paths in
* templates. Use these to construct paths that work correctly on all
* platforms.
*
* Available constants:
*
* <ul>
* <li><code>path.sep</code>: Platform-specific path separator (/ or \).</li>
* <li><code>path.delimiter</code>: Platform-specific PATH delimiter
* (; or :).</li>
* <li><code>path.posix.*</code>: Force POSIX conventions regardless
of platform.</li>
* <li><code>path.win32.*</code>: Force Windows conventions regardless
of platform.</li>
* </ul>
*
* Note: For path manipulation, prefer using Liquid filters like
* `path_join`, `path_dirname`, etc., which handle cross-platform concerns
* automatically.
*
* See [Node.js path module documentation](https://nodejs.org/dist/latest-v16.x/docs/api/path.html)
*/
path: {
/**
* Provides the platform-specific path delimiter:
* <ul>
* <li><code>;</code> for Windows</li>
* <li><code>:</code> for POSIX</li>
* </ul>
*/
delimiter: string
/**
* Provides the platform-specific path segment separator:
* <ul>
* <li><code>\\</code> on Windows</li>
* <li><code>/</code> on POSIX</li>
* </ul>
*/
sep: string
win32: {
delimiter: string
sep: string
}
posix: {
delimiter: string
sep: string
}
}
/**
* The package metadata exposed to Liquid templates.
*
* @remarks
* Contains the entire `package.json` content, allowing templates to access
* package name, version, description, dependencies, and xpack-specific
* metadata.
*
* Common template patterns:
*
* <ul>
* <li><code>\{\{ package.name \}\}</code>: Package name for generated
* files.</li>
* <li><code>\{\{ package.version \}\}</code>: Version string for
* documentation.</li>
* <li><code>\{\{ package.xpack.properties.key \}\}</code>: Access xpack
* properties.</li>
* </ul>
*
* Undefined when processing templates outside of a package context.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
package?: any
/**
* The build configuration exposed to Liquid templates.
*
* @remarks
* Available only when processing templates within a build configuration
* context (actions, dependencies, properties belonging to a specific
* configuration).
*
* Contains the configuration name and all configuration properties,
* allowing templates to reference the current build context:
*
* <ul>
* <li><code>\{\{ configuration.name \}\}</code>: The build configuration
* name.</li>
* <li><code>\{\{ configuration.properties.key \}\}</code>:
* Configuration-specific
* settings.</li>
* </ul>
*
* Undefined when processing package-level templates.
*/
configuration?: {
name: string
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any
}
/**
* The properties map used for substitutions.
*
* @remarks
* User-defined configuration values from `xpack.properties`
* in `package.json`.
* Provides a namespace for custom template variables without polluting
* the global scope.
*
* Properties support nested substitutions: a property value can reference
* other properties, package metadata, or system variables using Liquid
* syntax. The Liquid Drop pattern ensures recursive evaluation.
*
* Access via `{{ properties.key }}` in templates.
*/
properties: LiquidSubstitutionsStrings
/**
* Optional matrix parameters used for template expansion.
*
* @remarks
* Available only for actions or configurations generated from templates
* with matrix definitions. Each expanded instance receives a specific
* combination of matrix values.
*
* Matrix parameters enable generating multiple similar actions or
* configurations from a single template definition. For example, a matrix
* with `arch: ['x64', 'arm64']` and `os: ['linux', 'darwin']` generates
* 4 instances (x64-linux, x64-darwin, arm64-linux, arm64-darwin).
*
* Access via `{{ matrix.key }}` in templates. Scoped to the individual
* expanded instance, ensuring isolation between generated items.
*/
matrix?: LiquidSubstitutionsStrings
}
/**
* The base substitution variables initialised from the current environment.
*
* @remarks
* This constant provides the foundation for all Liquid template processing,
* capturing the runtime environment once at module load time.
*
* Initialization strategy:
*
* <ol>
* <li><b>Environment variables:</b> Snapshot of process.env at load time.</li>
* <li><b>OS information:</b> OS specific definitions (platform, arch,
* etc.).</li>
* <li><b>Path constants:</b> Platform-specific separators and delimiters.</li>
* <li><b>Properties:</b> Placeholder for package-specific additions.</li>
* </ol>
*
* These base variables are shared across all template processing within the
* application and extended with package, configuration, and matrix variables
* as needed. The base object is typically spread into new contexts rather
* than mutated, preserving the original snapshot.
*/
export const liquidSubstitutionsVariablesBase: LiquidSubstitutionsVariables = {
env: process.env,
os: {
EOL: os.EOL,
arch: os.arch(),
constants: {
signals: os.constants.signals,
errno: os.constants.errno,
},
cpus: os.cpus(),
endianness: os.endianness(),
homedir: os.homedir(),
hostname: os.hostname(),
platform: os.platform(),
release: os.release(),
tmpdir: os.tmpdir(),
type: os.type(),
// os.version() available since 12.x
version: os.version(),
},
path: {
delimiter: path.delimiter,
sep: path.sep,
win32: {
delimiter: path.win32.delimiter,
sep: path.win32.sep,
},
posix: {
delimiter: path.posix.delimiter,
sep: path.posix.sep,
},
},
properties: {},
}
// ----------------------------------------------------------------------------