UNPKG

@qlover/fe-release

Version:

A tool for releasing front-end projects, supporting multiple release modes and configurations, simplifying the release process and improving efficiency.

1,904 lines (1,885 loc) 73.7 kB
import { LifecycleExecutor, ExecutorContextInterface } from '@qlover/fe-corekit'; import { ScriptPlugin, ScriptContext, ScriptPluginProps, ScriptSharedInterface, ScriptContextInterface, FeReleaseConfig, ShellInterface } from '@qlover/scripts-context'; export { ScriptPlugin } from '@qlover/scripts-context'; import { CommitField } from 'gitlog'; import { LoggerInterface } from '@qlover/logger'; import { OptionValues } from 'commander'; /** * @module PluginTuple * @description Type-safe plugin tuple creation and handling * * This module provides utilities for creating and handling tuples that * represent plugin configurations. It ensures type safety when working * with plugin constructors and their parameters. * * Core Features: * - Type-safe plugin class handling * - Constructor parameter inference * - Plugin tuple creation * * @example Basic usage * ```typescript * class MyPlugin extends ScriptPlugin { * constructor(context: ScriptContext, config: { option: string }) { * super(context); * } * } * * const pluginTuple = tuple(MyPlugin, { option: 'value' }); * // [MyPlugin, { option: 'value' }] * ``` * * @example Plugin name string * ```typescript * const pluginTuple = tuple('MyPlugin', { option: 'value' }); * // ['MyPlugin', { option: 'value' }] * ``` */ /** * Plugin class constructor type * * Represents a constructor for a class that extends ScriptPlugin. * Supports generic constructor arguments. * * @template T - Array type for constructor arguments * * @example * ```typescript * class MyPlugin extends ScriptPlugin { * constructor(context: ScriptContext, config: { option: string }) { * super(context); * } * } * * const PluginCtor: PluginClass = MyPlugin; * ``` */ type PluginClass<T extends unknown[] = any[]> = new (...args: T) => ScriptPlugin<ScriptContext<any>, ScriptPluginProps>; /** * Plugin constructor parameters type * * Extracts the constructor parameter types for a plugin class, * excluding the first parameter (context). Uses TypeScript's * conditional types and inference to extract parameter types. * * @template T - Plugin class type * * @example * ```typescript * class MyPlugin extends ScriptPlugin { * constructor( * context: ScriptContext, * config: { option: string }, * extra: number * ) { * super(context); * } * } * * // Type: [{ option: string }, number] * type Params = PluginConstructorParams<typeof MyPlugin>; * ``` */ type PluginConstructorParams<T extends PluginClass> = T extends new (first: any, ...args: infer P) => unknown ? P : never; /** * Plugin configuration tuple type * * Represents a tuple containing a plugin class (or name) and its * constructor arguments. Used for plugin registration and loading. * * @template T - Plugin class type * * @example * ```typescript * class MyPlugin extends ScriptPlugin { * constructor(context: ScriptContext, config: { option: string }) { * super(context); * } * } * * // Type: [typeof MyPlugin, { option: string }] * type Tuple = PluginTuple<typeof MyPlugin>; * * // Type: [string, { option: string }] * type StringTuple = PluginTuple<'MyPlugin'>; * ``` */ type PluginTuple<T extends PluginClass> = [ T | string, ...PluginConstructorParams<T> ]; /** * Creates a type-safe plugin configuration tuple * * Helper function for creating tuples that represent plugin * configurations with proper type inference for constructor * arguments. * * @template T - Plugin class type * @param plugin - Plugin class or name * @param args - Plugin constructor arguments * @returns Plugin configuration tuple * * @example Class-based plugin * ```typescript * class MyPlugin extends ScriptPlugin { * constructor( * context: ScriptContext, * config: { option: string }, * extra: number * ) { * super(context); * } * } * * const config = tuple(MyPlugin, { option: 'value' }, 42); * // [MyPlugin, { option: 'value' }, 42] * ``` * * @example String-based plugin * ```typescript * const config = tuple('MyPlugin', { option: 'value' }); * // ['MyPlugin', { option: 'value' }] * ``` */ declare function tuple<T extends PluginClass>(plugin: T | string, ...args: PluginConstructorParams<T>): PluginTuple<T>; /** * @module ReleaseTask * @description Task orchestration for release process * * This module provides the core task orchestration for the release process, * managing plugin loading, execution order, and context handling. It serves * as the main entry point for executing release operations. * * Core Features: * - Plugin management and execution * - Release context initialization * - Task execution control * - Environment-based control * * Default Plugins: * - Workspaces: Monorepo workspace management * - Changelog: Version and changelog management * - GithubPR: Pull request creation and management * * @example Basic usage * ```typescript * // Initialize and execute * const task = new ReleaseTask({ * rootPath: '/path/to/project', * sourceBranch: 'main' * }); * * await task.exec(); * ``` * * @example Custom plugins * ```typescript * import { tuple } from '@qlover/fe-release'; * * // Add custom plugin * class CustomPlugin extends ScriptPlugin { * async onExec() { * // Custom release logic * } * } * * const task = new ReleaseTask({}, new LifecycleExecutor<ReleaseContext>(), [ * tuple(CustomPlugin, { option: 'value' }) * ]); * * await task.exec(); * ``` * * @example Environment control * ```typescript * // Skip release * process.env.FE_RELEASE = 'false'; * * const task = new ReleaseTask(); * try { * await task.exec(); * } catch (e) { * // Handle "Skip Release" error * } * ``` */ /** * Core task class for managing release operations * * Handles plugin orchestration, task execution, and context management * for the release process. Supports both built-in and custom plugins. * * Features: * - Plugin lifecycle management * - Task execution control * - Context initialization and access * - Environment-based control * * @example Basic initialization * ```typescript * const task = new ReleaseTask({ * rootPath: '/path/to/project' * }); * ``` * * @example Custom executor * ```typescript * const executor = new LifecycleExecutor<ReleaseContext>({ * onError: (err) => console.error('Release failed:', err) * }); * * const task = new ReleaseTask({}, executor); * ``` * * @example Custom plugins * ```typescript * const task = new ReleaseTask( * {}, // options * new LifecycleExecutor<ReleaseContext>(), * [ * tuple(CustomPlugin, { config: 'value' }), * ...innerTuples // include default plugins * ] * ); * ``` */ declare class ReleaseTask { private executor; private defaultTuples; /** * Release context instance * @protected */ protected context: ReleaseContext; /** * Creates a new ReleaseTask instance * * Initializes the release context and sets up plugin configuration. * Supports custom executors and plugin configurations. * * @param options - Release context configuration * @param executor - Custom async executor (optional) * @param defaultTuples - Plugin configuration tuples (optional) * * @example * ```typescript * // Basic initialization * const task = new ReleaseTask({ * rootPath: '/path/to/project', * sourceBranch: 'main' * }); * * // With custom executor and plugins * const task = new ReleaseTask( * { rootPath: '/path/to/project' }, * new LifecycleExecutor<ReleaseContext>(), * [tuple(CustomPlugin, { option: 'value' })] * ); * ``` */ constructor(options?: Partial<ReleaseContextOptions>, executor?: LifecycleExecutor<ReleaseContext>, defaultTuples?: PluginTuple<PluginClass>[]); /** * Gets the current release context * * @returns Release context instance * * @example * ```typescript * const task = new ReleaseTask(); * const context = task.getContext(); * * console.log(context.releaseEnv); * console.log(context.sourceBranch); * ``` */ getContext(): ReleaseContext; /** * Loads and configures plugins for the release task * * Combines default and external plugins, initializes them with * the current context, and configures special cases like the * Workspaces plugin. * * Plugin Loading Process: * 1. Merge default and external plugins * 2. Initialize plugins with context * 3. Configure special plugins * 4. Add plugins to executor * * @param externalTuples - Additional plugin configurations * @returns Array of initialized plugins * * @example Basic usage * ```typescript * const task = new ReleaseTask(); * const plugins = await task.usePlugins(); * ``` * * @example Custom plugins * ```typescript * const task = new ReleaseTask(); * const plugins = await task.usePlugins([ * tuple(CustomPlugin, { option: 'value' }) * ]); * ``` */ usePlugins(externalTuples?: PluginTuple<PluginClass>[]): Promise<ScriptPlugin<ScriptContext<any>, ScriptPluginProps>[]>; /** * Executes the release task * * Internal method that runs the task through the executor. * Preserves the context through the execution chain. * * @returns Execution result * @internal */ run(): Promise<unknown>; /** * Main entry point for executing the release task * * Checks environment conditions, loads plugins, and executes * the release process. Supports additional plugin configuration * at execution time. * * Environment Control: * - Checks FE_RELEASE environment variable * - Skips release if FE_RELEASE=false * * @param externalTuples - Additional plugin configurations * @returns Execution result * @throws Error if release is skipped via environment variable * * @example Basic execution * ```typescript * const task = new ReleaseTask(); * await task.exec(); * ``` * * @example With additional plugins * ```typescript * const task = new ReleaseTask(); * await task.exec([ * tuple(CustomPlugin, { option: 'value' }) * ]); * ``` * * @example Environment control * ```typescript * // Skip release * process.env.FE_RELEASE = 'false'; * * const task = new ReleaseTask(); * try { * await task.exec(); * } catch (e) { * if (e.message === 'Skip Release') { * console.log('Release skipped via environment variable'); * } * } * ``` */ exec(externalTuples?: PluginTuple<PluginClass>[]): Promise<unknown>; } type PackageJson$1 = Record<string, unknown>; interface WorkspacesProps extends ScriptPluginProps { /** * Whether to skip workspaces * * @default `false` */ skip?: boolean; /** * Whether to skip checking the package.json file * * @default `false` */ skipCheckPackage?: boolean; /** * The workspace to publish */ workspace?: WorkspaceValue; /** * The workspaces to publish * @private */ workspaces?: WorkspaceValue[]; /** * The change labels * * from `changePackagesLabel` */ changeLabels?: string[]; /** * The changed paths * @private */ changedPaths?: string[]; /** * The packages * @private */ packages?: string[]; /** * All project packages mapping * @private */ projectWorkspaces?: WorkspaceValue[]; } interface WorkspaceValue { name: string; version: string; /** * The relative path of the workspace */ path: string; /** * The absolute path of the workspace */ root: string; /** * The package.json of the workspace */ packageJson: PackageJson$1; /** * The tag name of the workspace * @private */ tagName?: string; /** * The last tag name of the workspace * @private */ lastTag?: string; /** * The changelog of the workspace * @private */ changelog?: string; } type ReleaseParamsConfig = { /** * Max number of workspaces to include in the release name * * @default 3 */ maxWorkspace?: number; /** * Multi-workspace separator * * @default '_' */ multiWorkspaceSeparator?: string; /** * Workspace version separator * * @default '@' */ workspaceVersionSeparator?: string; /** * The branch name for batch release * * @default `batch-${releaseName}-${length}-packages-${timestamp}` */ batchBranchName?: string; /** * The tag name for batch release * * @default `batch-${length}-packages-${timestamp}` */ batchTagName?: string; /** * The PR title for batch release * * default from feConfig.release.PRTitle * * @default `Release ${env} ${pkgName} ${tagName}` */ PRTitle?: string; /** * The PR body for batch release * * default from feConfig.release.PRBody */ PRBody?: string; }; /** * Base configuration for Git-related plugins * * Extends ScriptPluginProps with GitHub-specific configuration * options for API access and timeouts. * * @example * ```typescript * const config: GitBaseProps = { * tokenRef: 'CUSTOM_TOKEN', * timeout: 5000 * }; * ``` */ interface GitBaseProps extends ScriptPluginProps { /** * Environment variable name for GitHub API token * * The value of this environment variable will be used * for GitHub API authentication. * * @default 'GITHUB_TOKEN' * * @example * ```typescript * process.env.CUSTOM_TOKEN = 'ghp_123...'; * const config = { tokenRef: 'CUSTOM_TOKEN' }; * ``` */ tokenRef?: string; /** * Timeout for GitHub API requests in milliseconds * * Controls how long to wait for GitHub API responses * before timing out. * * @example * ```typescript * const config = { timeout: 5000 }; // 5 seconds * ``` */ timeout?: number; } /** * @module GithubPR * @description GitHub Pull Request and Release Management * * This module provides functionality for managing GitHub pull requests * and releases as part of the release process. It handles PR creation, * release publishing, and changelog management. * * Core Features: * - Pull request creation and management * - Release publishing * - Changelog integration * - Tag management * - Label management * - Auto-merge support * * @example Basic usage * ```typescript * const plugin = new GithubPR(context, { * releasePR: true, * autoGenerate: true * }); * * await plugin.exec(); * ``` * * @example Release publishing * ```typescript * const plugin = new GithubPR(context, { * releasePR: false, * makeLatest: true, * preRelease: false * }); * * await plugin.exec(); * ``` */ interface GithubPRProps extends ReleaseParamsConfig, GitBaseProps { /** * Whether to dry run the creation of the pull request * * - create pr * - changeset publish * * @default `false` */ dryRunCreatePR?: boolean; /** * Whether to skip the release * * @default `false` */ skip?: boolean; /** * Whether to publish a PR * * @default `false` */ releasePR?: boolean; /** * The commit message of the release * * support WorkspaceValue * * @default 'chore(tag): {{name}} v${version}' */ commitMessage?: string; /** * The commit args of the release * * @default [] */ commitArgs?: string[]; /** * The release name of the release * * @default 'Release ${name} v${version}' */ releaseName?: string; /** * Whether to create a draft release * * @default false */ draft?: boolean; /** * Whether to create a pre-release * * @default false */ preRelease?: boolean; /** * Whether to auto-generate the release notes * * @default false */ autoGenerate?: boolean; /** * Whether to make the latest release * * @default true */ makeLatest?: boolean | 'true' | 'false' | 'legacy'; /** * The release notes of the release * * @default undefined */ releaseNotes?: string; /** * The discussion category name of the release * * @default undefined */ discussionCategoryName?: string; /** * Whether to push the changed labels to the release PR * * @default false */ pushChangeLabels?: boolean; } /** * @module FeReleaseTypes * @description Type definitions for the fe-release framework * * This module provides TypeScript type definitions for the fe-release framework, * including interfaces for release context, configuration, execution context, * and various utility types. * * Type Categories: * - Execution Context: Types for task execution and return values * - Configuration: Types for release and workspace configuration * - Plugin Types: Interfaces for GitHub PR and workspace plugins * - Utility Types: Helper types like DeepPartial and PackageJson * * Design Considerations: * - Type safety for plugin configuration * - Extensible context interfaces * - Backward compatibility support * - Clear deprecation markers * - Generic type constraints */ /** * Extended execution context for release tasks * * Adds release-specific return value handling to the base executor context. * Used to track and manage release task execution results. * * @example * ```typescript * const context: ExecutorReleaseContext = { * returnValue: { githubToken: 'token123' }, * // ... other executor context properties * }; * ``` */ interface ExecutorReleaseContext extends ExecutorContextInterface<ReleaseContext> { returnValue: ReleaseReturnValue; } /** * Return value type for release tasks * * Defines the structure of data returned from release task execution. * Includes GitHub token and allows for additional custom properties. * * @example * ```typescript * const returnValue: ReleaseReturnValue = { * githubToken: 'github_pat_123', * customData: { version: '1.0.0' } * }; * ``` */ type ReleaseReturnValue = { githubToken?: string; [key: string]: unknown; }; /** * Utility type for creating deep partial types * * Makes all properties in T optional recursively, useful for * partial configuration objects and type-safe updates. * * @example * ```typescript * interface Config { * deep: { * nested: { * value: string; * } * } * } * * const partial: DeepPartial<Config> = { * deep: { * nested: { * // All properties optional * } * } * }; * ``` */ type DeepPartial<T> = { [P in keyof T]?: DeepPartial<T[P]>; }; /** * Configuration interface for release process * * Extends shared script configuration with release-specific * settings for GitHub PR and workspace management. * * @example * ```typescript * const config: ReleaseConfig = { * githubPR: { * owner: 'org', * repo: 'repo', * base: 'main' * }, * workspaces: { * packages: ['packages/*'] * } * }; * ``` */ interface ReleaseConfig extends ScriptSharedInterface { githubPR?: GithubPRProps; workspaces?: WorkspacesProps; } /** * Options interface for release context * * Extends script context interface with release-specific configuration. * Uses generic type parameter for custom configuration extensions. * * @example * ```typescript * interface CustomConfig extends ReleaseConfig { * custom: { * feature: boolean; * } * } * * const options: ReleaseContextOptions<CustomConfig> = { * custom: { * feature: true * } * }; * ``` */ interface ReleaseContextOptions$1<T extends ReleaseConfig = ReleaseConfig> extends ScriptContextInterface<T> { } /** * Configuration for a single execution step * * Defines a labeled, optionally enabled task with async execution. * Used for creating structured, trackable release steps. * * @example * ```typescript * const step: StepOption<string> = { * label: 'Update version', * enabled: true, * task: async () => { * // Version update logic * return 'Version updated to 1.0.0'; * } * }; * ``` */ type StepOption<T> = { label: string; enabled?: boolean; task: () => Promise<T>; }; /** * Type alias for package.json structure * * Represents the structure of a package.json file with flexible * key-value pairs. Used for package metadata handling. * * @example * ```typescript * const pkg: PackageJson = { * name: 'my-package', * version: '1.0.0', * dependencies: { * // ... * } * }; * ``` */ type PackageJson = Record<string, unknown>; /** * Context interface for template processing * * Combines release context options with workspace values and * adds template-specific properties. Includes deprecated fields * with migration guidance. * * @example * ```typescript * const context: TemplateContext = { * publishPath: './dist', * env: 'production', // Deprecated * branch: 'main', // Deprecated * // ... other properties from ReleaseContextOptions * }; * ``` */ interface TemplateContext extends ReleaseContextOptions$1, WorkspaceValue { publishPath: string; /** * @deprecated use `releaseEnv` from `shared` */ env: string; /** * @deprecated use `sourceBranch` from `shared` */ branch: string; } /** * @module ReleaseContext * @description Core context management for release operations * * This module provides the central context management for release operations, * handling configuration, environment variables, workspace management, and * package information. It extends the base ScriptContext with release-specific * functionality. * * Core Features: * - Environment variable management * - Workspace configuration * - Package.json access * - Template context generation * - Changeset CLI integration * * @example Basic usage * ```typescript * const context = new ReleaseContext('my-package', { * rootPath: '/path/to/project', * sourceBranch: 'main', * releaseEnv: 'production' * }); * * // Access package info * const version = context.getPkg('version'); * * // Generate template context * const templateData = context.getTemplateContext(); * ``` * * @example Workspace management * ```typescript * // Set workspace configuration * context.setWorkspaces([{ * name: 'package-a', * version: '1.0.0', * path: 'packages/a' * }]); * * // Access workspace info * const currentWorkspace = context.workspace; * ``` * * @example Changeset integration * ```typescript * // Run changeset commands * await context.runChangesetsCli('version', ['--snapshot', 'alpha']); * ``` */ interface ReleaseContextOptions extends ScriptContextInterface<ReleaseContextConfig> { } interface ReleaseContextConfig extends FeReleaseConfig, ScriptSharedInterface { /** * The github PR of the project * @private */ githubPR?: GithubPRProps; /** * The workspaces of the project * @private */ workspaces?: WorkspacesProps; /** * The environment of the project * * default: * - first, get from `FE_RELEASE_ENV` * - second, get from `NODE_ENV` * - `development` */ releaseEnv?: string; /** * Plugins */ plugins?: PluginTuple<PluginClass<unknown[]>>[]; /** * The name of the repository */ repoName?: string; /** * The name of the author */ authorName?: string; /** * The current branch of the project */ currentBranch?: string; } /** * Core context class for release operations * * Manages release-specific configuration, environment variables, * workspace settings, and provides utilities for release operations. * * Features: * - Automatic environment detection * - Source branch management * - Workspace configuration * - Package.json access * - Template context generation * - Changeset CLI integration * * @example Basic initialization * ```typescript * const context = new ReleaseContext('my-package', { * rootPath: '/path/to/project', * sourceBranch: 'main' * }); * ``` * * @example Environment configuration * ```typescript * // With environment variables * process.env.FE_RELEASE_ENV = 'staging'; * process.env.FE_RELEASE_BRANCH = 'develop'; * * const context = new ReleaseContext('my-package', {}); * // context.releaseEnv === 'staging' * // context.sourceBranch === 'develop' * ``` */ declare class ReleaseContext extends ScriptContext<ReleaseContextConfig> { /** * Creates a new ReleaseContext instance * * Initializes the context with provided options and sets up * default values for required configuration: * - rootPath: Defaults to current working directory * - sourceBranch: Uses environment variables or default * - releaseEnv: Uses environment variables or 'development' * * Environment Variable Priority: * - sourceBranch: FE_RELEASE_BRANCH > FE_RELEASE_SOURCE_BRANCH > DEFAULT_SOURCE_BRANCH * - releaseEnv: FE_RELEASE_ENV > NODE_ENV > 'development' * * @param name - Unique identifier for this release context * @param options - Configuration options * * @example * ```typescript * const context = new ReleaseContext('web-app', { * rootPath: '/projects/web-app', * sourceBranch: 'main', * releaseEnv: 'production' * }); * ``` */ constructor(name: string, options: Partial<ReleaseContextOptions>); /** * Gets the root path of the project * * @returns Absolute path to project root * * @example * ```typescript * const root = context.rootPath; * // '/path/to/project' * ``` */ get rootPath(): string; /** * Gets the source branch for the release * * @returns Branch name to use as source * * @example * ```typescript * const branch = context.sourceBranch; * // 'main' or custom branch name * ``` */ get sourceBranch(): string; /** * Gets the release environment * * @returns Environment name (e.g., 'development', 'production') * * @example * ```typescript * const env = context.releaseEnv; * // 'development' or custom environment * ``` */ get releaseEnv(): string; /** * Gets all configured workspaces * * @returns Array of workspace configurations or undefined * * @example * ```typescript * const allWorkspaces = context.workspaces; * // [{ name: 'pkg-a', version: '1.0.0', ... }] * ``` */ get workspaces(): WorkspaceValue[] | undefined; /** * Gets the current active workspace * * @returns Current workspace configuration or undefined * * @example * ```typescript * const current = context.workspace; * // { name: 'pkg-a', version: '1.0.0', ... } * ``` */ get workspace(): WorkspaceValue | undefined; /** * Sets the workspace configurations * * Updates the workspace list while preserving other workspace settings * * @param workspaces - Array of workspace configurations * * @example * ```typescript * context.setWorkspaces([{ * name: 'pkg-a', * version: '1.0.0', * path: 'packages/a' * }]); * ``` */ setWorkspaces(workspaces: WorkspaceValue[]): void; /** * Gets package.json data for the current workspace * * Provides type-safe access to package.json fields with optional * path and default value support. * * @param key - Optional dot-notation path to specific field * @param defaultValue - Default value if field not found * @returns Package data of type T * @throws Error if package.json not found * * @example Basic usage * ```typescript * // Get entire package.json * const pkg = context.getPkg(); * * // Get specific field * const version = context.getPkg<string>('version'); * * // Get nested field with default * const script = context.getPkg<string>( * 'scripts.build', * 'echo "No build script"' * ); * ``` */ getPkg<T>(key?: string, defaultValue?: T): T; /** * Generates template context for string interpolation * * Combines context options, workspace data, and specific paths * for use in template processing. Includes deprecated fields * for backward compatibility. * * @returns Combined template context * * @example * ```typescript * const context = releaseContext.getTemplateContext(); * // { * // publishPath: 'packages/my-pkg', * // env: 'production', // deprecated * // branch: 'main', // deprecated * // releaseEnv: 'production', // use this instead * // sourceBranch: 'main', // use this instead * // ...other options * // } * ``` */ getTemplateContext(): TemplateContext; /** * Executes changeset CLI commands * * Automatically detects and uses appropriate package manager * (pnpm or npx) to run changeset commands. * * @param name - Changeset command name * @param args - Optional command arguments * @returns Command output * * @example Version bump * ```typescript * // Bump version with snapshot * await context.runChangesetsCli('version', ['--snapshot', 'alpha']); * * // Create new changeset * await context.runChangesetsCli('add'); * * // Status check * await context.runChangesetsCli('status'); * ``` */ runChangesetsCli(name: string, args?: string[]): Promise<string>; } /** * @module ReleaseLabel * @description Release label management and file change detection * * This module provides utilities for managing release labels and detecting * which packages have changed based on file paths. It supports custom * comparison logic and label formatting. * * Core Features: * - File change detection * - Package path matching * - Label generation * - Custom comparison logic * * @example Basic usage * ```typescript * const label = new ReleaseLabel({ * changePackagesLabel: 'changed:${name}', * packagesDirectories: ['packages/a', 'packages/b'] * }); * * // Find changed packages * const changed = label.pick(['packages/a/src/index.ts']); * // ['packages/a'] * * // Generate labels * const labels = label.toChangeLabels(changed); * // ['changed:packages/a'] * ``` * * @example Custom comparison * ```typescript * const label = new ReleaseLabel({ * changePackagesLabel: 'changed:${name}', * packagesDirectories: ['packages/a'], * compare: (file, pkg) => file.includes(pkg) * }); * * const changed = label.pick(['src/packages/a/index.ts']); * // ['packages/a'] * ``` */ /** * Function type for custom file path comparison * * Used to determine if a changed file belongs to a package. * Default implementation checks if the file path starts with * the package path. * * @param changedFilePath - Path of the changed file * @param packagePath - Path of the package to check against * @returns True if the file belongs to the package */ type ReleaseLabelCompare = (changedFilePath: string, packagePath: string) => boolean; interface ReleaseLabelOptions { /** * The change packages label */ changePackagesLabel: string; /** * The packages directories */ packagesDirectories: string[]; compare?: ReleaseLabelCompare; } /** * Core class for managing release labels and change detection * * Provides utilities for detecting changed packages and generating * appropriate labels. Supports custom comparison logic and label * formatting. * * Features: * - File change detection * - Package path matching * - Label generation * - Custom comparison logic * * @example Basic usage * ```typescript * const label = new ReleaseLabel({ * changePackagesLabel: 'changed:${name}', * packagesDirectories: ['packages/a', 'packages/b'] * }); * * // Find changed packages * const changed = label.pick(['packages/a/src/index.ts']); * * // Generate labels * const labels = label.toChangeLabels(changed); * ``` * * @example Custom comparison * ```typescript * const label = new ReleaseLabel({ * changePackagesLabel: 'changed:${name}', * packagesDirectories: ['packages/a'], * compare: (file, pkg) => file.includes(pkg) * }); * * const changed = label.pick(['src/packages/a/index.ts']); * ``` */ declare class ReleaseLabel { private readonly options; /** * Creates a new ReleaseLabel instance * * @param options - Configuration options for label management * * @example * ```typescript * const label = new ReleaseLabel({ * // Label template with ${name} placeholder * changePackagesLabel: 'changed:${name}', * * // Package directories to monitor * packagesDirectories: ['packages/a', 'packages/b'], * * // Optional custom comparison logic * compare: (file, pkg) => file.includes(pkg) * }); * ``` */ constructor(options: ReleaseLabelOptions); /** * Compares a changed file path against a package path * * Uses custom comparison function if provided, otherwise * checks if the file path starts with the package path. * * @param changedFilePath - Path of the changed file * @param packagePath - Path of the package to check against * @returns True if the file belongs to the package * * @example * ```typescript * // Default comparison * label.compare('packages/a/src/index.ts', 'packages/a'); * // true * * // Custom comparison * const label = new ReleaseLabel({ * ...options, * compare: (file, pkg) => file.includes(pkg) * }); * label.compare('src/packages/a/index.ts', 'packages/a'); * // true * ``` */ compare(changedFilePath: string, packagePath: string): boolean; /** * Generates a change label for a single package * * Replaces ${name} placeholder in the label template with * the package path. * * @param packagePath - Path of the package * @param label - Optional custom label template * @returns Formatted change label * * @example * ```typescript * // Default label template * label.toChangeLabel('packages/a'); * // 'changed:packages/a' * * // Custom label template * label.toChangeLabel('packages/a', 'modified:${name}'); * // 'modified:packages/a' * ``` */ toChangeLabel(packagePath: string, label?: string): string; /** * Generates change labels for multiple packages * * Maps each package path to a formatted change label. * * @param packages - Array of package paths * @param label - Optional custom label template * @returns Array of formatted change labels * * @example * ```typescript * // Default label template * label.toChangeLabels(['packages/a', 'packages/b']); * // ['changed:packages/a', 'changed:packages/b'] * * // Custom label template * label.toChangeLabels( * ['packages/a', 'packages/b'], * 'modified:${name}' * ); * // ['modified:packages/a', 'modified:packages/b'] * ``` */ toChangeLabels(packages: string[], label?: string): string[]; /** * Identifies packages affected by changed files * * Checks each changed file against package paths to determine * which packages have been modified. * * @param changedFiles - Array or Set of changed file paths * @param packages - Optional array of package paths to check * @returns Array of affected package paths * * @example * ```typescript * // Check against default packages * label.pick(['packages/a/src/index.ts']); * // ['packages/a'] * * // Check specific packages * label.pick( * ['packages/a/index.ts', 'packages/b/test.ts'], * ['packages/a', 'packages/c'] * ); * // ['packages/a'] * * // Using Set of files * label.pick(new Set(['packages/a/index.ts'])); * // ['packages/a'] * ``` */ pick(changedFiles: Array<string> | Set<string>, packages?: string[]): string[]; } /** * @module ChangeLog * @description Core interfaces for changelog generation * * This module provides the core interfaces and types for generating * changelogs from Git commit history. It includes types for commit * parsing, formatting, and changelog generation. * * Core Components: * - Commit data structures * - Changelog formatting * - Git log options * - Changelog generation * * @example Basic usage * ```typescript * class MyChangeLog implements ChangeLogInterface { * async getCommits(options?: GitChangelogOptions): Promise<CommitValue[]> { * // Implementation * } * } * * class MyFormatter implements ChangelogFormatter { * format(commits: CommitValue[]): string[] { * // Implementation * } * } * ``` */ /** * Base commit type mapping Git commit fields * * Maps all available Git commit fields to optional string values. * Uses the CommitField type from gitlog package to ensure type safety. * * Available fields include: * - hash: Full commit hash * - abbrevHash: Abbreviated commit hash * - subject: Commit message subject * - authorName: Author's name * - authorDate: Author date * - And many more from gitlog.CommitField * * @example * ```typescript * const commit: BaseCommit = { * hash: 'abc123def456', * abbrevHash: 'abc123', * subject: 'feat: new feature', * authorName: 'John Doe', * authorDate: '2023-01-01' * }; * ``` */ type BaseCommit = { [key in CommitField]: string | undefined; }; /** * Configuration options for changelog generation * * Provides comprehensive options for controlling how changelogs * are generated from Git history, including commit range selection, * formatting, and filtering. * * @example Basic usage * ```typescript * const options: GitChangelogOptions = { * from: 'v1.0.0', * to: 'v2.0.0', * directory: 'packages/my-pkg', * noMerges: true * }; * ``` * * @example Custom formatting * ```typescript * const options: GitChangelogOptions = { * types: [ * { type: 'feat', section: '### Features' }, * { type: 'fix', section: '### Bug Fixes' } * ], * formatTemplate: '* ${commitlint.message} ${prLink}', * commitBody: true * }; * ``` */ interface GitChangelogOptions { /** * Starting tag or commit reference * * Defines the start point for collecting commits. * Can be a tag name, commit hash, or branch name. * * @example * ```typescript * from: 'v1.0.0' // Start from v1.0.0 tag * from: 'abc123' // Start from specific commit * ``` */ from?: string; /** * Ending tag or commit reference * * Defines the end point for collecting commits. * Can be a tag name, commit hash, or branch name. * * @example * ```typescript * to: 'v2.0.0' // End at v2.0.0 tag * to: 'main' // End at main branch * ``` */ to?: string; /** * Directory to collect commits from * * Limits commit collection to changes in specified directory. * Useful for monorepo package-specific changelogs. * * @example * ```typescript * directory: 'packages/my-pkg' // Only changes in this directory * ``` */ directory?: string; /** * Git commit fields to include * * Specifies which Git commit fields to retrieve. * @default ["abbrevHash", "hash", "subject", "authorName", "authorDate"] * * @example * ```typescript * fields: ['hash', 'subject', 'authorName'] * ``` */ fields?: CommitField[]; /** * Whether to exclude merge commits * * When true, merge commits are filtered out from the changelog. * @default true * * @example * ```typescript * noMerges: true // Exclude merge commits * noMerges: false // Include merge commits * ``` */ noMerges?: boolean; /** * Commit type configurations * * Defines how different commit types should be handled and * formatted in the changelog. * * @example * ```typescript * types: [ * { type: 'feat', section: '### Features' }, * { type: 'fix', section: '### Bug Fixes' }, * { type: 'chore', hidden: true } // Skip chore commits * ] * ``` */ types?: { type: string; section?: string; hidden?: boolean; }[]; /** * Template for formatting commit entries * * Supports variables from CommitValue properties and adds: * - ${scopeHeader}: Formatted scope * - ${commitLink}: Commit hash link * - ${prLink}: PR number link * * @default '\n- ${scopeHeader} ${commitlint.message} ${commitLink} ${prLink}' * * @example * ```typescript * formatTemplate: '* ${commitlint.message} (${commitLink})' * ``` */ formatTemplate?: string; /** * Whether to include commit message body * * When true, includes the full commit message body * in the changelog entry. * * @since 2.3.0 * @default false * * @example * ```typescript * commitBody: true // Include full commit message * ``` */ commitBody?: boolean; } /** * Parsed conventional commit data * * Represents a commit message parsed according to the * conventional commit specification. * * Format: type(scope): message * * @example * ```typescript * const commit: Commitlint = { * type: 'feat', * scope: 'api', * message: 'add new endpoint', * body: 'Adds support for new API endpoint\n\nBREAKING CHANGE: API format changed' * }; * ``` */ interface Commitlint { /** Commit type (e.g., 'feat', 'fix') */ type?: string; /** Commit scope (e.g., 'api', 'core') */ scope?: string; /** Main commit message */ message: string; /** * Commit message body with title removed * @since 2.3.0 */ body?: string; } /** * Complete commit information * * Combines Git commit data, parsed conventional commit info, * and PR metadata into a single value object. * * @example * ```typescript * const commit: CommitValue = { * base: { * hash: 'abc123', * subject: 'feat(api): new endpoint (#123)' * }, * commitlint: { * type: 'feat', * scope: 'api', * message: 'new endpoint' * }, * commits: [], * prNumber: '123' * }; * ``` */ interface CommitValue { /** Raw Git commit information */ base: BaseCommit; /** Parsed conventional commit data */ commitlint: Commitlint; /** Sub-commits (for merge commits) */ commits: CommitValue[]; /** Associated pull request number */ prNumber?: string; } /** * Interface for changelog formatting * * Defines the contract for classes that format commit data * into changelog entries. * * @example * ```typescript * class MarkdownFormatter implements ChangelogFormatter { * format(commits: CommitValue[]): string[] { * return commits.map(commit => * `- ${commit.commitlint.message} (#${commit.prNumber})` * ); * } * } * ``` */ interface ChangelogFormatter { /** * Formats commits into changelog entries * * @param commits - Array of commits to format * @param options - Optional formatting options * @returns Array of formatted changelog lines */ format<Opt extends GitChangelogOptions>(commits: unknown[], options?: Opt): string[]; } /** * Interface for changelog generation * * Defines the contract for classes that generate changelogs * from Git history. * * @example * ```typescript * class GitChangelog implements ChangeLogInterface { * async getCommits(options?: GitChangelogOptions): Promise<CommitValue[]> { * // Get commits from Git and parse them * const commits = await gitlog(options); * return commits.map(commit => ({ * base: commit, * commitlint: parseCommit(commit.subject), * commits: [] * })); * } * } * ``` */ interface ChangeLogInterface { /** * Retrieves and parses Git commits * * @param options - Optional Git log options * @returns Promise resolving to array of parsed commits */ getCommits(options?: GitChangelogOptions): Promise<CommitValue[]>; } /** * @module GitChangelog * @description Git-based changelog generation and commit parsing * * This module provides functionality for generating changelogs from Git * history, parsing commit messages according to conventional commit format, * and managing commit metadata. * * Core Features: * - Git log retrieval * - Conventional commit parsing * - Changelog generation * - Tag resolution * - PR number extraction * * @example Basic usage * ```typescript * const changelog = new GitChangelog({ * shell, * logger, * directory: 'packages/my-pkg' * }); * * // Get commits between tags * const commits = await changelog.getCommits({ * from: 'v1.0.0', * to: 'v2.0.0' * }); * ``` * * @example Commit parsing * ```typescript * const changelog = new GitChangelog({ shell, logger }); * * // Parse conventional commit * const commit = changelog.parseCommitlint( * 'feat(api): add new endpoint', * 'Detailed description\n\nBREAKING CHANGE: API format changed' * ); * // { * // type: 'feat', * // scope: 'api', * // message: 'add new endpoint', * // body: ' Detailed description\n\n BREAKING CHANGE: API format changed' * // } * ``` */ /** * Complete list of available Git commit fields * * These fields can be used when retrieving commit information * to specify which data should be included in the output. * * @example * ```typescript * const commits = await changelog.getGitLog({ * fields: CHANGELOG_ALL_FIELDS * }); * ``` */ declare const CHANGELOG_ALL_FIELDS: CommitField[]; interface GitChangelogProps extends GitChangelogOptions { shell: ShellInterface; logger: LoggerInterface; } /** * Core class for Git-based changelog generation * * Provides functionality for retrieving and parsing Git commit history, * generating changelogs, and managing commit metadata. Implements the * ChangeLogInterface for standardized changelog generation. * * Features: * - Git log retrieval with flexible options * - Conventional commit parsing * - Tag resolution and validation * - PR number extraction * - Commit body formatting * * @example Basic usage * ```typescript * const changelog = new GitChangelog({ * shell, * logger, * directory: 'packages/my-pkg' * }); * * // Get commits with parsed metadata * const commits = await changelog.getCommits({ * from: 'v1.0.0', * to: 'v2.0.0', * noMerges: true * }); * ``` * * @example Custom commit parsing * ```typescript * const changelog = new GitChangelog({ shell, logger }); * * // Create commit value from hash and message * const commit = changelog.toCommitValue( * 'abc1234', * 'feat(api): new endpoint (#123)' * ); * // { * // base: { hash: 'abc1234', ... }, * // commitlint: { type: 'feat', scope: 'api', ... }, * // prNumber: '123' * // } * ``` */ declare class GitChangelog implements ChangeLogInterface { protected options: GitChangelogProps; /** * Creates a new GitChangelog instance * * @param options - Configuration options including shell and logger * * @example * ```typescript * const changelog = new GitChangelog({ * shell: new Shell(), * logger: new Logger(), * directory: 'packages/my-pkg', * noMerges: true * }); * ``` */ constructor(options: GitChangelogProps); /** * Retrieves Git commit history with specified options * * Fetches commit information between specified tags or commits, * with support for filtering and field selection. * * @param options - Configuration options for Git log retrieval * @returns Array of commit objects with requested fields * * @example Basic usage * ```typescript * const commits = await changelog.getGitLog({ * from: 'v1.0.0', * to: 'v2.0.0', * directory: 'packages/my-pkg', * noMerges: true * }); * ``` * * @example Custom fields * ```typescript * const commits = await changelog.getGitLog({ * fields: ['hash', 'subject', 'authorName'], * directory: 'src' * }); * ``` */ getGitLog(options?: GitChangelogOptions): Promise<BaseCommit[]>; /** * Retrieves and parses Git commits with metadata * * Gets commit history and enhances it with parsed conventional * commit information and PR metadata. * * @override * @param options - Configuration options for Git log retrieval