UNPKG

@remotex-labs/xbuild

Version:

A versatile JavaScript and TypeScript toolchain build system

1,444 lines (1,442 loc) 212 kB
/** * This file was automatically generated by xBuild. * DO NOT EDIT MANUALLY. */ import { BuildOptions, BuildResult, Loader, Message, OnEndResult, OnLoadArgs, OnLoadResult, OnResolveArgs, OnResolveResult, OnStartResult, PartialMessage, Platform, PluginBuild } from 'esbuild'; import ts, { CompilerOptions, DiagnosticCategory, IScriptSnapshot, LanguageService, ParsedCommandLine, ResolvedModuleWithFailedLookupLocations, SourceFile } from 'typescript'; import { IncomingMessage, ServerResponse } from 'http'; import { ResolveMetadataInterface as xMapResolveMetadataInterface, ResolveOptionsInterface } from '@remotex-labs/xmap'; import { Options } from 'yargs'; /** * Orchestrates the build process across multiple variants with lifecycle management and configuration. * * @remarks * The `BuildService` is the primary service for managing multi-variant builds in xBuild. * It handles configuration changes, variant lifecycle, type checking, and build execution * with support for hot reloading and file watching. * * **Key responsibilities**: * - Manages multiple build variants (e.g., production, development, testing) * - Provides reactive configuration updates through subscription system * - Coordinates lifecycle hooks (onStart, onEnd) across all variants * - Handles macro transformation and directive processing * - Supports incremental builds and file touch notifications * - Aggregates build results and type checking diagnostics * * **Architecture**: * Each variant is managed by a {@link VariantService} instance with its own: * - esbuild configuration and context * - TypeScript language service * - Lifecycle provider for hooks and plugins * - Build state and watch mode support * * The service uses a subscription pattern to react to configuration changes, * automatically creating new variants or disposing removed ones. * * @example Basic usage * ```ts * const buildService = new BuildService({ * variants: { * production: { * esbuild: { minify: true, sourcemap: false } * }, * development: { * esbuild: { minify: false, sourcemap: true } * } * } * }); * * // Build all variants * const results = await buildService.build(); * console.log(results.production.errors); * ``` * * @example With lifecycle hooks * ```ts * const buildService = new BuildService(config); * * buildService.onStart = (context) => { * console.log(`Building variant: ${context.variantName}`); * }; * * buildService.onEnd = (context) => { * console.log(`Completed ${context.variantName}: ${context.result.errors.length} errors`); * }; * * await buildService.build(); * ``` * * @example Configuration reload * ```ts * const buildService = new BuildService(initialConfig); * * // Reload with new configuration * buildService.reload({ * variants: { * production: { esbuild: { target: 'es2020' } }, * staging: { esbuild: { minify: true } } * } * }); * // Old variants disposed, new ones created * ``` * * @example Type checking * ```ts * const buildService = new BuildService(config); * const diagnostics = await buildService.typeChack(); * * for (const [variant, errors] of Object.entries(diagnostics)) { * console.log(`${variant}: ${errors.length} type errors`); * } * ``` * * @see {@link VariantService} for individual variant management * @see {@link ConfigurationService} for configuration handling * @see {@link LifecycleProvider} for hook management * * @since 2.0.0 */ declare class BuildService { private argv; /** * Callback invoked when a build completes for any variant. * * @remarks * Set via the `onEnd` setter. Called after each variant's build finishes, * providing access to build results, errors, warnings, and metadata. * * @since 2.0.0 */ private onEndCallback?; /** * Callback invoked when a build starts for any variant. * * @remarks * Set via the `onStart` setter. Called before each variant's build begins, * after macro metadata analysis completes. * * @since 2.0.0 */ private onStartCallback?; /** * Map of variant names to their service instances. * * @remarks * Contains all active build variants. Variants are created during construction * and updated when configuration changes via {@link reload} or {@link setConfiguration}. * * @since 2.0.0 */ private variants; /** * Configuration service managing build settings and variant definitions. * * @remarks * Injected singleton that provides reactive configuration updates through * its subscription system. Changes trigger automatic variant recreation. * * @since 2.0.0 */ private readonly configuration; /** * Creates a new BuildService instance with optional configuration and command-line arguments. * * @param argv - Command-line arguments passed to variant services (default: empty object) * * @remarks * The constructor: * 1. Accepts optional initial configuration * 2. Stores command-line arguments for variant initialization * 3. Subscribes to configuration changes via {@link parseVariants} * 4. Automatically creates variants defined in the configuration * * Configuration can be provided later via {@link reload} or {@link setConfiguration} * if not supplied during construction. * * @since 2.0.0 */ constructor(argv?: Record<string, unknown>); /** * Gets the current complete build configuration. * * @returns The active build configuration including all variants and common settings * * @remarks * Retrieves the immutable snapshot of the current configuration from the * configuration service. Changes to the returned object do not affect * the actual configuration - use {@link setConfiguration} or {@link reload} instead. * * @since 2.0.0 */ get config(): BuildConfigInterface; /** * Sets the callback to invoke when any variant build completes. * * @param callback - Function receiving the result context with build output and metadata * * @remarks * The callback receives a {@link ResultContextInterface} containing: * - Variant name * - Build result (errors, warnings, outputs) * - Metadata files and outputs * - Timestamp and duration * * Called after the build finishes but before promises resolve. * * @example * ```ts * buildService.onEnd = (context) => { * const { variantName, result } = context; * console.log(`✓ ${variantName}: ${result.errors.length} errors`); * }; * ``` * * @since 2.0.0 */ set onEnd(callback: OnEndType); /** * Sets the callback to invoke when any variant build starts. * * @param callback - Function receiving the build context with file and variant information * * @remarks * The callback receives a {@link BuildContextInterface} containing: * - Variant name * - File path being processed * - Build stage and metadata * - Loader type * * Called after macro analysis but before transformation begins. * * @example * ```ts * buildService.onStart = (context) => { * console.log(`Building ${context.args.path} for ${context.variantName}`); * }; * ``` * * @since 2.0.0 */ set onStart(callback: OnStartType); /** * Reloads the build configuration and updates variants accordingly. * * @param config - Optional new configuration to replace the current one * @param clearCache - Whether to clear cached files and TypeScript language service state before reloading * * @remarks * The reload process: * 1. Optionally clears cached file state and TypeScript language service data * 2. Replaces configuration if provided * 3. Compares new variant names with existing ones * 4. Disposes variants no longer in configuration * 5. Creates new variants from the updated configuration * 6. Existing variants with matching names continue unchanged * * This is useful for hot-reloading configuration files without restarting the build process. * * @example * ```ts * // Reload with a new staging variant * buildService.reload({ * config: { * variants: { * ...buildService.config.variants, * staging: { esbuild: { minify: true } } * } * } * }); * ``` * * @example * ```ts * // Reload and clear cached file/type-checking state first * buildService.reload({ * clearCache: true * }); * ``` * * @since 2.3.0 */ reload({ config, clearCache }?: ReloadOptionsInterface): void; /** * Notifies all variants that specific files have been modified. * * @param files - Array of file paths that have changed * * @remarks * Propagates file change notifications to all variant services, triggering * incremental rebuilds in watch mode. Each variant's watch service handles * the actual rebuild logic. * * Typically used by file watchers or development servers to trigger hot reloads. * * @example * ```ts * // File watcher integration * watcher.on('change', (changedFiles) => { * buildService.touchFiles(changedFiles); * }); * ``` * * @see {@link VariantService.touchFiles} * * @since 2.0.0 */ touchFiles(files: Array<string>): void; /** * Partially updates the build configuration without replacing it entirely. * * @param config - Partial configuration to merge with the current configuration * * @remarks * Performs a shallow merge of the provided configuration with the current one. * Use {@link reload} for deep configuration replacement or variant restructuring. * * Common use cases: * - Toggling minification * - Updating define constants * - Modifying common build options * * @example * ```ts * // Enable minification for all variants * buildService.setConfiguration({ * common: { esbuild: { minify: true } } * }); * ``` * * @see {@link reload} for full configuration replacement * * @since 2.0.0 */ setConfiguration(config: Partial<BuildConfigInterface>): void; /** * Performs TypeScript type checking across all variants. * * @returns Promise resolving to a map of variant names to their diagnostic results * * @remarks * Runs the TypeScript compiler's diagnostic checker for each variant in parallel. * Returns all type errors, warnings, and suggestions without failing the build. * * **Note**: Method name has a typo - should be `typeCheck` but kept for backward compatibility. * * Useful for: * - Pre-build validation * - CI/CD type checking pipelines * - IDE integration and diagnostics display * * @example * ```ts * const diagnostics = await buildService.typeChack(); * * for (const [variant, errors] of Object.entries(diagnostics)) { * if (errors.length > 0) { * console.error(`${variant} has ${errors.length} type errors`); * errors.forEach(err => console.error(err.messageText)); * } * } * ``` * * @see {@link DiagnosticInterface} * @see {@link VariantService.check} * * @since 2.0.0 */ typeChack(): Promise<Record<string, DiagnosticInterface[]>>; /** * Executes the build process for all or specific variants, * respecting `dependOn` ordering and running independent variants in parallel. * * @param names - Optional array of variant names to build (builds all if omitted) * * @returns Promise resolving to a map of variant names to their enhanced build results * * @throws xBuildError - When a circular dependency is detected before any build starts * @throws AggregateError - When any variant build fails, containing all error details * * @remarks * The build process: * 1. Validates the dependency graph — throws immediately on circular deps * 2. Launches all requested variants concurrently * 3. Each variant awaits its `dependOn` dependencies before running * 4. Collects results and errors from each variant without stopping others * 5. Enhances build results with additional metadata * 6. Throws AggregateError if any builds failed * * **Dependency resolution**: * - `dependOn` variants always finish before the dependent variant starts * - A shared dependency (e.g. two variants both depending on `types`) builds * only once — subsequent dependents await the same running promise * - Independent variants run fully in parallel * * **Error handling**: * - Build failures don't stop other variants from building * - All errors are collected into {@link BuildTreeInterface.errors} and thrown * together after all builds complete * - Supports both esbuild-specific errors and generic JavaScript errors * * **Result enhancement**: * Build results are processed by {@link enhancedBuildResult} to provide * structured error and warning information. * * @example Build all variants * ```ts * try { * const results = await buildService.build(); * console.log(`Built ${Object.keys(results).length} variants`); * } catch (error) { * if (error instanceof AggregateError) { * error.errors.forEach(err => console.error(err.message)); * } * } * ``` * * @example Build specific variants * ```ts * const results = await buildService.build(['production', 'staging']); * // Only production and staging variants are built * ``` * * @example With dependency ordering * ```ts * // Given: main dependOn shared, shared dependOn types * // Build order: types → shared → main (dependencies first) * const results = await buildService.build(); * ``` * * @see {@link buildVariant} for per-variant execution and caching logic * @see {@link BuildTreeInterface} * @see {@link enhancedBuildResult} * @see {@link BuildResultInterface} * @see {@link VariantService.build} * @see {@link validateDependencies} for circular dependency detection * * @since 2.4.0 */ build(names?: Array<string>): Promise<Record<string, BuildResultInterface>>; /** * Triggers the onEnd callback when a variant build completes. * * @param context - The result context containing build output and metadata * * @remarks * Internal handler that safely invokes the user-provided onEnd callback if set. * Called by variant lifecycle providers after each build finishes. * * @since 2.0.0 */ private onEndTrigger; /** * Triggers the onStart callback and performs macro analysis before a variant build starts. * * @param context - The build context containing file and variant information * * @returns Promise resolving to the load result after macro metadata analysis * * @throws Error - Propagates errors from macro analysis that aren't AggregateErrors * * @remarks * Internal handler that: * 1. Analyzes macro metadata for the file being built * 2. Invokes the user-provided onStart callback if set * 3. Returns the analysis result to the build pipeline * 4. Converts AggregateErrors to esbuild-compatible error format * * The macro analysis prepares directive information ($$ifdef, $$inline, etc.) * that will be used during the transformation phase. * * @see {@link analyzeMacroMetadata} * * @since 2.0.0 */ private onStartTrigger; /** * Disposes and removes variants by name. * * @param dispose - Array of variant names to dispose * * @remarks * Cleanly shuts down variant services and removes them from the internal map. * Called during configuration reload to remove variants no longer in config. * * Each variant's dispose method: * - Stops watch mode if active * - Cleans up esbuild contexts * - Releases TypeScript language service resources * * @since 2.0.0 */ private disposeVariants; /** * Compares two objects and returns keys present in the second but not the first. * * @param obj1 - Reference object (usually new configuration) * @param obj2 - Comparison object (usually existing variants) * * @returns Array of keys present in obj2 but missing in obj1 * * @remarks * Used to identify variants that should be disposed during configuration reload. * If a variant exists in the service but not in the new configuration, it's removed. * * @since 2.0.0 */ private compareKeys; /** * Creates variant service instances from the current configuration. * * @throws xBuildError - When no variants are defined in the configuration * * @remarks * Invoked by the configuration subscription whenever configuration changes. * For each variant in the configuration: * 1. Skips if the variant already exists (prevents recreation) * 2. Creates a new LifecycleProvider with hooks * 3. Attaches onStart and onEnd listeners * 4. Creates VariantService with configuration * 5. Registers macro transformer directive * * The lifecycle hooks enable: * - Build start/end notifications * - Macro analysis and transformation * - Custom plugin integration * * @see {@link VariantService} * @see {@link LifecycleProvider} * @see {@link transformerDirective} * * @since 2.0.0 */ private parseVariants; /** * Returns the normalized `dependOn` list for a variant. * * @param variantName - The variant to look up * * @returns Array of dependency variant names, empty if none defined * * @remarks * Normalizes the `dependOn` field from the variant configuration into * a consistent array form, since the field accepts either a single * string or an array of strings. * * @see {@link buildVariant} * @see {@link validateDependencies} * * @since 2.4.0 */ private getDependOn; /** * Validates the dependency graph for all or specific variants before building starts. * * @param names - Optional subset of variant names to validate (validates all if omitted) * * @throws xBuildError - When a circular dependency is detected, with the full cycle * path included in the message (e.g. `Circular dependency detected: main → shared → main`) * * @remarks * Performs a depth-first traversal of the dependency graph using two sets: * - `visited` — variants fully processed, skipped on revisit * - `inStack` — variants in the current traversal path, used to detect cycles * * Dependencies that exist in `dependOn` but have no matching variant instance * in {@link variants} are silently skipped. * * Called by {@link build} before any variant starts, ensuring the entire * graph is valid before any work begins. * * @see {@link build} * @see {@link getDependOn} * * @since 2.4.0 */ private validateDependencies; /** * Executes the build for a single variant after all its dependencies resolve. * * @param name - Variant name to build * @param ctx - Isolated build context for this {@link build} invocation * * @remarks * Called exclusively by {@link buildVariant} after the promise is registered * in {@link BuildTreeInterface.cache}, preventing re-entry. * * Awaits all `dependOn` dependencies concurrently via `Promise.all` before * running the variant. Dependencies missing from {@link variants} are silently skipped. * * Errors are pushed into {@link BuildTreeInterface.errors} rather than thrown, * so all variants attempt to build even if a sibling fails. Handles both * esbuild-specific errors via {@link isBuildResultError} and generic JavaScript errors. * * @see {@link buildVariant} * @see {@link getDependOn} * @see {@link isBuildResultError} * @see {@link BuildTreeInterface} * @see {@link enhancedBuildResult} * * @since 2.4.0 */ private executeBuild; /** * Builds a single variant, first awaiting any `dependOn` dependencies. * * @param name - Variant name to build * @param ctx - Isolated build context for this {@link build} invocation, * carrying the promise cache, error list, and results map * * @returns Promise that resolves when the variant and all its dependencies finish * * @remarks * Stores its promise in {@link BuildTreeInterface.cache} on first call so any * subsequent caller depending on the same variant awaits the already-running * promise rather than triggering a duplicate build. * * Delegates actual execution to {@link executeBuild} after registering the promise, * ensuring the cache is populated before any async work begins. * * @see {@link build} * @see {@link executeBuild} * @see {@link BuildTreeInterface} * * @since 2.4.0 */ private buildVariant; } /** * Options used to reload the build service configuration. * * @remarks * These options control how configuration reload behaves: * - `config` replaces the current build configuration * - `clearCache` clears cached file and TypeScript language service state before reloading * * @since 2.3.0 */ interface ReloadOptionsInterface { /** * Optional new configuration to replace the current one. * * @remarks * When provided, the build service reloads using this configuration * before recalculating variants. * * @since 2.3.0 */ config?: PartialBuildConfigType; /** * Whether to clear cached files and TypeScript language service state before reloading. * * @remarks * When enabled, cached file tracking and language service state are reset * before the configuration is reloaded. * * @since 2.3.0 */ clearCache?: boolean; } /** * Isolated state container for a single {@link BuildService.build} invocation. * * @remarks * Created fresh on every `build()` call to ensure concurrent watch-mode * rebuilds cannot share or overwrite each other's state. * * Passed through {@link BuildService.buildVariant} to carry the promise * cache, accumulated errors, and collected results across the full * dependency graph traversal. * * @see {@link BuildService.build} * @see {@link BuildService.buildVariant} * * @since 2.4.0 */ interface BuildTreeInterface { /** * Promise cache keyed by variant name. * * @remarks * Ensures each variant builds exactly once per `build()` call. * Subsequent callers depending on the same variant await the * already-running promise instead of triggering a duplicate build. * * @since 2.4.0 */ cache: Map<string, Promise<void>>; /** * Accumulated build errors across all variants. * * @remarks * Errors are pushed here rather than thrown immediately, so all * variants attempt to build even if a sibling fails. Thrown together * as an `AggregateError` after all builds complete. * * @since 2.4.0 */ errors: Array<Error>; /** * Collected build results keyed by variant name. * * @remarks * Populated by {@link BuildService.buildVariant} as each variant * finishes. Only contains results for variants that built successfully. * * @since 2.4.0 */ results: Record<string, BuildResultInterface>; } /** * Extended build result interface with normalized error and warning arrays. * * @remarks * This interface extends esbuild's {@link BuildResult} while replacing the `errors` and `warnings` * properties with normalized Error instances instead of esbuild's Message objects. This normalization * provides consistent error handling throughout the xBuild system with proper stack traces, formatting, * and error classification. * * **Key differences from esbuild's BuildResult**: * - `errors`: Changed from `Message[]` to `Error[]` with normalized error types * - `warnings`: Changed from `Message[]` to `Error[]` with normalized error types * - All other properties (metafile, outputFiles, mangleCache) are preserved unchanged * * **Benefits of normalization**: * - Consistent error handling across different error sources (esbuild, TypeScript, VM runtime) * - Proper error inheritance and type checking * - Rich stack trace information with source mapping * - Formatted error output with syntax highlighting * - Integration with xBuild's custom error classes * * The normalized errors may include: * - {@link TypesError} for TypeScript type checking failures * - {@link xBuildError} for text errors during build hooks * - {@link esBuildError} for esbuild compilation errors with location information * - {@link VMRuntimeError} for runtime errors during build hooks * - {@link xBuildBaseError} for custom build system errors * * @example * ```ts * const result: BuildResultInterface = { * errors: [ * new esBuildError(esbuildMessage), * new TypesError('Type checking failed', diagnostics) * ], * warnings: [ * new xBuildError('Deprecation warning') * ], * metafile: { ... }, * outputFiles: [ ... ], * mangleCache: { ... } * }; * ``` * * @see {@link BuildResult} from esbuild for the base interface * * @since 2.0.0 */ interface BuildResultInterface extends Omit<BuildResult, 'errors' | 'warnings'> { /** * Array of normalized error instances encountered during the build. * * @remarks * Contains Error instances converted from esbuild messages and other error sources. * Unlike esbuild's native error array which contains Message objects, this array * contains fully normalized Error instances with proper stack traces and formatting. * * Errors in this array may originate from: * - Compilation errors (syntax, resolution failures) * - Type checking failures * - Build hook execution errors * - Plugin errors * * @example * ```ts * if (result.errors.length > 0) { * console.error(`Build failed with ${result.errors.length} errors`); * result.errors.forEach(err => console.error(err.stack)); * } * ``` * * @since 2.0.0 */ errors: Array<Error>; /** * Array of normalized warning instances encountered during the build. * * @remarks * Contains Error instances converted from esbuild warning messages and other warning sources. * Unlike esbuild's native warning array which contains Message objects, this array * contains fully normalized Error instances with proper stack traces and formatting. * * Warnings indicate non-fatal issues that don't prevent build completion but may * require attention, such as: * - Deprecated API usage * - Type checking warnings * - Performance concerns * - Potential runtime issues * * @example * ```ts * if (result.warnings.length > 0) { * console.warn(`Build completed with ${result.warnings.length} warnings`); * result.warnings.forEach(warn => console.warn(warn.message)); * } * ``` * * @since 2.0.0 */ warnings: Array<Error>; } /** * Recursively makes all properties of a type optional. * * @remarks * This utility type behaves like TypeScript’s built-in {@link Partial} type, * but applies recursively to all nested object properties. * * It is commonly used for: * - Partial configuration overrides * - Patch / update objects * - Programmatic configuration merging * - Build variant and preset definitions * * This type only affects compile-time type checking and has no runtime impact. * * ⚠️ **Important limitations**: * - Arrays and functions are treated as objects and will also be recursively * transformed. If this is undesirable, a more specialized deep-partial * implementation should be used. * - Intended for configuration and data-shaping use cases, not strict domain models. * * @example * ```ts * interface Config { * server: { * host: string; * port: number; * }; * features: { * experimental: boolean; * }; * } * * const override: DeepPartialType<Config> = { * server: { * port: 8080 * } * }; * ``` * * @template T - The type to recursively make optional. * * @since 2.0.0 */ type DeepPartialType<T> = { [K in keyof T]?: T[K] extends object ? DeepPartialType<T[K]> : T[K]; }; /** * Represents code that can be injected into build output as a banner or footer. * Can be a static string or a function that generates code dynamically based on build context. * * @remarks * This type provides flexibility for injecting code at the top (banner) or bottom (footer) of * bundled output files. The function form receives the plugin name and command-line arguments, * allowing for context-aware code generation. * * Common use cases: * - Static banners: Copyright notices, license headers, version information * - Dynamic banners: Build timestamps, environment-specific code, conditional imports * - Footer code: Analytics snippets, polyfills, initialization scripts * * When using the function form, the generated string is cached per build variant to avoid * regenerating the same code multiple times. * * @example * ```ts * // Static banner * const banner: InjectableCodeType = '\/* Copyright 2024 *\/'; * * // Dynamic banner * const banner: InjectableCodeType = (name, argv) => { * const version = argv.version || '1.0.0'; * return `\/* Built by ${name} v${version} at ${new Date().toISOString()} *\/`; * }; * ``` * * @see {@link BaseBuildDefinitionInterface.banner} * @see {@link BaseBuildDefinitionInterface.footer} * * @since 2.0.0 */ type InjectableCodeType = string | ((name: string, argv: Record<string, unknown>) => string); /** * Defines lifecycle hook handlers for build process stages. * Allows registration of custom logic during resolution, loading, build start, build end, and success. * * @remarks * This interface groups all available lifecycle hooks in a single configuration object. * All hooks are optional, allowing selective registration of only necessary handlers. * * Hook execution order during a build: * 1. `onStart` - Before any file processing * 2. `onResolve` - During import path resolution * 3. `onLoad` - When loading file contents * 4. `onEnd` - After build completes (success or failure) * 5. `onSuccess` - After a build completes successfully * * Each hook receives a specialized context object appropriate for its lifecycle stage, providing * access to build configuration, variant information, and cross-hook communication through the * shared stage object. * * @example * ```ts * const hooks: LifecycleHooksInterface = { * onStart: async (context) => { * console.log(`${context.variantName} build starting...`); * }, * onLoad: async (context) => { * if (context.args.path.endsWith('.custom')) { * return { contents: transform(context.contents), loader: 'ts' }; * } * }, * onSuccess: async (context) => { * console.log(`Build succeeded in ${context.duration}ms!`); * } * }; * ``` * * @see {@link OnEndType} * @see {@link OnLoadType} * @see {@link OnStartType} * @see {@link OnResolveType} * * @since 2.0.0 */ interface LifecycleHooksInterface { /** * Hook handler executed when the build completes, regardless of success or failure. * * @remarks * Called after all build operations finish with a result context containing the build result, * calculated duration, variant name, arguments, and stage state. Useful for cleanup, logging, * reporting, and post-processing. * * The handler receives `ResultContextInterface` providing access to: * - `buildResult`: Final build outcome with errors and warnings * - `duration`: Build duration in milliseconds * - `variantName`: Build variant identifier * - `argv`: Command-line arguments and configuration * - `stage`: Shared state object for cross-hook communication * * @example * ```ts * onEnd: async (context) => { * const { buildResult, duration, variantName } = context; * console.log(`${variantName} completed in ${duration}ms`); * if (buildResult.errors.length > 0) { * // Handle errors * } * } * ``` * * @see {@link ResultContextInterface} * * @since 2.0.0 */ onEnd?: OnEndType; /** * Hook handler executed when loading file contents during module processing. * * @remarks * Called for each file being processed with a load context containing the current file contents * (potentially transformed by previous hooks), loader type, load arguments, variant name, and * stage state. Can transform contents and change the loader type. Multiple handlers execute in * a pipeline pattern where each receives the output of previous hooks. * * The handler receives `LoadContextInterface` providing access to: * - `contents`: Current file contents (string or binary) * - `loader`: Current loader type (e.g., 'ts', 'js', 'json') * - `args`: Load arguments including file path and namespace * - `variantName`: Build variant identifier * - `argv`: Command-line arguments and configuration * - `stage`: Shared state object for cross-hook communication * * @example * ```ts * onLoad: async (context) => { * const { contents, args, variantName } = context; * if (args.path.endsWith('.custom')) { * return { * contents: transform(contents.toString()), * loader: 'ts' * }; * } * } * ``` * * @see {@link LoadContextInterface} * * @since 2.0.0 */ onLoad?: OnLoadType; /** * Hook handler executed when the build process begins. * * @remarks * Called before any file processing starts with a build context containing the esbuild build object, * variant name, arguments, and stage state. Useful for initialization, validation, and setup tasks. * * The handler receives `BuildContextInterface` providing access to: * - `build`: esbuild plugin build object with configuration and utilities * - `variantName`: Build variant identifier * - `argv`: Command-line arguments and configuration * - `stage`: Shared state object for cross-hook communication * * @example * ```ts * onStart: async (context) => { * const { build, variantName, stage } = context; * console.log(`Starting ${variantName} build`); * stage.startTime = new Date(); * * // Validate configuration * if (!build.initialOptions.outdir) { * return { errors: [{ text: 'Output directory required' }] }; * } * } * ``` * * @see {@link BuildContextInterface} * * @since 2.0.0 */ onStart?: OnStartType; /** * Hook handler executed when the build completes successfully without errors. * * @remarks * Only called when `buildResult.errors.length === 0`, after all regular end hooks have completed. * Receives the same result context as end hooks, containing build result, duration, variant name, * arguments, and stage state. Useful for deployment, success notifications, and success-only operations. * * The handler receives `ResultContextInterface` providing access to: * - `buildResult`: Final build outcome (guaranteed to have zero errors) * - `duration`: Build duration in milliseconds * - `variantName`: Build variant identifier * - `argv`: Command-line arguments and configuration * - `stage`: Shared state object for cross-hook communication * * @example * ```ts * onSuccess: async (context) => { * const { buildResult, duration, variantName } = context; * console.log(`${variantName} succeeded in ${duration}ms!`); * await deploy(buildResult.metafile); * } * ``` * * @see {@link ResultContextInterface} * * @since 2.0.0 */ onSuccess?: OnEndType; /** * Hook handler executed during module path resolution. * * @remarks * Called when resolving import paths to file system locations with a resolve context containing * the resolution arguments, variant name, and stage state. Can redirect imports, mark modules as * external, or implement custom resolution logic. Multiple handlers execute, and their results are * merged, with later hooks able to override earlier ones. * * The handler receives `ResolveContextInterface` providing access to: * - `args`: Resolution arguments including import path and importer info * - `variantName`: Build variant identifier * - `argv`: Command-line arguments and configuration * - `stage`: Shared state object for cross-hook communication * * @example * ```ts * onResolve: async (context) => { * const { args, variantName } = context; * * // Redirect '@/' imports to 'src/' * if (args.path.startsWith('@/')) { * return { * path: resolve('src', args.path.slice(2)), * namespace: 'file' * }; * } * * // Mark as external in production * if (variantName === 'production' && args.path.includes('node_modules')) { * return { path: args.path, external: true }; * } * } * ``` * * @see {@link ResolveContextInterface} * * @since 2.0.0 */ onResolve?: OnResolveType; } /** * Configuration options for TypeScript declaration file generation. * * @remarks * Controls how and where TypeScript declaration files (`.d.ts`) are generated during the build. * These options work in conjunction with the TypeScript compiler to produce type definitions * for bundled code. * * When `bundle` is true, declarations from multiple source files are combined into a single * declaration file per entry point. When false, individual declaration files are generated * for each source file. * * @example * ```ts * // Generate bundled declarations in custom directory * const options: DeclarationOptionsInterface = { * outDir: 'types', * bundle: true * }; * ``` * * @see {@link BaseBuildDefinitionInterface.declaration} * * @since 2.0.0 */ interface DeclarationOptionsInterface { /** * Output directory for generated declaration files. * * @remarks * Specifies where `.d.ts` files should be written. If not provided, uses the TypeScript * compiler's `declarationDir` or `outDir` from `tsconfig.json`. * * @example * ```ts * outDir: 'dist/types' * ``` * * @since 2.0.0 */ outDir?: string; /** * Whether to bundle declarations into a single file per entry point. * * @remarks * When true, combines all declarations from imported modules into a single `.d.ts` file. * When false, generates individual declaration files mirroring the source structure. * * Bundling is useful for library distribution as it provides a single type definition file * that consumers can reference. * * @example * ```ts * bundle: true // Produces single bundled .d.ts * bundle: false // Produces multiple .d.ts files * ``` * * @since 2.0.0 */ bundle?: boolean; } /** * Configuration options for TypeScript type checking during builds. * * @remarks * Controls how TypeScript type checking is performed and whether type errors should fail the build. * Type checking runs in parallel with the esbuild compilation process for better performance. * * @example * ```ts * // Fail build on type errors * const options: TypeCheckOptionsInterface = { * failOnError: true * }; * ``` * * @see {@link BaseBuildDefinitionInterface.types} * * @since 2.0.0 */ interface TypeCheckOptionsInterface { /** * Whether to fail the build when TypeScript errors are detected. * * @remarks * When true, any TypeScript errors will cause the build to fail with a non-zero exit code. * When false, errors are logged, but the build continues and succeeds. * * Useful in CI/CD pipelines where type safety must be enforced before deployment. * * @example * ```ts * failOnError: true // Build fails on type errors * failOnError: false // Type errors logged, but build continues * ``` * * @since 2.0.0 */ failOnError?: boolean; } /** * Base configuration shared across all build definitions, including common and variant builds. * Provides common settings for hooks, type checking, code injection, and declaration generation. * * @remarks * This interface defines the foundation for build configuration that applies to both common * settings and individual build variants. Properties defined here can be overridden at the * variant level for customization. * * Configuration inheritance: * - Common build settings apply to all variants * - Variant settings override common settings * - Objects like `define` are merged (variant takes precedence) * - Arrays and primitives replace common values * * @example * ```ts * const base: BaseBuildDefinitionInterface = { * types: { failOnError: true }, * declaration: { bundle: true, outDir: 'types' }, * define: { 'process.env.NODE_ENV': '"production"' }, * banner: 'const x = "test"', * hooks: { * onSuccess: async () => console.log('Build complete!') * } * }; * ``` * * @see {@link CommonBuildInterface} * @see {@link VariantBuildInterface} * @see {@link BuildConfigInterface} * * @since 2.0.0 */ interface BaseBuildDefinitionInterface { /** * Lifecycle hook handlers for build process stages. * * @remarks * Registers custom handlers for various build lifecycle events including start, resolve, * load, end, and success stages. All hooks are optional. * * @see {@link LifecycleHooksInterface} * * @since 2.0.0 */ lifecycle?: LifecycleHooksInterface; /** * TypeScript type checking configuration. * * @remarks * Controls whether and how TypeScript type checking is performed during builds. * - `true`: Enable type checking with default options * - `false` or omitted: Disable type checking * - Object: Enable with specific options like `failOnError` * * @example * ```ts * types: true // Enable with default * types: { failOnError: true } // Enable and fail on errors * types: false // Disable * ``` * * @see {@link TypeCheckOptionsInterface} * * @since 2.0.0 */ types?: boolean | TypeCheckOptionsInterface; /** * Global constants to replace during bundling. * * @remarks * Defines key-value pairs for constant replacement during the build. Keys are identifiers * or property access expressions, values are JSON-stringified replacements. * * Commonly used for environment variables, feature flags, and build-time constants. * * @example * ```ts * define: { * 'process.env.NODE_ENV': '"production"', * 'DEBUG': 'false', * 'VERSION': '"1.2.3"' * } * ``` * * @since 2.0.0 */ define?: Record<string, unknown>; /** * Code to inject at the beginning of each output file. * * @remarks * Can be a static string or a function that generates code based on build context. * Commonly used for copyright notices, license headers, or polyfill imports. * * @example * ```ts * banner: 'const x = "test"' * banner: (name, argv) => `const x = "Built: ${new Date().toISOString()}"` * ``` * * @see {@link InjectableCodeType} * * @since 2.0.0 */ banner?: { [key: string]: InjectableCodeType; }; /** * Code to inject at the end of each output file. * * @remarks * Can be a static string or a function that generates code based on build context. * Commonly used for initialization code, analytics, or polyfills. * * @example * ```ts * footer: '// End of bundle' * footer: (name, argv) => `console.log('Loaded ${name}');` * ``` * * @see {@link InjectableCodeType} * * @since 2.0.0 */ footer?: { [key: string]: InjectableCodeType; }; /** * TypeScript declaration file generation configuration. * * @remarks * Controls whether and how TypeScript declaration files are generated. * - `true`: Generate declarations with default options * - `false` or omitted: Do not generate declarations * - Object: Generate with specific options like `outDir` and `bundle` * * @example * ```ts * declaration: true // Generate with default * declaration: { outDir: 'types', bundle: true } // Generate bundled in custom dir * declaration: false // Disable * ``` * * @see {@link DeclarationOptionsInterface} * * @since 2.0.0 */ declaration?: boolean | DeclarationOptionsInterface; } /** * Build configuration for a specific build variant including esbuild settings and entry points. * Extends base configuration with variant-specific esbuild options and required entry points. * * @remarks * A variant represents a distinct build target with its own entry points and esbuild configuration. * Variants inherit settings from the common configuration but can override any property. * * The `esbuild` property excludes fields that are managed by the build system: * - `plugins`: Managed by the hook provider * - `define`, `banner`, `footer`: Managed by base configuration * - `entryPoints`: Required at variant level (non-nullable) * * Multiple variants enable building different outputs from the same codebase, such as * - Different module formats (ESM, CJS) * - Different targets (Node.js, browser) * - Different bundles (main, worker, tests) * * @example * ```ts * const variant: VariantBuildInterface = { * esbuild: { * entryPoints: ['src/index.ts'], * outdir: 'dist/esm', * format: 'esm', * target: 'es2020' * }, * types: true, * declaration: { bundle: true, outDir: 'dist/types' } * }; * ``` * * @see {@link VariantsType} * @see {@link CommonBuildInterface} * @see {@link BaseBuildDefinitionInterface} * * @since 2.0.0 */ interface VariantBuildInterface extends BaseBuildDefinitionInterface { /** * Esbuild-specific configuration for this variant including entry points. * * @remarks * Contains all esbuild options except those managed by the build system. * The `entryPoints` field is required and must be non-empty to define what to build. * * Common options include: * - `format`: Output format (esm, cjs, iife) * - `outdir` or `outfile`: Output location * - `target`: ECMAScript target version * - `platform`: Target platform (browser, node, neutral) * - `minify`: Whether to minify output * - `sourcemap`: Whether to generate source maps * * @example * ```ts * esbuild: { * entryPoints: ['src/index.ts', 'src/worker.ts'], * outdir: 'dist', * format: 'esm', * target: 'es2020', * minify: true, * sourcemap: true * } * ``` * * @since 2.0.0 */ esbuild: Omit<BuildOptions, 'plugins' | 'define' | 'banner' | 'footer'>; /** * Variants that must finish building before this variant starts. * * @remarks * Use this field to express build ordering between variants when one output * depends on another being completed first. * * You can provide: * - a single variant name * - an array of variant names * * The build system should resolve these dependencies before running the current * variant and should also detect circular dependencies to avoid infinite loops. * * @example * ```ts * dependOn: 'types' * ``` * * @example * ```ts * dependOn: ['types', 'shared'] * ``` * * @see {@link VariantsType} * @since 2.4.0 */ dependOn?: string | Array<string>; } /** * Shared configuration applied to all build variants. * Extends base configuration with esbuild settings but without entry points. * * @remarks * Common configuration provides default settings that apply to all variants unless overridden. * This reduces duplication when multiple variants share similar settings. * * The `esbuild` property excludes managed fields and `entryPoints` (which must be variant-specific). * Settings defined here are merged with variant-specific settings, with variants taking preced