@qlover/fe-release 3.0.0 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2414,6 +2414,31 @@ var BATCH_PR_BODY = "\n## ${name} ${version}\n${changelog}\n";
2414
2414
  // src/implments/ReleaseContext.ts
2415
2415
  var import_scripts_context = require("@qlover/scripts-context");
2416
2416
  var ReleaseContext = class extends import_scripts_context.ScriptContext {
2417
+ /**
2418
+ * Creates a new ReleaseContext instance
2419
+ *
2420
+ * Initializes the context with provided options and sets up
2421
+ * default values for required configuration:
2422
+ * - rootPath: Defaults to current working directory
2423
+ * - sourceBranch: Uses environment variables or default
2424
+ * - releaseEnv: Uses environment variables or 'development'
2425
+ *
2426
+ * Environment Variable Priority:
2427
+ * - sourceBranch: FE_RELEASE_BRANCH > FE_RELEASE_SOURCE_BRANCH > DEFAULT_SOURCE_BRANCH
2428
+ * - releaseEnv: FE_RELEASE_ENV > NODE_ENV > 'development'
2429
+ *
2430
+ * @param name - Unique identifier for this release context
2431
+ * @param options - Configuration options
2432
+ *
2433
+ * @example
2434
+ * ```typescript
2435
+ * const context = new ReleaseContext('web-app', {
2436
+ * rootPath: '/projects/web-app',
2437
+ * sourceBranch: 'main',
2438
+ * releaseEnv: 'production'
2439
+ * });
2440
+ * ```
2441
+ */
2417
2442
  constructor(name, options) {
2418
2443
  super(name, options);
2419
2444
  if (!this.options.rootPath) {
@@ -2430,27 +2455,124 @@ var ReleaseContext = class extends import_scripts_context.ScriptContext {
2430
2455
  });
2431
2456
  }
2432
2457
  }
2458
+ /**
2459
+ * Gets the root path of the project
2460
+ *
2461
+ * @returns Absolute path to project root
2462
+ *
2463
+ * @example
2464
+ * ```typescript
2465
+ * const root = context.rootPath;
2466
+ * // '/path/to/project'
2467
+ * ```
2468
+ */
2433
2469
  get rootPath() {
2434
2470
  return this.getOptions("rootPath");
2435
2471
  }
2472
+ /**
2473
+ * Gets the source branch for the release
2474
+ *
2475
+ * @returns Branch name to use as source
2476
+ *
2477
+ * @example
2478
+ * ```typescript
2479
+ * const branch = context.sourceBranch;
2480
+ * // 'main' or custom branch name
2481
+ * ```
2482
+ */
2436
2483
  get sourceBranch() {
2437
2484
  return this.getOptions("sourceBranch");
2438
2485
  }
2486
+ /**
2487
+ * Gets the release environment
2488
+ *
2489
+ * @returns Environment name (e.g., 'development', 'production')
2490
+ *
2491
+ * @example
2492
+ * ```typescript
2493
+ * const env = context.releaseEnv;
2494
+ * // 'development' or custom environment
2495
+ * ```
2496
+ */
2439
2497
  get releaseEnv() {
2440
2498
  return this.getOptions("releaseEnv");
2441
2499
  }
2500
+ /**
2501
+ * Gets all configured workspaces
2502
+ *
2503
+ * @returns Array of workspace configurations or undefined
2504
+ *
2505
+ * @example
2506
+ * ```typescript
2507
+ * const allWorkspaces = context.workspaces;
2508
+ * // [{ name: 'pkg-a', version: '1.0.0', ... }]
2509
+ * ```
2510
+ */
2442
2511
  get workspaces() {
2443
2512
  return this.getOptions("workspaces.workspaces");
2444
2513
  }
2514
+ /**
2515
+ * Gets the current active workspace
2516
+ *
2517
+ * @returns Current workspace configuration or undefined
2518
+ *
2519
+ * @example
2520
+ * ```typescript
2521
+ * const current = context.workspace;
2522
+ * // { name: 'pkg-a', version: '1.0.0', ... }
2523
+ * ```
2524
+ */
2445
2525
  get workspace() {
2446
2526
  return this.getOptions("workspaces.workspace");
2447
2527
  }
2528
+ /**
2529
+ * Sets the workspace configurations
2530
+ *
2531
+ * Updates the workspace list while preserving other workspace settings
2532
+ *
2533
+ * @param workspaces - Array of workspace configurations
2534
+ *
2535
+ * @example
2536
+ * ```typescript
2537
+ * context.setWorkspaces([{
2538
+ * name: 'pkg-a',
2539
+ * version: '1.0.0',
2540
+ * path: 'packages/a'
2541
+ * }]);
2542
+ * ```
2543
+ */
2448
2544
  setWorkspaces(workspaces) {
2449
2545
  this.options.workspaces = {
2450
2546
  ...this.options.workspaces,
2451
2547
  workspaces
2452
2548
  };
2453
2549
  }
2550
+ /**
2551
+ * Gets package.json data for the current workspace
2552
+ *
2553
+ * Provides type-safe access to package.json fields with optional
2554
+ * path and default value support.
2555
+ *
2556
+ * @param key - Optional dot-notation path to specific field
2557
+ * @param defaultValue - Default value if field not found
2558
+ * @returns Package data of type T
2559
+ * @throws Error if package.json not found
2560
+ *
2561
+ * @example Basic usage
2562
+ * ```typescript
2563
+ * // Get entire package.json
2564
+ * const pkg = context.getPkg();
2565
+ *
2566
+ * // Get specific field
2567
+ * const version = context.getPkg<string>('version');
2568
+ *
2569
+ * // Get nested field with default
2570
+ * const script = context.getPkg<string>(
2571
+ * 'scripts.build',
2572
+ * 'echo "No build script"'
2573
+ * );
2574
+ * ```
2575
+ */
2454
2576
  getPkg(key, defaultValue) {
2455
2577
  const packageJson = this.workspace?.packageJson;
2456
2578
  if (!packageJson) {
@@ -2461,6 +2583,28 @@ var ReleaseContext = class extends import_scripts_context.ScriptContext {
2461
2583
  }
2462
2584
  return (0, import_get.default)(packageJson, key, defaultValue);
2463
2585
  }
2586
+ /**
2587
+ * Generates template context for string interpolation
2588
+ *
2589
+ * Combines context options, workspace data, and specific paths
2590
+ * for use in template processing. Includes deprecated fields
2591
+ * for backward compatibility.
2592
+ *
2593
+ * @returns Combined template context
2594
+ *
2595
+ * @example
2596
+ * ```typescript
2597
+ * const context = releaseContext.getTemplateContext();
2598
+ * // {
2599
+ * // publishPath: 'packages/my-pkg',
2600
+ * // env: 'production', // deprecated
2601
+ * // branch: 'main', // deprecated
2602
+ * // releaseEnv: 'production', // use this instead
2603
+ * // sourceBranch: 'main', // use this instead
2604
+ * // ...other options
2605
+ * // }
2606
+ * ```
2607
+ */
2464
2608
  getTemplateContext() {
2465
2609
  return {
2466
2610
  ...this.getOptions(),
@@ -2471,6 +2615,28 @@ var ReleaseContext = class extends import_scripts_context.ScriptContext {
2471
2615
  branch: this.sourceBranch
2472
2616
  };
2473
2617
  }
2618
+ /**
2619
+ * Executes changeset CLI commands
2620
+ *
2621
+ * Automatically detects and uses appropriate package manager
2622
+ * (pnpm or npx) to run changeset commands.
2623
+ *
2624
+ * @param name - Changeset command name
2625
+ * @param args - Optional command arguments
2626
+ * @returns Command output
2627
+ *
2628
+ * @example Version bump
2629
+ * ```typescript
2630
+ * // Bump version with snapshot
2631
+ * await context.runChangesetsCli('version', ['--snapshot', 'alpha']);
2632
+ *
2633
+ * // Create new changeset
2634
+ * await context.runChangesetsCli('add');
2635
+ *
2636
+ * // Status check
2637
+ * await context.runChangesetsCli('status');
2638
+ * ```
2639
+ */
2474
2640
  async runChangesetsCli(name, args) {
2475
2641
  let packageManager = "pnpm";
2476
2642
  try {
@@ -2505,6 +2671,28 @@ var DEFAULT_RELEASE_CONFIG = {
2505
2671
  batchTagName: "batch-${length}-packages-${timestamp}"
2506
2672
  };
2507
2673
  var ReleaseParams = class {
2674
+ /**
2675
+ * Creates a new ReleaseParams instance
2676
+ *
2677
+ * Initializes with logger for debug output and optional configuration
2678
+ * overrides. Uses default configuration values for any unspecified options.
2679
+ *
2680
+ * @param logger - Logger instance for debug output
2681
+ * @param config - Optional configuration overrides
2682
+ *
2683
+ * @example
2684
+ * ```typescript
2685
+ * // Basic initialization
2686
+ * const params = new ReleaseParams(logger);
2687
+ *
2688
+ * // With custom configuration
2689
+ * const params = new ReleaseParams(logger, {
2690
+ * maxWorkspace: 5,
2691
+ * multiWorkspaceSeparator: '-',
2692
+ * workspaceVersionSeparator: '#'
2693
+ * });
2694
+ * ```
2695
+ */
2508
2696
  constructor(logger, config = {}) {
2509
2697
  this.logger = logger;
2510
2698
  this.config = {
@@ -2512,7 +2700,49 @@ var ReleaseParams = class {
2512
2700
  ...config
2513
2701
  };
2514
2702
  }
2703
+ /**
2704
+ * Current configuration
2705
+ * @private
2706
+ */
2515
2707
  config;
2708
+ /**
2709
+ * Generates a release branch name for a single package
2710
+ *
2711
+ * Uses the branch name template from shared configuration or
2712
+ * falls back to the default template 'release-${tagName}'.
2713
+ * Supports variable interpolation in the template.
2714
+ *
2715
+ * Template Variables:
2716
+ * - ${pkgName}: Package name
2717
+ * - ${releaseName}: Release name (same as pkgName)
2718
+ * - ${tagName}: Release tag
2719
+ * - All shared config properties
2720
+ *
2721
+ * @param releaseName - Name of the package being released
2722
+ * @param tagName - Version tag for the release
2723
+ * @param shared - Shared configuration with branch template
2724
+ * @returns Formatted branch name
2725
+ * @throws Error if branch name template is not a string
2726
+ *
2727
+ * @example
2728
+ * ```typescript
2729
+ * // With default template
2730
+ * const branch = params.getReleaseBranchName(
2731
+ * 'my-pkg',
2732
+ * 'v1.0.0',
2733
+ * {}
2734
+ * );
2735
+ * // 'release-v1.0.0'
2736
+ *
2737
+ * // With custom template
2738
+ * const branch = params.getReleaseBranchName(
2739
+ * 'my-pkg',
2740
+ * 'v1.0.0',
2741
+ * { branchName: '${pkgName}-release-${tagName}' }
2742
+ * );
2743
+ * // 'my-pkg-release-v1.0.0'
2744
+ * ```
2745
+ */
2516
2746
  getReleaseBranchName(releaseName, tagName, shared) {
2517
2747
  const branchNameTpl = shared.branchName || "release-${tagName}";
2518
2748
  if (typeof branchNameTpl !== "string") {
@@ -2527,6 +2757,49 @@ var ReleaseParams = class {
2527
2757
  ...shared
2528
2758
  });
2529
2759
  }
2760
+ /**
2761
+ * Generates a release branch name for multiple packages
2762
+ *
2763
+ * Uses the batch branch name template from configuration.
2764
+ * Supports variable interpolation with additional batch-specific
2765
+ * variables.
2766
+ *
2767
+ * Template Variables:
2768
+ * - ${pkgName}: Package name
2769
+ * - ${releaseName}: Combined release name
2770
+ * - ${tagName}: Combined tag name
2771
+ * - ${length}: Number of packages
2772
+ * - ${timestamp}: Current timestamp
2773
+ * - All shared config properties
2774
+ *
2775
+ * @param releaseName - Combined name of packages
2776
+ * @param tagName - Combined version tag
2777
+ * @param shared - Shared configuration
2778
+ * @param length - Number of packages in batch
2779
+ * @returns Formatted batch branch name
2780
+ * @throws Error if branch name template is not a string
2781
+ *
2782
+ * @example
2783
+ * ```typescript
2784
+ * // With default template
2785
+ * const branch = params.getBatchReleaseBranchName(
2786
+ * 'pkg-a@1.0.0_pkg-b@2.0.0',
2787
+ * 'batch-2-packages-1234567890',
2788
+ * {},
2789
+ * 2
2790
+ * );
2791
+ * // 'batch-pkg-a@1.0.0_pkg-b@2.0.0-2-packages-1234567890'
2792
+ *
2793
+ * // With custom template
2794
+ * const branch = params.getBatchReleaseBranchName(
2795
+ * 'pkg-a@1.0.0_pkg-b@2.0.0',
2796
+ * 'v1.0.0',
2797
+ * {},
2798
+ * 2,
2799
+ * );
2800
+ * // Custom formatted branch name
2801
+ * ```
2802
+ */
2530
2803
  getBatchReleaseBranchName(releaseName, tagName, shared, length) {
2531
2804
  const branchNameTpl = this.config.batchBranchName;
2532
2805
  if (typeof branchNameTpl !== "string") {
@@ -2543,6 +2816,45 @@ var ReleaseParams = class {
2543
2816
  timestamp: Date.now()
2544
2817
  });
2545
2818
  }
2819
+ /**
2820
+ * Generates a release name from workspace configurations
2821
+ *
2822
+ * For single packages, returns the package name.
2823
+ * For multiple packages, combines names and versions up to
2824
+ * the configured maximum number of workspaces.
2825
+ *
2826
+ * Format:
2827
+ * - Single: packageName
2828
+ * - Multiple: pkg1@1.0.0_pkg2@2.0.0_pkg3@3.0.0
2829
+ *
2830
+ * @param composeWorkspaces - Array of workspace configurations
2831
+ * @returns Formatted release name
2832
+ *
2833
+ * @example
2834
+ * ```typescript
2835
+ * // Single package
2836
+ * const name = params.getReleaseName([
2837
+ * { name: 'pkg-a', version: '1.0.0' }
2838
+ * ]);
2839
+ * // 'pkg-a'
2840
+ *
2841
+ * // Multiple packages
2842
+ * const name = params.getReleaseName([
2843
+ * { name: 'pkg-a', version: '1.0.0' },
2844
+ * { name: 'pkg-b', version: '2.0.0' }
2845
+ * ]);
2846
+ * // 'pkg-a@1.0.0_pkg-b@2.0.0'
2847
+ *
2848
+ * // With max limit
2849
+ * const name = params.getReleaseName([
2850
+ * { name: 'pkg-a', version: '1.0.0' },
2851
+ * { name: 'pkg-b', version: '2.0.0' },
2852
+ * { name: 'pkg-c', version: '3.0.0' },
2853
+ * { name: 'pkg-d', version: '4.0.0' }
2854
+ * ]);
2855
+ * // Only first 3: 'pkg-a@1.0.0_pkg-b@2.0.0_pkg-c@3.0.0'
2856
+ * ```
2857
+ */
2546
2858
  getReleaseName(composeWorkspaces) {
2547
2859
  if (composeWorkspaces.length === 1) {
2548
2860
  return composeWorkspaces[0].name;
@@ -2552,6 +2864,36 @@ var ReleaseParams = class {
2552
2864
  ({ name, version }) => `${name}${workspaceVersionSeparator}${version}`
2553
2865
  ).join(multiWorkspaceSeparator);
2554
2866
  }
2867
+ /**
2868
+ * Generates a tag name for the release
2869
+ *
2870
+ * For single packages, uses the package version.
2871
+ * For multiple packages, generates a batch tag name using
2872
+ * the configured template.
2873
+ *
2874
+ * Format:
2875
+ * - Single: version
2876
+ * - Multiple: batch-${length}-packages-${timestamp}
2877
+ *
2878
+ * @param composeWorkspaces - Array of workspace configurations
2879
+ * @returns Formatted tag name
2880
+ *
2881
+ * @example
2882
+ * ```typescript
2883
+ * // Single package
2884
+ * const tag = params.getReleaseTagName([
2885
+ * { name: 'pkg-a', version: '1.0.0' }
2886
+ * ]);
2887
+ * // '1.0.0'
2888
+ *
2889
+ * // Multiple packages
2890
+ * const tag = params.getReleaseTagName([
2891
+ * { name: 'pkg-a', version: '1.0.0' },
2892
+ * { name: 'pkg-b', version: '2.0.0' }
2893
+ * ]);
2894
+ * // 'batch-2-packages-1234567890'
2895
+ * ```
2896
+ */
2555
2897
  getReleaseTagName(composeWorkspaces) {
2556
2898
  if (composeWorkspaces.length === 1) {
2557
2899
  return composeWorkspaces[0].version;
@@ -2562,6 +2904,44 @@ var ReleaseParams = class {
2562
2904
  timestamp: Date.now()
2563
2905
  });
2564
2906
  }
2907
+ /**
2908
+ * Generates branch and tag parameters for the release
2909
+ *
2910
+ * Combines the generation of branch name and tag name into
2911
+ * a single operation. Handles both single and multi-package
2912
+ * releases appropriately.
2913
+ *
2914
+ * @param composeWorkspaces - Array of workspace configurations
2915
+ * @param shared - Shared configuration
2916
+ * @returns Object containing tag name and branch name
2917
+ *
2918
+ * @example Single package
2919
+ * ```typescript
2920
+ * const params = releaseParams.getReleaseBranchParams(
2921
+ * [{ name: 'pkg-a', version: '1.0.0' }],
2922
+ * { branchName: 'release-${tagName}' }
2923
+ * );
2924
+ * // {
2925
+ * // tagName: '1.0.0',
2926
+ * // releaseBranch: 'release-1.0.0'
2927
+ * // }
2928
+ * ```
2929
+ *
2930
+ * @example Multiple packages
2931
+ * ```typescript
2932
+ * const params = releaseParams.getReleaseBranchParams(
2933
+ * [
2934
+ * { name: 'pkg-a', version: '1.0.0' },
2935
+ * { name: 'pkg-b', version: '2.0.0' }
2936
+ * ],
2937
+ * {}
2938
+ * );
2939
+ * // {
2940
+ * // tagName: 'batch-2-packages-1234567890',
2941
+ * // releaseBranch: 'batch-pkg-a@1.0.0_pkg-b@2.0.0-2-packages-1234567890'
2942
+ * // }
2943
+ * ```
2944
+ */
2565
2945
  getReleaseBranchParams(composeWorkspaces, shared) {
2566
2946
  const releaseTagName = this.getReleaseTagName(composeWorkspaces);
2567
2947
  const releaseName = this.getReleaseName(composeWorkspaces);
@@ -2576,6 +2956,50 @@ var ReleaseParams = class {
2576
2956
  releaseBranch: releaseBranchName
2577
2957
  };
2578
2958
  }
2959
+ /**
2960
+ * Generates a title for the release pull request
2961
+ *
2962
+ * Uses the configured PR title template or falls back to the default.
2963
+ * Supports variable interpolation with release parameters and context.
2964
+ *
2965
+ * Template Variables:
2966
+ * - ${tagName}: Release tag name
2967
+ * - ${pkgName}: Package/branch name
2968
+ * - All template context properties
2969
+ *
2970
+ * @param releaseBranchParams - Release branch parameters
2971
+ * @param context - Template context for variable interpolation
2972
+ * @returns Formatted PR title
2973
+ *
2974
+ * @example
2975
+ * ```typescript
2976
+ * // With default template
2977
+ * const title = params.getPRTitle(
2978
+ * {
2979
+ * tagName: '1.0.0',
2980
+ * releaseBranch: 'release-1.0.0'
2981
+ * },
2982
+ * {
2983
+ * env: 'production',
2984
+ * pkgName: 'my-package'
2985
+ * }
2986
+ * );
2987
+ * // 'Release production my-package 1.0.0'
2988
+ *
2989
+ * // With custom template
2990
+ * const title = params.getPRTitle(
2991
+ * {
2992
+ * tagName: '1.0.0',
2993
+ * releaseBranch: 'release-1.0.0'
2994
+ * },
2995
+ * {
2996
+ * env: 'staging',
2997
+ * pkgName: 'my-package'
2998
+ * }
2999
+ * );
3000
+ * // Custom formatted title
3001
+ * ```
3002
+ */
2579
3003
  getPRTitle(releaseBranchParams, context) {
2580
3004
  const prTitleTpl = this.config.PRTitle || DEFAULT_PR_TITLE;
2581
3005
  return import_scripts_context2.Shell.format(prTitleTpl, {
@@ -2585,10 +3009,67 @@ var ReleaseParams = class {
2585
3009
  });
2586
3010
  }
2587
3011
  /**
2588
- * Gets the body for the release pull request.
3012
+ * Generates the body content for the release pull request
3013
+ *
3014
+ * Handles both single and multi-package releases, combining
3015
+ * changelogs appropriately. For batch releases, formats each
3016
+ * package's changelog according to the batch template.
3017
+ *
3018
+ * Template Variables:
3019
+ * - ${tagName}: Release tag or combined workspace versions
3020
+ * - ${changelog}: Single changelog or combined batch changelogs
3021
+ * - All template context properties
3022
+ *
3023
+ * @param composeWorkspaces - Array of workspace configurations
3024
+ * @param releaseBranchParams - Release branch parameters
3025
+ * @param context - Template context for variable interpolation
3026
+ * @returns Formatted PR body content
2589
3027
  *
2590
- * @param options - The options containing tag name and changelog.
2591
- * @returns The formatted release pull request body.
3028
+ * @example Single package
3029
+ * ```typescript
3030
+ * const body = params.getPRBody(
3031
+ * [{
3032
+ * name: 'pkg-a',
3033
+ * version: '1.0.0',
3034
+ * changelog: '- Feature: New functionality\n- Fix: Bug fix'
3035
+ * }],
3036
+ * {
3037
+ * tagName: '1.0.0',
3038
+ * releaseBranch: 'release-1.0.0'
3039
+ * },
3040
+ * context
3041
+ * );
3042
+ * // Custom formatted body with single changelog
3043
+ * ```
3044
+ *
3045
+ * @example Multiple packages
3046
+ * ```typescript
3047
+ * const body = params.getPRBody(
3048
+ * [
3049
+ * {
3050
+ * name: 'pkg-a',
3051
+ * version: '1.0.0',
3052
+ * changelog: '- Feature: Package A changes'
3053
+ * },
3054
+ * {
3055
+ * name: 'pkg-b',
3056
+ * version: '2.0.0',
3057
+ * changelog: '- Feature: Package B changes'
3058
+ * }
3059
+ * ],
3060
+ * {
3061
+ * tagName: 'batch-2-packages-1234567890',
3062
+ * releaseBranch: 'batch-release'
3063
+ * },
3064
+ * context
3065
+ * );
3066
+ * // Formatted body with combined changelogs:
3067
+ * // ## pkg-a 1.0.0
3068
+ * // - Feature: Package A changes
3069
+ * //
3070
+ * // ## pkg-b 2.0.0
3071
+ * // - Feature: Package B changes
3072
+ * ```
2592
3073
  */
2593
3074
  getPRBody(composeWorkspaces, releaseBranchParams, context) {
2594
3075
  const PRBodyTpl = this.config.PRBody;
@@ -2614,10 +3095,36 @@ var ReleaseParams = class {
2614
3095
  var import_scripts_context3 = require("@qlover/scripts-context");
2615
3096
  var import_rest = require("@octokit/rest");
2616
3097
  var GithubManager = class {
3098
+ /**
3099
+ * Creates a new GithubManager instance
3100
+ *
3101
+ * @param context - Release context containing configuration
3102
+ *
3103
+ * @example
3104
+ * ```typescript
3105
+ * const manager = new GithubManager(context);
3106
+ * ```
3107
+ */
2617
3108
  constructor(context) {
2618
3109
  this.context = context;
2619
3110
  }
3111
+ /** Lazy-loaded Octokit instance */
2620
3112
  _octokit = null;
3113
+ /**
3114
+ * Gets GitHub repository information
3115
+ *
3116
+ * Retrieves the owner and repository name from the context.
3117
+ * This information is required for most GitHub API operations.
3118
+ *
3119
+ * @returns Repository owner and name
3120
+ * @throws Error if owner or repo name is not set
3121
+ *
3122
+ * @example
3123
+ * ```typescript
3124
+ * const info = manager.getGitHubUserInfo();
3125
+ * // { owner: 'org-name', repo: 'repo-name' }
3126
+ * ```
3127
+ */
2621
3128
  getGitHubUserInfo() {
2622
3129
  const { authorName, repoName } = this.context.getOptions();
2623
3130
  if (!authorName || !repoName) {
@@ -2628,6 +3135,28 @@ var GithubManager = class {
2628
3135
  repo: repoName
2629
3136
  };
2630
3137
  }
3138
+ /**
3139
+ * Gets GitHub API token
3140
+ *
3141
+ * Retrieves the GitHub API token from environment variables.
3142
+ * The token name can be configured via the tokenRef option.
3143
+ *
3144
+ * @returns GitHub API token
3145
+ * @throws Error if token is not set
3146
+ *
3147
+ * @example Default token
3148
+ * ```typescript
3149
+ * const token = manager.getToken();
3150
+ * // Uses GITHUB_TOKEN env var
3151
+ * ```
3152
+ *
3153
+ * @example Custom token
3154
+ * ```typescript
3155
+ * context.options.githubPR.tokenRef = 'CUSTOM_TOKEN';
3156
+ * const token = manager.getToken();
3157
+ * // Uses CUSTOM_TOKEN env var
3158
+ * ```
3159
+ */
2631
3160
  getToken() {
2632
3161
  const { tokenRef = "GITHUB_TOKEN" } = this.context.getOptions("githubPR");
2633
3162
  const token = this.context.env.get(tokenRef);
@@ -2638,6 +3167,24 @@ var GithubManager = class {
2638
3167
  }
2639
3168
  return token;
2640
3169
  }
3170
+ /**
3171
+ * Gets Octokit instance
3172
+ *
3173
+ * Lazily initializes and returns an Octokit instance configured
3174
+ * with the GitHub token and timeout settings.
3175
+ *
3176
+ * @returns Configured Octokit instance
3177
+ * @throws Error if token retrieval fails
3178
+ *
3179
+ * @example
3180
+ * ```typescript
3181
+ * const octokit = manager.octokit;
3182
+ * await octokit.rest.issues.create({
3183
+ * ...manager.getGitHubUserInfo(),
3184
+ * title: 'Issue title'
3185
+ * });
3186
+ * ```
3187
+ */
2641
3188
  get octokit() {
2642
3189
  if (this._octokit) {
2643
3190
  return this._octokit;
@@ -2652,9 +3199,37 @@ var GithubManager = class {
2652
3199
  this._octokit = new import_rest.Octokit(options);
2653
3200
  return this._octokit;
2654
3201
  }
3202
+ /**
3203
+ * Gets logger instance
3204
+ *
3205
+ * Provides access to the context's logger for consistent
3206
+ * logging across the manager.
3207
+ *
3208
+ * @returns Logger instance
3209
+ *
3210
+ * @example
3211
+ * ```typescript
3212
+ * manager.logger.info('Creating release...');
3213
+ * manager.logger.debug('API response:', response);
3214
+ * ```
3215
+ */
2655
3216
  get logger() {
2656
3217
  return this.context.logger;
2657
3218
  }
3219
+ /**
3220
+ * Gets shell interface
3221
+ *
3222
+ * Provides access to the context's shell interface for
3223
+ * executing Git commands and other shell operations.
3224
+ *
3225
+ * @returns Shell interface
3226
+ *
3227
+ * @example
3228
+ * ```typescript
3229
+ * await manager.shell.exec('git fetch origin');
3230
+ * await manager.shell.exec(['git', 'push', 'origin', 'main']);
3231
+ * ```
3232
+ */
2658
3233
  get shell() {
2659
3234
  return this.context.shell;
2660
3235
  }
@@ -2663,6 +3238,23 @@ var GithubManager = class {
2663
3238
  *
2664
3239
  * @default `squash`
2665
3240
  */
3241
+ /**
3242
+ * Gets auto-merge type for pull requests
3243
+ *
3244
+ * Determines how pull requests should be merged when
3245
+ * auto-merge is enabled. Defaults to 'squash'.
3246
+ *
3247
+ * @returns Auto-merge type ('merge', 'squash', or 'rebase')
3248
+ *
3249
+ * @example
3250
+ * ```typescript
3251
+ * const mergeType = manager.autoMergeType;
3252
+ * // 'squash' (default)
3253
+ *
3254
+ * context.options.autoMergeType = 'rebase';
3255
+ * manager.autoMergeType; // 'rebase'
3256
+ * ```
3257
+ */
2666
3258
  get autoMergeType() {
2667
3259
  return this.context.getOptions().autoMergeType || DEFAULT_AUTO_MERGE_TYPE;
2668
3260
  }
@@ -2671,6 +3263,25 @@ var GithubManager = class {
2671
3263
  *
2672
3264
  * @default `999999`
2673
3265
  */
3266
+ /**
3267
+ * Gets pull request number for dry runs
3268
+ *
3269
+ * Returns a placeholder PR number when running in dry-run mode.
3270
+ * This allows testing PR-related functionality without creating
3271
+ * actual pull requests.
3272
+ *
3273
+ * @returns Dry run PR number (default: '999999')
3274
+ *
3275
+ * @example
3276
+ * ```typescript
3277
+ * context.dryRun = true;
3278
+ * const prNumber = manager.dryRunPRNumber;
3279
+ * // '999999'
3280
+ *
3281
+ * context.options.githubPR.dryRunPRNumber = '123456';
3282
+ * manager.dryRunPRNumber; // '123456'
3283
+ * ```
3284
+ */
2674
3285
  get dryRunPRNumber() {
2675
3286
  return this.context.getOptions("githubPR.dryRunPRNumber", "999999");
2676
3287
  }
@@ -2679,6 +3290,23 @@ var GithubManager = class {
2679
3290
  *
2680
3291
  * @default `false`
2681
3292
  */
3293
+ /**
3294
+ * Gets auto-merge setting for release PRs
3295
+ *
3296
+ * Determines whether release pull requests should be
3297
+ * automatically merged after creation. Defaults to false.
3298
+ *
3299
+ * @returns True if auto-merge is enabled
3300
+ *
3301
+ * @example
3302
+ * ```typescript
3303
+ * const autoMerge = manager.autoMergeReleasePR;
3304
+ * // false (default)
3305
+ *
3306
+ * context.options.autoMergeReleasePR = true;
3307
+ * manager.autoMergeReleasePR; // true
3308
+ * ```
3309
+ */
2682
3310
  get autoMergeReleasePR() {
2683
3311
  return this.context.getOptions("autoMergeReleasePR") || DEFAULT_AUTO_MERGE_RELEASE_PR;
2684
3312
  }
@@ -2688,6 +3316,30 @@ var GithubManager = class {
2688
3316
  * @param prNumber - The pull request number to merge.
2689
3317
  * @param releaseBranch - The branch to merge into.
2690
3318
  */
3319
+ /**
3320
+ * Merges a pull request
3321
+ *
3322
+ * Merges the specified pull request using the configured
3323
+ * merge method. In dry-run mode, logs the merge action
3324
+ * without performing it.
3325
+ *
3326
+ * @param prNumber - Pull request number
3327
+ * @param releaseBranch - Branch to merge
3328
+ * @throws Error if merge fails
3329
+ *
3330
+ * @example Basic merge
3331
+ * ```typescript
3332
+ * await manager.mergePR('123', 'release-1.0.0');
3333
+ * // Merges PR #123 using configured merge method
3334
+ * ```
3335
+ *
3336
+ * @example Dry run
3337
+ * ```typescript
3338
+ * context.dryRun = true;
3339
+ * await manager.mergePR('123', 'release-1.0.0');
3340
+ * // Logs merge action without performing it
3341
+ * ```
3342
+ */
2691
3343
  async mergePR(prNumber, releaseBranch) {
2692
3344
  if (!prNumber) {
2693
3345
  this.logger.error("Failed to create Pull Request.", prNumber);
@@ -2707,6 +3359,24 @@ var GithubManager = class {
2707
3359
  merge_method: mergeMethod
2708
3360
  });
2709
3361
  }
3362
+ /**
3363
+ * Gets commits from a pull request
3364
+ *
3365
+ * Retrieves all commits associated with the specified pull request.
3366
+ * Useful for generating changelogs or analyzing changes.
3367
+ *
3368
+ * @param prNumber - Pull request number
3369
+ * @returns Promise resolving to array of commit information
3370
+ * @throws Error if request fails
3371
+ *
3372
+ * @example
3373
+ * ```typescript
3374
+ * const commits = await manager.getPullRequestCommits(123);
3375
+ * commits.forEach(commit => {
3376
+ * console.log(commit.sha, commit.commit.message);
3377
+ * });
3378
+ * ```
3379
+ */
2710
3380
  async getPullRequestCommits(prNumber) {
2711
3381
  const pr = await this.octokit.rest.pulls.listCommits({
2712
3382
  ...this.getGitHubUserInfo(),
@@ -2714,6 +3384,23 @@ var GithubManager = class {
2714
3384
  });
2715
3385
  return pr.data;
2716
3386
  }
3387
+ /**
3388
+ * Gets detailed information about a commit
3389
+ *
3390
+ * Retrieves detailed information about a specific commit,
3391
+ * including files changed, author details, and commit message.
3392
+ *
3393
+ * @param commitSha - Commit SHA
3394
+ * @returns Promise resolving to commit information
3395
+ * @throws Error if request fails
3396
+ *
3397
+ * @example
3398
+ * ```typescript
3399
+ * const info = await manager.getCommitInfo('abc123');
3400
+ * console.log(info.commit.message);
3401
+ * console.log(info.files.map(f => f.filename));
3402
+ * ```
3403
+ */
2717
3404
  async getCommitInfo(commitSha) {
2718
3405
  const pr = await this.octokit.rest.repos.getCommit({
2719
3406
  ...this.getGitHubUserInfo(),
@@ -2721,6 +3408,24 @@ var GithubManager = class {
2721
3408
  });
2722
3409
  return pr.data;
2723
3410
  }
3411
+ /**
3412
+ * Gets pull request information
3413
+ *
3414
+ * Retrieves detailed information about a pull request,
3415
+ * including title, body, labels, and review status.
3416
+ *
3417
+ * @param prNumber - Pull request number
3418
+ * @returns Promise resolving to pull request information
3419
+ * @throws Error if request fails
3420
+ *
3421
+ * @example
3422
+ * ```typescript
3423
+ * const pr = await manager.getPullRequest(123);
3424
+ * console.log(pr.title);
3425
+ * console.log(pr.labels.map(l => l.name));
3426
+ * console.log(pr.mergeable_state);
3427
+ * ```
3428
+ */
2724
3429
  async getPullRequest(prNumber) {
2725
3430
  const pr = await this.octokit.rest.pulls.get({
2726
3431
  ...this.getGitHubUserInfo(),
@@ -2734,6 +3439,27 @@ var GithubManager = class {
2734
3439
  * @param prNumber - The pull request number to check.
2735
3440
  * @param releaseBranch - The branch to check against.
2736
3441
  */
3442
+ /**
3443
+ * Checks pull request status and cleans up
3444
+ *
3445
+ * Verifies pull request status and deletes the release branch
3446
+ * if the PR has been merged. Used for post-merge cleanup.
3447
+ *
3448
+ * Process:
3449
+ * 1. Verify PR exists and status
3450
+ * 2. Delete release branch if PR merged
3451
+ * 3. Log cleanup results
3452
+ *
3453
+ * @param prNumber - Pull request number
3454
+ * @param releaseBranch - Branch to clean up
3455
+ * @throws Error if verification or cleanup fails
3456
+ *
3457
+ * @example
3458
+ * ```typescript
3459
+ * await manager.checkedPR('123', 'release-1.0.0');
3460
+ * // Verifies PR #123 and deletes release-1.0.0 if merged
3461
+ * ```
3462
+ */
2737
3463
  async checkedPR(prNumber, releaseBranch) {
2738
3464
  try {
2739
3465
  await this.getPullRequest(Number(prNumber));
@@ -2834,10 +3560,55 @@ var GithubManager = class {
2834
3560
  throw error;
2835
3561
  }
2836
3562
  }
3563
+ /**
3564
+ * Truncates long PR/release body text
3565
+ *
3566
+ * GitHub has a limit on PR and release body length.
3567
+ * This method ensures the text stays within limits by
3568
+ * truncating if necessary.
3569
+ *
3570
+ * @param body - Body text to truncate
3571
+ * @returns Truncated text (if > 124000 chars)
3572
+ *
3573
+ * @example
3574
+ * ```typescript
3575
+ * const body = manager.truncateBody(veryLongText);
3576
+ * // Returns truncated text if > 124000 chars
3577
+ * // Adds '...' to indicate truncation
3578
+ * ```
3579
+ * @private
3580
+ */
2837
3581
  truncateBody(body) {
2838
3582
  if (body && body.length >= 124e3) return body.substring(0, 124e3) + "...";
2839
3583
  return body;
2840
3584
  }
3585
+ /**
3586
+ * Builds GitHub release options
3587
+ *
3588
+ * Combines default release options with provided overrides
3589
+ * and context configuration. Handles formatting of release
3590
+ * name, body, and other settings.
3591
+ *
3592
+ * @param options - Override options for release
3593
+ * @returns Complete release options
3594
+ *
3595
+ * @example
3596
+ * ```typescript
3597
+ * const opts = manager.getOctokitReleaseOptions({
3598
+ * tag_name: 'v1.0.0',
3599
+ * body: 'Release notes...'
3600
+ * });
3601
+ * // Returns merged options with defaults:
3602
+ * // {
3603
+ * // name: 'Release v1.0.0',
3604
+ * // body: 'Release notes...',
3605
+ * // draft: false,
3606
+ * // prerelease: false,
3607
+ * // ...
3608
+ * // }
3609
+ * ```
3610
+ * @private
3611
+ */
2841
3612
  getOctokitReleaseOptions(options) {
2842
3613
  const {
2843
3614
  releaseName,
@@ -2863,6 +3634,37 @@ var GithubManager = class {
2863
3634
  ...this.getGitHubUserInfo()
2864
3635
  };
2865
3636
  }
3637
+ /**
3638
+ * Creates a GitHub release
3639
+ *
3640
+ * Creates a new GitHub release for a workspace with:
3641
+ * - Formatted release name
3642
+ * - Changelog as release notes
3643
+ * - Proper tag
3644
+ * - Configurable settings (draft, prerelease, etc.)
3645
+ *
3646
+ * Handles dry run mode and error cases gracefully.
3647
+ *
3648
+ * @param workspace - Workspace to create release for
3649
+ * @throws Error if tag name is missing or creation fails
3650
+ *
3651
+ * @example Basic release
3652
+ * ```typescript
3653
+ * await manager.createRelease({
3654
+ * name: 'pkg-a',
3655
+ * version: '1.0.0',
3656
+ * tagName: 'v1.0.0',
3657
+ * changelog: '...'
3658
+ * });
3659
+ * ```
3660
+ *
3661
+ * @example Dry run
3662
+ * ```typescript
3663
+ * context.dryRun = true;
3664
+ * await manager.createRelease(workspace);
3665
+ * // Logs release info without creating
3666
+ * ```
3667
+ */
2866
3668
  async createRelease(workspace) {
2867
3669
  const meragedOptions = this.getOctokitReleaseOptions({
2868
3670
  tag_name: workspace.tagName,
@@ -2902,6 +3704,30 @@ var GithubManager = class {
2902
3704
  var import_isString = __toESM(require_isString(), 1);
2903
3705
  var import_scripts_context4 = require("@qlover/scripts-context");
2904
3706
  var GitBase = class extends import_scripts_context4.ScriptPlugin {
3707
+ /**
3708
+ * Plugin initialization hook
3709
+ *
3710
+ * Runs before plugin execution to set up repository context:
3711
+ * 1. Retrieves repository information
3712
+ * 2. Gets current branch
3713
+ * 3. Switches to current branch if needed
3714
+ * 4. Updates context with repository info
3715
+ *
3716
+ * @throws Error if repository information cannot be retrieved
3717
+ *
3718
+ * @example
3719
+ * ```typescript
3720
+ * class MyPlugin extends GitBase<GitBaseProps> {
3721
+ * async onExec() {
3722
+ * // onBefore has already:
3723
+ * // - Set up repository info
3724
+ * // - Switched to correct branch
3725
+ * // - Updated context
3726
+ * await this.doSomething();
3727
+ * }
3728
+ * }
3729
+ * ```
3730
+ */
2905
3731
  async onBefore() {
2906
3732
  const repoInfo = await this.getUserInfo();
2907
3733
  if (!repoInfo) {
@@ -2922,12 +3748,44 @@ var GitBase = class extends import_scripts_context4.ScriptPlugin {
2922
3748
  currentBranch
2923
3749
  });
2924
3750
  }
3751
+ /**
3752
+ * Gets the current Git branch name
3753
+ *
3754
+ * Retrieves the name of the currently checked out Git branch.
3755
+ * Includes a small delay to ensure Git's internal state is updated.
3756
+ *
3757
+ * @returns Promise resolving to branch name
3758
+ * @throws Error if branch name cannot be retrieved
3759
+ *
3760
+ * @example
3761
+ * ```typescript
3762
+ * const branch = await plugin.getCurrentBranch();
3763
+ * // 'main' or 'feature/new-feature'
3764
+ * ```
3765
+ */
2925
3766
  async getCurrentBranch() {
2926
3767
  await new Promise((resolve2) => setTimeout(resolve2, 100));
2927
3768
  return this.context.shell.exec("git rev-parse --abbrev-ref HEAD", {
2928
3769
  dryRun: false
2929
3770
  });
2930
3771
  }
3772
+ /**
3773
+ * Gets the Git remote URL
3774
+ *
3775
+ * Retrieves the URL of the 'origin' remote from Git configuration.
3776
+ * This URL is used to identify the GitHub repository.
3777
+ *
3778
+ * @returns Promise resolving to remote URL
3779
+ * @throws Error if remote URL cannot be retrieved
3780
+ *
3781
+ * @example
3782
+ * ```typescript
3783
+ * const url = await plugin.getRemoteUrl();
3784
+ * // 'https://github.com/org/repo.git'
3785
+ * // or
3786
+ * // 'git@github.com:org/repo.git'
3787
+ * ```
3788
+ */
2931
3789
  async getRemoteUrl() {
2932
3790
  return (await this.context.shell.exec("git config --get remote.origin.url", {
2933
3791
  dryRun: false
@@ -2981,9 +3839,53 @@ var GitBase = class extends import_scripts_context4.ScriptPlugin {
2981
3839
  * @param value - The value to check.
2982
3840
  * @returns True if the value is a valid string, otherwise false.
2983
3841
  */
3842
+ /**
3843
+ * Type guard for valid string values
3844
+ *
3845
+ * Checks if a value is a non-empty string. Used for validating
3846
+ * repository information and other string inputs.
3847
+ *
3848
+ * @param value - Value to check
3849
+ * @returns True if value is a non-empty string
3850
+ *
3851
+ * @example
3852
+ * ```typescript
3853
+ * if (plugin.isValidString(value)) {
3854
+ * // value is definitely a non-empty string
3855
+ * console.log(value.toUpperCase());
3856
+ * }
3857
+ * ```
3858
+ */
2984
3859
  isValidString(value) {
2985
3860
  return !!value && (0, import_isString.default)(value);
2986
3861
  }
3862
+ /**
3863
+ * Creates a Git commit
3864
+ *
3865
+ * Creates a new Git commit with the specified message and optional
3866
+ * additional arguments. The message is automatically JSON-stringified
3867
+ * to handle special characters properly.
3868
+ *
3869
+ * @param message - Commit message
3870
+ * @param args - Additional Git commit arguments
3871
+ * @returns Promise resolving to command output
3872
+ *
3873
+ * @example Basic commit
3874
+ * ```typescript
3875
+ * await plugin.commit('feat: add new feature');
3876
+ * ```
3877
+ *
3878
+ * @example Commit with arguments
3879
+ * ```typescript
3880
+ * await plugin.commit('fix: update deps', ['--no-verify']);
3881
+ * ```
3882
+ *
3883
+ * @example Commit with special characters
3884
+ * ```typescript
3885
+ * await plugin.commit('fix: handle "quotes" & symbols');
3886
+ * // Message is automatically escaped
3887
+ * ```
3888
+ */
2987
3889
  commit(message, args = []) {
2988
3890
  return this.context.shell.exec([
2989
3891
  "git",
@@ -3018,14 +3920,50 @@ var CHANGELOG_ALL_FIELDS = [
3018
3920
  "tag"
3019
3921
  ];
3020
3922
  var GitChangelog = class {
3923
+ /**
3924
+ * Creates a new GitChangelog instance
3925
+ *
3926
+ * @param options - Configuration options including shell and logger
3927
+ *
3928
+ * @example
3929
+ * ```typescript
3930
+ * const changelog = new GitChangelog({
3931
+ * shell: new Shell(),
3932
+ * logger: new Logger(),
3933
+ * directory: 'packages/my-pkg',
3934
+ * noMerges: true
3935
+ * });
3936
+ * ```
3937
+ */
3021
3938
  constructor(options) {
3022
3939
  this.options = options;
3023
3940
  }
3024
3941
  /**
3025
- * Get the git log
3942
+ * Retrieves Git commit history with specified options
3943
+ *
3944
+ * Fetches commit information between specified tags or commits,
3945
+ * with support for filtering and field selection.
3946
+ *
3947
+ * @param options - Configuration options for Git log retrieval
3948
+ * @returns Array of commit objects with requested fields
3026
3949
  *
3027
- * @param options
3028
- * @returns
3950
+ * @example Basic usage
3951
+ * ```typescript
3952
+ * const commits = await changelog.getGitLog({
3953
+ * from: 'v1.0.0',
3954
+ * to: 'v2.0.0',
3955
+ * directory: 'packages/my-pkg',
3956
+ * noMerges: true
3957
+ * });
3958
+ * ```
3959
+ *
3960
+ * @example Custom fields
3961
+ * ```typescript
3962
+ * const commits = await changelog.getGitLog({
3963
+ * fields: ['hash', 'subject', 'authorName'],
3964
+ * directory: 'src'
3965
+ * });
3966
+ * ```
3029
3967
  */
3030
3968
  async getGitLog(options = {}) {
3031
3969
  const { directory, noMerges = true, fields } = options;
@@ -3046,6 +3984,38 @@ var GitChangelog = class {
3046
3984
  this.options.logger?.debug("GitChangelog commits", commits);
3047
3985
  return commits;
3048
3986
  }
3987
+ /**
3988
+ * Retrieves and parses Git commits with metadata
3989
+ *
3990
+ * Gets commit history and enhances it with parsed conventional
3991
+ * commit information and PR metadata.
3992
+ *
3993
+ * @param options - Configuration options for Git log retrieval
3994
+ * @returns Array of enhanced commit objects with parsed metadata
3995
+ *
3996
+ * @example Basic usage
3997
+ * ```typescript
3998
+ * const commits = await changelog.getCommits({
3999
+ * from: 'v1.0.0',
4000
+ * to: 'v2.0.0'
4001
+ * });
4002
+ * // [
4003
+ * // {
4004
+ * // base: { hash: '...', subject: '...' },
4005
+ * // commitlint: { type: 'feat', scope: 'api', ... },
4006
+ * // commits: []
4007
+ * // }
4008
+ * // ]
4009
+ * ```
4010
+ *
4011
+ * @example Filtered commits
4012
+ * ```typescript
4013
+ * const commits = await changelog.getCommits({
4014
+ * directory: 'packages/my-pkg',
4015
+ * noMerges: true
4016
+ * });
4017
+ * ```
4018
+ */
3049
4019
  async getCommits(options) {
3050
4020
  const gitCommits = await this.getGitLog(options);
3051
4021
  return gitCommits.map((commit) => {
@@ -3058,6 +4028,28 @@ var GitChangelog = class {
3058
4028
  };
3059
4029
  });
3060
4030
  }
4031
+ /**
4032
+ * Creates a base commit object from message and optional data
4033
+ *
4034
+ * Utility method to create a standardized commit object with
4035
+ * basic metadata. Used internally for commit value creation.
4036
+ *
4037
+ * @param message - Commit message
4038
+ * @param target - Optional additional commit data
4039
+ * @returns Base commit object
4040
+ * @protected
4041
+ *
4042
+ * @example
4043
+ * ```typescript
4044
+ * const commit = changelog.createBaseCommit(
4045
+ * 'feat: new feature',
4046
+ * {
4047
+ * hash: 'abc123',
4048
+ * authorName: 'John Doe'
4049
+ * }
4050
+ * );
4051
+ * ```
4052
+ */
3061
4053
  createBaseCommit(message, target) {
3062
4054
  return {
3063
4055
  subject: message,
@@ -3067,16 +4059,66 @@ var GitChangelog = class {
3067
4059
  };
3068
4060
  }
3069
4061
  /**
3070
- * Tabify the body
4062
+ * Indents each line of a text block
4063
+ *
4064
+ * Adds specified number of spaces to the start of each line
4065
+ * in a multi-line string. Used for formatting commit body text.
3071
4066
  *
3072
4067
  * @since 2.3.2
3073
- * @param body
3074
- * @param size
3075
- * @returns
4068
+ * @param body - Text to indent
4069
+ * @param size - Number of spaces to add (default: 2)
4070
+ * @returns Indented text
4071
+ *
4072
+ * @example
4073
+ * ```typescript
4074
+ * const text = changelog.tabify(
4075
+ * 'Line 1\nLine 2\nLine 3',
4076
+ * 4
4077
+ * );
4078
+ * // ' Line 1\n Line 2\n Line 3'
4079
+ * ```
3076
4080
  */
3077
4081
  tabify(body, size = 2) {
3078
4082
  return body.split("\n").map((line) => " ".repeat(size) + line.trim()).join("\n");
3079
4083
  }
4084
+ /**
4085
+ * Parses a commit message into conventional commit format
4086
+ *
4087
+ * Extracts type, scope, message, and body from a commit message
4088
+ * following the conventional commit specification.
4089
+ *
4090
+ * Format: type(scope): message
4091
+ *
4092
+ * @param subject - Commit subject line
4093
+ * @param rawBody - Full commit message body
4094
+ * @returns Parsed conventional commit data
4095
+ *
4096
+ * @example Basic commit
4097
+ * ```typescript
4098
+ * const commit = changelog.parseCommitlint(
4099
+ * 'feat(api): add new endpoint'
4100
+ * );
4101
+ * // {
4102
+ * // type: 'feat',
4103
+ * // scope: 'api',
4104
+ * // message: 'add new endpoint'
4105
+ * // }
4106
+ * ```
4107
+ *
4108
+ * @example With body
4109
+ * ```typescript
4110
+ * const commit = changelog.parseCommitlint(
4111
+ * 'fix(core): memory leak',
4112
+ * 'Fixed memory leak in core module\n\nBREAKING CHANGE: API changed'
4113
+ * );
4114
+ * // {
4115
+ * // type: 'fix',
4116
+ * // scope: 'core',
4117
+ * // message: 'memory leak',
4118
+ * // body: ' Fixed memory leak in core module\n\n BREAKING CHANGE: API changed'
4119
+ * // }
4120
+ * ```
4121
+ */
3080
4122
  parseCommitlint(subject, rawBody = "") {
3081
4123
  const [title] = subject.trim().split("\n");
3082
4124
  const bodyLines = rawBody.startsWith(title) ? rawBody.replace(title, "") : rawBody;
@@ -3095,6 +4137,51 @@ var GitChangelog = class {
3095
4137
  body: bodyLines ? this.tabify(bodyLines) : void 0
3096
4138
  };
3097
4139
  }
4140
+ /**
4141
+ * Creates a complete commit value object from hash and message
4142
+ *
4143
+ * Combines commit hash, parsed conventional commit data, and
4144
+ * PR information into a single commit value object.
4145
+ *
4146
+ * @param hash - Commit hash
4147
+ * @param message - Full commit message
4148
+ * @returns Complete commit value object
4149
+ *
4150
+ * @example Basic commit
4151
+ * ```typescript
4152
+ * const commit = changelog.toCommitValue(
4153
+ * 'abc123',
4154
+ * 'feat(api): new endpoint'
4155
+ * );
4156
+ * // {
4157
+ * // base: {
4158
+ * // hash: 'abc123',
4159
+ * // abbrevHash: 'abc123',
4160
+ * // subject: 'feat(api): new endpoint'
4161
+ * // },
4162
+ * // commitlint: {
4163
+ * // type: 'feat',
4164
+ * // scope: 'api',
4165
+ * // message: 'new endpoint'
4166
+ * // },
4167
+ * // commits: []
4168
+ * // }
4169
+ * ```
4170
+ *
4171
+ * @example PR commit
4172
+ * ```typescript
4173
+ * const commit = changelog.toCommitValue(
4174
+ * 'def456',
4175
+ * 'fix(core): memory leak (#123)'
4176
+ * );
4177
+ * // {
4178
+ * // base: { hash: 'def456', ... },
4179
+ * // commitlint: { type: 'fix', ... },
4180
+ * // commits: [],
4181
+ * // prNumber: '123'
4182
+ * // }
4183
+ * ```
4184
+ */
3098
4185
  toCommitValue(hash, message) {
3099
4186
  const [title] = message.trim().split("\n");
3100
4187
  const prMatch = title.match(/\(#(\d+)\)/);
@@ -3112,6 +4199,33 @@ var GitChangelog = class {
3112
4199
  prNumber: prMatch?.[1]
3113
4200
  };
3114
4201
  }
4202
+ /**
4203
+ * Resolves a Git tag or reference to a valid commit reference
4204
+ *
4205
+ * Attempts to resolve a tag name to a valid Git reference.
4206
+ * Falls back to root commit or HEAD if tag doesn't exist.
4207
+ *
4208
+ * @param tag - Tag name to resolve
4209
+ * @param fallback - Fallback value ('root' or 'HEAD')
4210
+ * @returns Resolved Git reference
4211
+ * @protected
4212
+ *
4213
+ * @example Basic tag resolution
4214
+ * ```typescript
4215
+ * const ref = await changelog.resolveTag('v1.0.0');
4216
+ * // 'v1.0.0' if tag exists
4217
+ * // 'HEAD' if tag doesn't exist
4218
+ * ```
4219
+ *
4220
+ * @example Root commit fallback
4221
+ * ```typescript
4222
+ * const ref = await changelog.resolveTag(
4223
+ * 'non-existent-tag',
4224
+ * 'root'
4225
+ * );
4226
+ * // First commit hash if tag doesn't exist
4227
+ * ```
4228
+ */
3115
4229
  async resolveTag(tag, fallback) {
3116
4230
  if (tag) {
3117
4231
  try {
@@ -3132,9 +4246,75 @@ var import_scripts_context5 = require("@qlover/scripts-context");
3132
4246
  var import_groupBy = __toESM(require_groupBy(), 1);
3133
4247
  var DEFAULT_TEMPLATE = "\n- ${scopeHeader} ${commitlint.message} ${commitLink} ${prLink}";
3134
4248
  var GitChangelogFormatter = class {
4249
+ /**
4250
+ * Creates a new GitChangelogFormatter instance
4251
+ *
4252
+ * @param options - Configuration options including shell interface
4253
+ *
4254
+ * @example
4255
+ * ```typescript
4256
+ * const formatter = new GitChangelogFormatter({
4257
+ * shell: new Shell(),
4258
+ * repoUrl: 'https://github.com/org/repo',
4259
+ * types: [
4260
+ * { type: 'feat', section: '### Features' }
4261
+ * ],
4262
+ * formatTemplate: '- ${commitlint.message}'
4263
+ * });
4264
+ * ```
4265
+ */
3135
4266
  constructor(options) {
3136
4267
  this.options = options;
3137
4268
  }
4269
+ /**
4270
+ * Formats an array of commits into changelog entries
4271
+ *
4272
+ * Groups commits by type and formats them according to the
4273
+ * configured template and options. Supports commit body
4274
+ * inclusion and type-based sections.
4275
+ *
4276
+ * @param commits - Array of commit values to format
4277
+ * @param options - Optional formatting options
4278
+ * @returns Array of formatted changelog lines
4279
+ *
4280
+ * @example Basic formatting
4281
+ * ```typescript
4282
+ * const changelog = formatter.format([
4283
+ * {
4284
+ * base: { hash: 'abc123' },
4285
+ * commitlint: {
4286
+ * type: 'feat',
4287
+ * scope: 'api',
4288
+ * message: 'new endpoint'
4289
+ * }
4290
+ * }
4291
+ * ]);
4292
+ * // [
4293
+ * // '### Features',
4294
+ * // '- **api:** new endpoint ([abc123](...))'
4295
+ * // ]
4296
+ * ```
4297
+ *
4298
+ * @example With commit body
4299
+ * ```typescript
4300
+ * const changelog = formatter.format(
4301
+ * [{
4302
+ * commitlint: {
4303
+ * type: 'fix',
4304
+ * message: 'memory leak',
4305
+ * body: 'Fixed memory allocation\nAdded cleanup'
4306
+ * }
4307
+ * }],
4308
+ * { commitBody: true }
4309
+ * );
4310
+ * // [
4311
+ * // '### Bug Fixes',
4312
+ * // '- memory leak',
4313
+ * // ' Fixed memory allocation',
4314
+ * // ' Added cleanup'
4315
+ * // ]
4316
+ * ```
4317
+ */
3138
4318
  format(commits, options) {
3139
4319
  const { types = [], commitBody = false } = { ...this.options, ...options };
3140
4320
  const changelog = [];
@@ -3161,6 +4341,41 @@ var GitChangelogFormatter = class {
3161
4341
  });
3162
4342
  return changelog;
3163
4343
  }
4344
+ /**
4345
+ * Formats a single commit into a changelog entry
4346
+ *
4347
+ * Applies the configured template to a commit, including
4348
+ * scope formatting, PR links, and commit hash links.
4349
+ *
4350
+ * @param commit - Commit value to format
4351
+ * @param options - Optional formatting options
4352
+ * @returns Formatted changelog entry
4353
+ *
4354
+ * @example Basic formatting
4355
+ * ```typescript
4356
+ * const entry = formatter.formatCommit({
4357
+ * base: { hash: 'abc123' },
4358
+ * commitlint: {
4359
+ * type: 'feat',
4360
+ * scope: 'api',
4361
+ * message: 'new endpoint'
4362
+ * }
4363
+ * });
4364
+ * // '- **api:** new endpoint ([abc123](...))'
4365
+ * ```
4366
+ *
4367
+ * @example With PR number
4368
+ * ```typescript
4369
+ * const entry = formatter.formatCommit({
4370
+ * base: { hash: 'def456' },
4371
+ * commitlint: {
4372
+ * message: 'fix bug'
4373
+ * },
4374
+ * prNumber: '123'
4375
+ * });
4376
+ * // '- fix bug ([def456](...)) (#123)'
4377
+ * ```
4378
+ */
3164
4379
  formatCommit(commit, options) {
3165
4380
  const {
3166
4381
  commitlint,
@@ -3187,12 +4402,65 @@ var GitChangelogFormatter = class {
3187
4402
  prLink
3188
4403
  });
3189
4404
  }
4405
+ /**
4406
+ * Formats a target string as a Markdown link
4407
+ *
4408
+ * Creates a Markdown-formatted link with optional URL.
4409
+ * If no URL is provided, formats as a plain reference.
4410
+ *
4411
+ * @param target - Text to display
4412
+ * @param url - Optional URL for the link
4413
+ * @returns Formatted Markdown link
4414
+ *
4415
+ * @example With URL
4416
+ * ```typescript
4417
+ * const link = formatter.foramtLink('abc123', 'https://github.com/org/repo/commit/abc123');
4418
+ * // '([abc123](https://github.com/org/repo/commit/abc123))'
4419
+ * ```
4420
+ *
4421
+ * @example Without URL
4422
+ * ```typescript
4423
+ * const link = formatter.foramtLink('abc123');
4424
+ * // '(abc123)'
4425
+ * ```
4426
+ */
3190
4427
  foramtLink(target, url) {
3191
4428
  return url ? `([${target}](${url}))` : `(${target})`;
3192
4429
  }
4430
+ /**
4431
+ * Formats a commit hash as a Markdown link
4432
+ *
4433
+ * @deprecated Use foramtLink instead
4434
+ * @param target - Commit hash to display
4435
+ * @param url - Optional URL to the commit
4436
+ * @returns Formatted Markdown link
4437
+ *
4438
+ * @example
4439
+ * ```typescript
4440
+ * const link = formatter.formatCommitLink(
4441
+ * 'abc123',
4442
+ * 'https://github.com/org/repo/commit/abc123'
4443
+ * );
4444
+ * // '([abc123](https://github.com/org/repo/commit/abc123))'
4445
+ * ```
4446
+ */
3193
4447
  formatCommitLink(target, url) {
3194
4448
  return url ? `([${target}](${url}))` : `(${target})`;
3195
4449
  }
4450
+ /**
4451
+ * Formats a commit scope in Markdown
4452
+ *
4453
+ * Wraps the scope in bold syntax and adds a colon.
4454
+ *
4455
+ * @param scope - Scope to format
4456
+ * @returns Formatted scope in Markdown
4457
+ *
4458
+ * @example
4459
+ * ```typescript
4460
+ * const scope = formatter.formatScope('api');
4461
+ * // '**api:**'
4462
+ * ```
4463
+ */
3196
4464
  formatScope(scope) {
3197
4465
  return `**${scope}:**`;
3198
4466
  }
@@ -3246,7 +4514,30 @@ var Pather = class {
3246
4514
  return child[boundaryIndex] === import_node_path.sep;
3247
4515
  }
3248
4516
  /**
3249
- * Normalised `startsWith` helper.
4517
+ * Normalized path prefix check
4518
+ *
4519
+ * Checks if sourcePath starts with targetPath after normalization.
4520
+ * Handles cross-platform path separators and trailing separators.
4521
+ *
4522
+ * @param sourcePath - Path to check
4523
+ * @param targetPath - Prefix path to match
4524
+ * @returns True if sourcePath starts with targetPath
4525
+ *
4526
+ * @example Basic usage
4527
+ * ```typescript
4528
+ * const pather = new Pather();
4529
+ *
4530
+ * pather.startsWith('src/utils/file.ts', 'src') // true
4531
+ * pather.startsWith('src\\utils\\file.ts', 'src') // true
4532
+ * pather.startsWith('lib/utils/file.ts', 'src') // false
4533
+ * ```
4534
+ *
4535
+ * @example Trailing separators
4536
+ * ```typescript
4537
+ * pather.startsWith('src/utils', 'src/') // true
4538
+ * pather.startsWith('src/utils/', 'src') // true
4539
+ * pather.startsWith('src2/utils', 'src') // false
4540
+ * ```
3250
4541
  */
3251
4542
  startsWith(sourcePath, targetPath) {
3252
4543
  let src = this.toLocalPath(sourcePath);
@@ -3260,7 +4551,44 @@ var Pather = class {
3260
4551
  return src.startsWith(tgt);
3261
4552
  }
3262
4553
  /**
3263
- * Segment-aware containment check (not mere substring).
4554
+ * Segment-aware path containment check
4555
+ *
4556
+ * Checks if sourcePath contains targetPath as a complete path segment.
4557
+ * Unlike simple substring matching, this ensures proper path boundaries.
4558
+ * For example, 'src/abc' does not contain 'src/a' even though 'src/a'
4559
+ * is a substring.
4560
+ *
4561
+ * Features:
4562
+ * - Cross-platform path handling
4563
+ * - Proper segment boundary checking
4564
+ * - Trailing separator normalization
4565
+ * - Exact match support
4566
+ *
4567
+ * @param sourcePath - Path to search in
4568
+ * @param targetPath - Path to search for
4569
+ * @returns True if sourcePath contains targetPath as a segment
4570
+ *
4571
+ * @example Basic usage
4572
+ * ```typescript
4573
+ * const pather = new Pather();
4574
+ *
4575
+ * pather.containsPath('src/utils/file.ts', 'utils') // true
4576
+ * pather.containsPath('src/utils/file.ts', 'src/utils') // true
4577
+ * pather.containsPath('src/utils/file.ts', 'til') // false
4578
+ * ```
4579
+ *
4580
+ * @example Segment boundaries
4581
+ * ```typescript
4582
+ * pather.containsPath('src/abc/file.ts', 'src/a') // false
4583
+ * pather.containsPath('src/abc/file.ts', 'src/abc') // true
4584
+ * ```
4585
+ *
4586
+ * @example Trailing separators
4587
+ * ```typescript
4588
+ * pather.containsPath('src/utils/', 'utils') // true
4589
+ * pather.containsPath('src/utils', 'utils/') // true
4590
+ * pather.containsPath('src/utils/', 'utils/') // true
4591
+ * ```
3264
4592
  */
3265
4593
  containsPath(sourcePath, targetPath) {
3266
4594
  let src = this.toLocalPath(sourcePath);
@@ -3286,18 +4614,49 @@ var Pather = class {
3286
4614
  // src/plugins/githubPR/GithubChangelog.ts
3287
4615
  var DOMAIN = "https://github.com";
3288
4616
  var GithubChangelog = class _GithubChangelog extends GitChangelog {
4617
+ /**
4618
+ * Creates a new GitHub changelog generator
4619
+ *
4620
+ * @param options - Changelog generation options
4621
+ * @param githubManager - GitHub API manager
4622
+ *
4623
+ * @example
4624
+ * ```typescript
4625
+ * const changelog = new GithubChangelog({
4626
+ * shell,
4627
+ * logger,
4628
+ * mergePRcommit: true,
4629
+ * githubRootPath: 'https://github.com/org/repo'
4630
+ * }, githubManager);
4631
+ * ```
4632
+ */
3289
4633
  constructor(options, githubManager) {
3290
4634
  super(options);
3291
4635
  this.options = options;
3292
4636
  this.githubManager = githubManager;
3293
4637
  }
4638
+ /** Path manipulation utility */
3294
4639
  pather = new Pather();
3295
4640
  /**
3296
- * Filter commits by directory
3297
- * @param commits - commits
3298
- * @param directory - directory
3299
- * @returns filtered commits
4641
+ * Filters commits by directory
4642
+ *
4643
+ * Filters commits based on whether they contain changes in
4644
+ * the specified directory. Uses GitHub API to get detailed
4645
+ * commit information.
4646
+ *
4647
+ * @param commits - Array of commits to filter
4648
+ * @param directory - Directory path to filter by
4649
+ * @returns Promise resolving to filtered commits
3300
4650
  * @since 2.4.0
4651
+ *
4652
+ * @example
4653
+ * ```typescript
4654
+ * const commits = await changelog.filterCommitsByDirectory(
4655
+ * allCommits,
4656
+ * 'packages/pkg-a'
4657
+ * );
4658
+ * // Only commits that modified files in packages/pkg-a
4659
+ * ```
3301
4660
  */
3302
4661
  async filterCommitsByDirectory(commits, directory) {
3303
4662
  const result = [];
@@ -3315,6 +4674,41 @@ var GithubChangelog = class _GithubChangelog extends GitChangelog {
3315
4674
  }
3316
4675
  return result;
3317
4676
  }
4677
+ /**
4678
+ * Gets complete commit information with PR details
4679
+ *
4680
+ * Retrieves commits and enhances them with pull request
4681
+ * information. For commits associated with PRs, includes
4682
+ * all PR commits and filters by directory.
4683
+ *
4684
+ * Process:
4685
+ * 1. Get base commits
4686
+ * 2. Extract PR numbers
4687
+ * 3. Fetch PR commits
4688
+ * 4. Filter by directory
4689
+ * 5. Flatten results
4690
+ *
4691
+ * @param options - Changelog options
4692
+ * @returns Promise resolving to enhanced commits
4693
+ *
4694
+ * @example Basic usage
4695
+ * ```typescript
4696
+ * const commits = await changelog.getFullCommit({
4697
+ * from: 'v1.0.0',
4698
+ * directory: 'packages/pkg-a'
4699
+ * });
4700
+ * // Returns commits with PR information
4701
+ * ```
4702
+ *
4703
+ * @example With PR merging
4704
+ * ```typescript
4705
+ * const commits = await changelog.getFullCommit({
4706
+ * mergePRcommit: true,
4707
+ * directory: 'packages/pkg-a'
4708
+ * });
4709
+ * // Includes all PR commits
4710
+ * ```
4711
+ */
3318
4712
  async getFullCommit(options) {
3319
4713
  const _options = { ...this.options, ...options };
3320
4714
  const allCommits = await this.getCommits(_options);
@@ -3342,6 +4736,41 @@ var GithubChangelog = class _GithubChangelog extends GitChangelog {
3342
4736
  );
3343
4737
  return newallCommits.flat();
3344
4738
  }
4739
+ /**
4740
+ * Transforms workspaces with GitHub changelogs
4741
+ *
4742
+ * Processes each workspace to add GitHub-specific changelog
4743
+ * information. Includes:
4744
+ * - GitHub repository URL
4745
+ * - PR-aware commit history
4746
+ * - Formatted changelog with links
4747
+ *
4748
+ * Process:
4749
+ * 1. Build GitHub root path
4750
+ * 2. Configure changelog options
4751
+ * 3. Get commits for each workspace
4752
+ * 4. Format changelog with links
4753
+ * 5. Update workspace objects
4754
+ *
4755
+ * @param workspaces - Array of workspaces to process
4756
+ * @param context - Release context
4757
+ * @returns Promise resolving to updated workspaces
4758
+ *
4759
+ * @example
4760
+ * ```typescript
4761
+ * const workspaces = await changelog.transformWorkspace(
4762
+ * [
4763
+ * {
4764
+ * name: 'pkg-a',
4765
+ * path: 'packages/a',
4766
+ * lastTag: 'v1.0.0'
4767
+ * }
4768
+ * ],
4769
+ * context
4770
+ * );
4771
+ * // Returns workspaces with GitHub-formatted changelogs
4772
+ * ```
4773
+ */
3345
4774
  async transformWorkspace(workspaces, context) {
3346
4775
  const githubRootPath = [
3347
4776
  DOMAIN,
@@ -3391,6 +4820,26 @@ var import_scripts_context6 = require("@qlover/scripts-context");
3391
4820
  var DEFAULT_RELEASE_NAME = "Release ${name} v${version}";
3392
4821
  var DEFAULT_COMMIT_MESSAGE = "chore(tag): ${name} v${version}";
3393
4822
  var GithubPR = class extends GitBase {
4823
+ /**
4824
+ * Creates a new GithubPR plugin instance
4825
+ *
4826
+ * Initializes the plugin with GitHub-specific configuration and
4827
+ * sets up release parameters and GitHub manager.
4828
+ *
4829
+ * @param context - Release context
4830
+ * @param props - Plugin configuration
4831
+ *
4832
+ * @example
4833
+ * ```typescript
4834
+ * const plugin = new GithubPR(context, {
4835
+ * releasePR: true,
4836
+ * releaseName: 'Release v${version}',
4837
+ * commitMessage: 'chore: release v${version}',
4838
+ * draft: false,
4839
+ * preRelease: false
4840
+ * });
4841
+ * ```
4842
+ */
3394
4843
  constructor(context, props) {
3395
4844
  super(context, "githubPR", {
3396
4845
  releaseName: DEFAULT_RELEASE_NAME,
@@ -3406,15 +4855,67 @@ var GithubPR = class extends GitBase {
3406
4855
  }
3407
4856
  releaseParams;
3408
4857
  githubManager;
4858
+ /**
4859
+ * Determines if the plugin should be enabled
4860
+ *
4861
+ * Plugin is enabled unless explicitly skipped via configuration.
4862
+ * This allows for conditional PR creation and release publishing.
4863
+ *
4864
+ * @param _name - Plugin name (unused)
4865
+ * @returns True if plugin should be enabled
4866
+ *
4867
+ * @example
4868
+ * ```typescript
4869
+ * const plugin = new GithubPR(context, { skip: true });
4870
+ * plugin.enabled(); // false
4871
+ *
4872
+ * const plugin2 = new GithubPR(context, {});
4873
+ * plugin2.enabled(); // true
4874
+ * ```
4875
+ */
3409
4876
  enabled(_name) {
3410
4877
  if (this.getConfig("skip")) {
3411
4878
  return false;
3412
4879
  }
3413
4880
  return true;
3414
4881
  }
4882
+ /**
4883
+ * Determines if the plugin is in publish mode
4884
+ *
4885
+ * In publish mode, the plugin publishes releases directly.
4886
+ * In non-publish mode (releasePR=true), it creates pull requests.
4887
+ *
4888
+ * @returns True if in publish mode
4889
+ *
4890
+ * @example
4891
+ * ```typescript
4892
+ * const plugin = new GithubPR(context, { releasePR: true });
4893
+ * plugin.isPublish; // false (PR mode)
4894
+ *
4895
+ * const plugin2 = new GithubPR(context, { releasePR: false });
4896
+ * plugin2.isPublish; // true (publish mode)
4897
+ * ```
4898
+ */
3415
4899
  get isPublish() {
3416
4900
  return !this.getConfig("releasePR");
3417
4901
  }
4902
+ /**
4903
+ * Checks if the current repository is a GitHub repository
4904
+ *
4905
+ * Verifies that the remote URL contains 'github.com' to ensure
4906
+ * GitHub-specific features can be used.
4907
+ *
4908
+ * @returns Promise resolving to true if GitHub repository
4909
+ *
4910
+ * @example
4911
+ * ```typescript
4912
+ * const isGithub = await plugin.isGithubRepository();
4913
+ * if (isGithub) {
4914
+ * // Use GitHub-specific features
4915
+ * }
4916
+ * ```
4917
+ * @private
4918
+ */
3418
4919
  async isGithubRepository() {
3419
4920
  try {
3420
4921
  const remoteUrl = await this.getRemoteUrl();
@@ -3423,6 +4924,24 @@ var GithubPR = class extends GitBase {
3423
4924
  return false;
3424
4925
  }
3425
4926
  }
4927
+ /**
4928
+ * Plugin initialization hook
4929
+ *
4930
+ * Performs pre-execution setup:
4931
+ * 1. Verifies repository is on GitHub
4932
+ * 2. Runs parent class initialization
4933
+ * 3. Sets up NPM token for publishing
4934
+ *
4935
+ * @throws Error if not a GitHub repository
4936
+ * @throws Error if NPM_TOKEN missing in publish mode
4937
+ *
4938
+ * @example
4939
+ * ```typescript
4940
+ * const plugin = new GithubPR(context, {});
4941
+ * await plugin.onBefore();
4942
+ * // Throws if not GitHub repo or missing NPM token
4943
+ * ```
4944
+ */
3426
4945
  async onBefore() {
3427
4946
  this.logger.debug("GithubPR onBefore");
3428
4947
  const isGithub = await this.isGithubRepository();
@@ -3442,6 +4961,24 @@ var GithubPR = class extends GitBase {
3442
4961
  );
3443
4962
  }
3444
4963
  }
4964
+ /**
4965
+ * Main plugin execution hook
4966
+ *
4967
+ * Processes changelogs for all workspaces using GitHub-specific
4968
+ * formatting and updates the context with the results.
4969
+ *
4970
+ * Process:
4971
+ * 1. Initialize GitHub changelog processor
4972
+ * 2. Transform workspace changelogs
4973
+ * 3. Update context with new workspace info
4974
+ *
4975
+ * @example
4976
+ * ```typescript
4977
+ * const plugin = new GithubPR(context, {});
4978
+ * await plugin.onExec();
4979
+ * // Transforms changelogs with GitHub links
4980
+ * ```
4981
+ */
3445
4982
  async onExec() {
3446
4983
  const workspaces = this.context.workspaces;
3447
4984
  const githubChangelog = new GithubChangelog(
@@ -3454,6 +4991,27 @@ var GithubPR = class extends GitBase {
3454
4991
  });
3455
4992
  this.context.setWorkspaces(newWorkspaces);
3456
4993
  }
4994
+ /**
4995
+ * Success hook after plugin execution
4996
+ *
4997
+ * Handles either PR creation or release publishing based on
4998
+ * configuration. In publish mode, publishes to NPM and creates
4999
+ * GitHub releases. In PR mode, creates release pull requests.
5000
+ *
5001
+ * @example PR mode
5002
+ * ```typescript
5003
+ * const plugin = new GithubPR(context, { releasePR: true });
5004
+ * await plugin.onSuccess();
5005
+ * // Creates release PR
5006
+ * ```
5007
+ *
5008
+ * @example Publish mode
5009
+ * ```typescript
5010
+ * const plugin = new GithubPR(context, { releasePR: false });
5011
+ * await plugin.onSuccess();
5012
+ * // Publishes to NPM and creates GitHub release
5013
+ * ```
5014
+ */
3457
5015
  async onSuccess() {
3458
5016
  if (this.isPublish) {
3459
5017
  await this.publishPR(this.context.workspaces);
@@ -3461,6 +5019,28 @@ var GithubPR = class extends GitBase {
3461
5019
  }
3462
5020
  await this.releasePR(this.context.workspaces);
3463
5021
  }
5022
+ /**
5023
+ * Creates a release pull request
5024
+ *
5025
+ * Handles the complete process of creating a release PR:
5026
+ * 1. Creates release commit
5027
+ * 2. Creates release branch
5028
+ * 3. Creates and configures pull request
5029
+ *
5030
+ * @param workspaces - Array of workspace configurations
5031
+ *
5032
+ * @example
5033
+ * ```typescript
5034
+ * const workspaces = [{
5035
+ * name: 'pkg-a',
5036
+ * version: '1.0.0',
5037
+ * changelog: '...'
5038
+ * }];
5039
+ *
5040
+ * await plugin.releasePR(workspaces);
5041
+ * // Creates PR with release changes
5042
+ * ```
5043
+ */
3464
5044
  async releasePR(workspaces) {
3465
5045
  await this.step({
3466
5046
  label: "Release Commit",
@@ -3472,6 +5052,28 @@ var GithubPR = class extends GitBase {
3472
5052
  });
3473
5053
  await this.releasePullRequest(workspaces, releaseBranchParams);
3474
5054
  }
5055
+ /**
5056
+ * Publishes releases to NPM and GitHub
5057
+ *
5058
+ * In non-dry-run mode:
5059
+ * 1. Publishes packages to NPM
5060
+ * 2. Pushes tags to GitHub
5061
+ * 3. Creates GitHub releases
5062
+ *
5063
+ * @param workspaces - Array of workspace configurations
5064
+ *
5065
+ * @example
5066
+ * ```typescript
5067
+ * const workspaces = [{
5068
+ * name: 'pkg-a',
5069
+ * version: '1.0.0',
5070
+ * changelog: '...'
5071
+ * }];
5072
+ *
5073
+ * await plugin.publishPR(workspaces);
5074
+ * // Publishes to NPM and creates GitHub releases
5075
+ * ```
5076
+ */
3475
5077
  async publishPR(workspaces) {
3476
5078
  if (!this.getConfig("dryRunCreatePR")) {
3477
5079
  await this.context.runChangesetsCli("publish");
@@ -3487,6 +5089,34 @@ var GithubPR = class extends GitBase {
3487
5089
  )
3488
5090
  });
3489
5091
  }
5092
+ /**
5093
+ * Creates release commit(s)
5094
+ *
5095
+ * Creates either a single commit for all workspaces or
5096
+ * individual commits per workspace. Uses configured commit
5097
+ * message template.
5098
+ *
5099
+ * @param workspaces - Array of workspace configurations
5100
+ *
5101
+ * @example Single workspace
5102
+ * ```typescript
5103
+ * await plugin.relesaeCommit([{
5104
+ * name: 'pkg-a',
5105
+ * version: '1.0.0'
5106
+ * }]);
5107
+ * // Creates: "chore(tag): pkg-a v1.0.0"
5108
+ * ```
5109
+ *
5110
+ * @example Multiple workspaces
5111
+ * ```typescript
5112
+ * await plugin.relesaeCommit([
5113
+ * { name: 'pkg-a', version: '1.0.0' },
5114
+ * { name: 'pkg-b', version: '2.0.0' }
5115
+ * ]);
5116
+ * // Creates: "chore(tag): pkg-a v1.0.0,pkg-b v2.0.0"
5117
+ * ```
5118
+ * @private
5119
+ */
3490
5120
  async relesaeCommit(workspaces) {
3491
5121
  const commitArgs = this.getConfig("commitArgs", []);
3492
5122
  if (workspaces.length === 1) {
@@ -3498,6 +5128,34 @@ var GithubPR = class extends GitBase {
3498
5128
  const commitMessage = `chore(tag): ${workspaces.map((w) => `${w.name} v${w.version}`).join(",")}`;
3499
5129
  await this.commit(commitMessage, commitArgs);
3500
5130
  }
5131
+ /**
5132
+ * Creates and optionally merges a release pull request
5133
+ *
5134
+ * Creates a PR with release changes and handles auto-merge
5135
+ * if configured. Adds release and change labels to the PR.
5136
+ *
5137
+ * @param workspaces - Array of workspace configurations
5138
+ * @param releaseBranchParams - Branch and tag information
5139
+ *
5140
+ * @example Manual merge
5141
+ * ```typescript
5142
+ * await plugin.releasePullRequest(
5143
+ * workspaces,
5144
+ * { releaseBranch: 'release-v1.0.0', tagName: 'v1.0.0' }
5145
+ * );
5146
+ * // Creates PR for manual merge
5147
+ * ```
5148
+ *
5149
+ * @example Auto-merge
5150
+ * ```typescript
5151
+ * const plugin = new GithubPR(context, {
5152
+ * autoMergeReleasePR: true
5153
+ * });
5154
+ *
5155
+ * await plugin.releasePullRequest(workspaces, params);
5156
+ * // Creates and auto-merges PR
5157
+ * ```
5158
+ */
3501
5159
  async releasePullRequest(workspaces, releaseBranchParams) {
3502
5160
  const prNumber = await this.step({
3503
5161
  label: "Create Release PR",
@@ -3519,6 +5177,34 @@ var GithubPR = class extends GitBase {
3519
5177
  `Please manually merge PR(#${prNumber}) and complete the publishing process afterwards`
3520
5178
  );
3521
5179
  }
5180
+ /**
5181
+ * Creates a commit for a single workspace
5182
+ *
5183
+ * Uses the configured commit message template to create a
5184
+ * commit for the workspace's changes.
5185
+ *
5186
+ * @param workspace - Workspace configuration
5187
+ * @param commitArgs - Additional Git commit arguments
5188
+ * @returns Promise resolving to commit output
5189
+ *
5190
+ * @example Basic commit
5191
+ * ```typescript
5192
+ * await plugin.commitWorkspace({
5193
+ * name: 'pkg-a',
5194
+ * version: '1.0.0'
5195
+ * });
5196
+ * // Creates: "chore(tag): pkg-a v1.0.0"
5197
+ * ```
5198
+ *
5199
+ * @example With arguments
5200
+ * ```typescript
5201
+ * await plugin.commitWorkspace(
5202
+ * { name: 'pkg-a', version: '1.0.0' },
5203
+ * ['--no-verify']
5204
+ * );
5205
+ * ```
5206
+ * @private
5207
+ */
3522
5208
  async commitWorkspace(workspace, commitArgs = []) {
3523
5209
  const commitMessage = import_scripts_context6.Shell.format(
3524
5210
  this.getConfig("commitMessage", DEFAULT_COMMIT_MESSAGE),
@@ -3536,6 +5222,37 @@ var GithubPR = class extends GitBase {
3536
5222
  *
3537
5223
  * @returns The release branch.
3538
5224
  */
5225
+ /**
5226
+ * Creates a release branch for changes
5227
+ *
5228
+ * Creates a new branch from the current branch for release
5229
+ * changes. The branch name is generated from the configured
5230
+ * template and workspace information.
5231
+ *
5232
+ * Process:
5233
+ * 1. Generate branch parameters
5234
+ * 2. Fetch required branches
5235
+ * 3. Create and push release branch
5236
+ *
5237
+ * @param workspaces - Array of workspace configurations
5238
+ * @returns Promise resolving to branch parameters
5239
+ *
5240
+ * @example
5241
+ * ```typescript
5242
+ * const params = await plugin.createReleaseBranch([{
5243
+ * name: 'pkg-a',
5244
+ * version: '1.0.0'
5245
+ * }]);
5246
+ * // {
5247
+ * // tagName: 'pkg-a@1.0.0',
5248
+ * // releaseBranch: 'release-pkg-a-1.0.0'
5249
+ * // }
5250
+ * ```
5251
+ *
5252
+ * @throws Error if tag name is invalid
5253
+ * @throws Error if branch creation fails
5254
+ * @private
5255
+ */
3539
5256
  async createReleaseBranch(workspaces) {
3540
5257
  const params = this.releaseParams.getReleaseBranchParams(
3541
5258
  workspaces,
@@ -3578,6 +5295,42 @@ var GithubPR = class extends GitBase {
3578
5295
  * @param releaseBranchParams - The release branch params.
3579
5296
  * @returns The created pull request number.
3580
5297
  */
5298
+ /**
5299
+ * Creates a release pull request
5300
+ *
5301
+ * Creates a pull request with:
5302
+ * 1. Release label
5303
+ * 2. Change labels (if configured)
5304
+ * 3. Generated title and body
5305
+ * 4. Proper branch configuration
5306
+ *
5307
+ * @param workspaces - Array of workspace configurations
5308
+ * @param releaseBranchParams - Branch and tag information
5309
+ * @returns Promise resolving to PR number
5310
+ *
5311
+ * @example Basic PR
5312
+ * ```typescript
5313
+ * const prNumber = await plugin.createReleasePR(
5314
+ * workspaces,
5315
+ * { releaseBranch: 'release-v1.0.0', tagName: 'v1.0.0' }
5316
+ * );
5317
+ * // Creates PR with default labels
5318
+ * ```
5319
+ *
5320
+ * @example With change labels
5321
+ * ```typescript
5322
+ * const plugin = new GithubPR(context, {
5323
+ * pushChangeLabels: true
5324
+ * });
5325
+ *
5326
+ * const prNumber = await plugin.createReleasePR(
5327
+ * workspaces,
5328
+ * params
5329
+ * );
5330
+ * // Creates PR with release and change labels
5331
+ * ```
5332
+ * @private
5333
+ */
3581
5334
  async createReleasePR(workspaces, releaseBranchParams) {
3582
5335
  const label = await this.githubManager.createReleasePRLabel();
3583
5336
  let labels = [label.name];
@@ -3611,21 +5364,137 @@ var import_node_path2 = require("path");
3611
5364
 
3612
5365
  // src/implments/ReleaseLabel.ts
3613
5366
  var ReleaseLabel = class {
5367
+ /**
5368
+ * Creates a new ReleaseLabel instance
5369
+ *
5370
+ * @param options - Configuration options for label management
5371
+ *
5372
+ * @example
5373
+ * ```typescript
5374
+ * const label = new ReleaseLabel({
5375
+ * // Label template with ${name} placeholder
5376
+ * changePackagesLabel: 'changed:${name}',
5377
+ *
5378
+ * // Package directories to monitor
5379
+ * packagesDirectories: ['packages/a', 'packages/b'],
5380
+ *
5381
+ * // Optional custom comparison logic
5382
+ * compare: (file, pkg) => file.includes(pkg)
5383
+ * });
5384
+ * ```
5385
+ */
3614
5386
  constructor(options) {
3615
5387
  this.options = options;
3616
5388
  }
5389
+ /**
5390
+ * Compares a changed file path against a package path
5391
+ *
5392
+ * Uses custom comparison function if provided, otherwise
5393
+ * checks if the file path starts with the package path.
5394
+ *
5395
+ * @param changedFilePath - Path of the changed file
5396
+ * @param packagePath - Path of the package to check against
5397
+ * @returns True if the file belongs to the package
5398
+ *
5399
+ * @example
5400
+ * ```typescript
5401
+ * // Default comparison
5402
+ * label.compare('packages/a/src/index.ts', 'packages/a');
5403
+ * // true
5404
+ *
5405
+ * // Custom comparison
5406
+ * const label = new ReleaseLabel({
5407
+ * ...options,
5408
+ * compare: (file, pkg) => file.includes(pkg)
5409
+ * });
5410
+ * label.compare('src/packages/a/index.ts', 'packages/a');
5411
+ * // true
5412
+ * ```
5413
+ */
3617
5414
  compare(changedFilePath, packagePath) {
3618
5415
  if (typeof this.options.compare === "function") {
3619
5416
  return this.options.compare(changedFilePath, packagePath);
3620
5417
  }
3621
5418
  return changedFilePath.startsWith(packagePath);
3622
5419
  }
5420
+ /**
5421
+ * Generates a change label for a single package
5422
+ *
5423
+ * Replaces ${name} placeholder in the label template with
5424
+ * the package path.
5425
+ *
5426
+ * @param packagePath - Path of the package
5427
+ * @param label - Optional custom label template
5428
+ * @returns Formatted change label
5429
+ *
5430
+ * @example
5431
+ * ```typescript
5432
+ * // Default label template
5433
+ * label.toChangeLabel('packages/a');
5434
+ * // 'changed:packages/a'
5435
+ *
5436
+ * // Custom label template
5437
+ * label.toChangeLabel('packages/a', 'modified:${name}');
5438
+ * // 'modified:packages/a'
5439
+ * ```
5440
+ */
3623
5441
  toChangeLabel(packagePath, label = this.options.changePackagesLabel) {
3624
5442
  return label.replace("${name}", packagePath);
3625
5443
  }
5444
+ /**
5445
+ * Generates change labels for multiple packages
5446
+ *
5447
+ * Maps each package path to a formatted change label.
5448
+ *
5449
+ * @param packages - Array of package paths
5450
+ * @param label - Optional custom label template
5451
+ * @returns Array of formatted change labels
5452
+ *
5453
+ * @example
5454
+ * ```typescript
5455
+ * // Default label template
5456
+ * label.toChangeLabels(['packages/a', 'packages/b']);
5457
+ * // ['changed:packages/a', 'changed:packages/b']
5458
+ *
5459
+ * // Custom label template
5460
+ * label.toChangeLabels(
5461
+ * ['packages/a', 'packages/b'],
5462
+ * 'modified:${name}'
5463
+ * );
5464
+ * // ['modified:packages/a', 'modified:packages/b']
5465
+ * ```
5466
+ */
3626
5467
  toChangeLabels(packages, label = this.options.changePackagesLabel) {
3627
5468
  return packages.map((pkg) => this.toChangeLabel(pkg, label));
3628
5469
  }
5470
+ /**
5471
+ * Identifies packages affected by changed files
5472
+ *
5473
+ * Checks each changed file against package paths to determine
5474
+ * which packages have been modified.
5475
+ *
5476
+ * @param changedFiles - Array or Set of changed file paths
5477
+ * @param packages - Optional array of package paths to check
5478
+ * @returns Array of affected package paths
5479
+ *
5480
+ * @example
5481
+ * ```typescript
5482
+ * // Check against default packages
5483
+ * label.pick(['packages/a/src/index.ts']);
5484
+ * // ['packages/a']
5485
+ *
5486
+ * // Check specific packages
5487
+ * label.pick(
5488
+ * ['packages/a/index.ts', 'packages/b/test.ts'],
5489
+ * ['packages/a', 'packages/c']
5490
+ * );
5491
+ * // ['packages/a']
5492
+ *
5493
+ * // Using Set of files
5494
+ * label.pick(new Set(['packages/a/index.ts']));
5495
+ * // ['packages/a']
5496
+ * ```
5497
+ */
3629
5498
  pick(changedFiles, packages = this.options.packagesDirectories) {
3630
5499
  const result = [];
3631
5500
  for (const pkgPath of packages) {
@@ -3991,6 +5860,31 @@ var import_fs2 = require("fs");
3991
5860
  var import_scripts_context8 = require("@qlover/scripts-context");
3992
5861
  var contentTmplate = "---\n'${name}': '${increment}'\n---\n\n${changelog}";
3993
5862
  var Changelog = class extends import_scripts_context8.ScriptPlugin {
5863
+ /**
5864
+ * Creates a new Changelog plugin instance
5865
+ *
5866
+ * Initializes the plugin with default configuration values and
5867
+ * merges them with provided options.
5868
+ *
5869
+ * Default values:
5870
+ * - increment: 'patch'
5871
+ * - changesetRoot: '.changeset'
5872
+ * - tagTemplate: '${name}@${version}'
5873
+ * - tagPrefix: '${name}'
5874
+ * - tagMatch: '${name}@*'
5875
+ *
5876
+ * @param context - Release context
5877
+ * @param props - Plugin configuration
5878
+ *
5879
+ * @example
5880
+ * ```typescript
5881
+ * const plugin = new Changelog(context, {
5882
+ * increment: 'minor',
5883
+ * changesetRoot: 'custom/changeset',
5884
+ * tagTemplate: 'v${version}'
5885
+ * });
5886
+ * ```
5887
+ */
3994
5888
  constructor(context, props) {
3995
5889
  super(context, "changelog", {
3996
5890
  increment: "patch",
@@ -4001,15 +5895,78 @@ var Changelog = class extends import_scripts_context8.ScriptPlugin {
4001
5895
  ...props
4002
5896
  });
4003
5897
  }
5898
+ /**
5899
+ * Gets the absolute path to the changeset root directory
5900
+ *
5901
+ * Combines the project root path with the configured changeset
5902
+ * directory path.
5903
+ *
5904
+ * @returns Absolute path to changeset directory
5905
+ *
5906
+ * @example
5907
+ * ```typescript
5908
+ * const root = plugin.changesetRoot;
5909
+ * // '/path/to/project/.changeset'
5910
+ * ```
5911
+ */
4004
5912
  get changesetRoot() {
4005
5913
  return (0, import_path2.join)(this.context.rootPath, this.getConfig("changesetRoot"));
4006
5914
  }
5915
+ /**
5916
+ * Gets the path to the changeset configuration file
5917
+ *
5918
+ * Returns the absolute path to the config.json file in the
5919
+ * changeset directory.
5920
+ *
5921
+ * @returns Path to changeset config file
5922
+ *
5923
+ * @example
5924
+ * ```typescript
5925
+ * const configPath = plugin.changesetConfigPath;
5926
+ * // '/path/to/project/.changeset/config.json'
5927
+ * ```
5928
+ */
4007
5929
  get changesetConfigPath() {
4008
5930
  return (0, import_path2.join)(this.changesetRoot, "config.json");
4009
5931
  }
5932
+ /**
5933
+ * Determines if the plugin should be enabled
5934
+ *
5935
+ * Plugin is enabled unless explicitly skipped via configuration.
5936
+ * This allows for conditional changelog generation.
5937
+ *
5938
+ * @returns True if plugin should be enabled
5939
+ *
5940
+ * @example
5941
+ * ```typescript
5942
+ * const plugin = new Changelog(context, { skip: true });
5943
+ * plugin.enabled(); // false
5944
+ *
5945
+ * const plugin2 = new Changelog(context, {});
5946
+ * plugin2.enabled(); // true
5947
+ * ```
5948
+ */
4010
5949
  enabled() {
4011
5950
  return !this.getConfig("skip");
4012
5951
  }
5952
+ /**
5953
+ * Plugin initialization hook
5954
+ *
5955
+ * Verifies that the changeset directory exists before proceeding
5956
+ * with changelog generation.
5957
+ *
5958
+ * @throws Error if changeset directory does not exist
5959
+ *
5960
+ * @example
5961
+ * ```typescript
5962
+ * const plugin = new Changelog(context, {
5963
+ * changesetRoot: '.changeset'
5964
+ * });
5965
+ *
5966
+ * await plugin.onBefore();
5967
+ * // Throws if .changeset directory doesn't exist
5968
+ * ```
5969
+ */
4013
5970
  async onBefore() {
4014
5971
  if (!(0, import_fs2.existsSync)(this.changesetRoot)) {
4015
5972
  throw new Error(
@@ -4018,6 +5975,33 @@ var Changelog = class extends import_scripts_context8.ScriptPlugin {
4018
5975
  }
4019
5976
  this.logger.debug(`${this.changesetRoot} exists`);
4020
5977
  }
5978
+ /**
5979
+ * Updates workspace information with latest versions
5980
+ *
5981
+ * Reads the latest version information from each workspace's
5982
+ * package.json and updates the workspace objects with new
5983
+ * versions and tag names.
5984
+ *
5985
+ * @param workspaces - Array of workspace configurations
5986
+ * @returns Updated workspace configurations
5987
+ *
5988
+ * @example
5989
+ * ```typescript
5990
+ * const workspaces = [
5991
+ * { name: 'pkg-a', path: 'packages/a', version: '1.0.0' }
5992
+ * ];
5993
+ *
5994
+ * const updated = plugin.mergeWorkspaces(workspaces);
5995
+ * // [
5996
+ * // {
5997
+ * // name: 'pkg-a',
5998
+ * // path: 'packages/a',
5999
+ * // version: '1.1.0', // Updated version
6000
+ * // tagName: 'pkg-a@1.1.0'
6001
+ * // }
6002
+ * // ]
6003
+ * ```
6004
+ */
4021
6005
  mergeWorkspaces(workspaces) {
4022
6006
  return workspaces.map((workspace) => {
4023
6007
  const newPackgeJson = WorkspaceCreator.toWorkspace(
@@ -4034,6 +6018,25 @@ var Changelog = class extends import_scripts_context8.ScriptPlugin {
4034
6018
  return newWorkspace;
4035
6019
  });
4036
6020
  }
6021
+ /**
6022
+ * Main plugin execution hook
6023
+ *
6024
+ * Generates changelogs for all workspaces in parallel and updates
6025
+ * the context with the results.
6026
+ *
6027
+ * Process:
6028
+ * 1. Generate changelogs for each workspace
6029
+ * 2. Update context with new workspace information
6030
+ *
6031
+ * @param _context - Execution context
6032
+ *
6033
+ * @example
6034
+ * ```typescript
6035
+ * const plugin = new Changelog(context, {});
6036
+ * await plugin.onExec(execContext);
6037
+ * // Generates changelogs for all workspaces
6038
+ * ```
6039
+ */
4037
6040
  async onExec(_context) {
4038
6041
  const workspaces = await this.step({
4039
6042
  label: "Generate Changelogs",
@@ -4045,6 +6048,28 @@ var Changelog = class extends import_scripts_context8.ScriptPlugin {
4045
6048
  });
4046
6049
  this.context.setWorkspaces(workspaces);
4047
6050
  }
6051
+ /**
6052
+ * Success hook after plugin execution
6053
+ *
6054
+ * Handles post-changelog generation tasks:
6055
+ * 1. Creates changeset files (if not skipped)
6056
+ * 2. Updates package versions
6057
+ * 3. Restores unchanged packages (if configured)
6058
+ * 4. Updates workspace information
6059
+ *
6060
+ * @example
6061
+ * ```typescript
6062
+ * const plugin = new Changelog(context, {
6063
+ * skipChangeset: false,
6064
+ * ignoreNonUpdatedPackages: true
6065
+ * });
6066
+ *
6067
+ * await plugin.onSuccess();
6068
+ * // - Creates changeset files
6069
+ * // - Updates versions
6070
+ * // - Restores unchanged packages
6071
+ * ```
6072
+ */
4048
6073
  async onSuccess() {
4049
6074
  const workspaces = this.context.workspaces;
4050
6075
  if (!this.getConfig("skipChangeset")) {
@@ -4068,6 +6093,25 @@ var Changelog = class extends import_scripts_context8.ScriptPlugin {
4068
6093
  this.logger.debug("new workspaces", newWorkspaces);
4069
6094
  this.context.setWorkspaces(newWorkspaces);
4070
6095
  }
6096
+ /**
6097
+ * Restores unchanged packages to their original state
6098
+ *
6099
+ * When ignoreNonUpdatedPackages is enabled, this method:
6100
+ * 1. Identifies packages without changes
6101
+ * 2. Uses git restore to revert them to original state
6102
+ *
6103
+ * @example
6104
+ * ```typescript
6105
+ * // With changed and unchanged packages
6106
+ * context.options.workspaces = {
6107
+ * packages: ['pkg-a', 'pkg-b', 'pkg-c'],
6108
+ * changedPaths: ['pkg-a', 'pkg-b']
6109
+ * };
6110
+ *
6111
+ * await plugin.restoreIgnorePackages();
6112
+ * // Restores 'pkg-c' to original state
6113
+ * ```
6114
+ */
4071
6115
  async restoreIgnorePackages() {
4072
6116
  const { changedPaths = [], packages = [] } = this.context.getOptions(
4073
6117
  "workspaces"
@@ -4080,12 +6124,60 @@ var Changelog = class extends import_scripts_context8.ScriptPlugin {
4080
6124
  await this.shell.exec(["git", "restore", ...noChangedPackages]);
4081
6125
  }
4082
6126
  }
6127
+ /**
6128
+ * Gets the tag prefix for a workspace
6129
+ *
6130
+ * Formats the configured tag prefix template with workspace
6131
+ * information. Used for generating Git tag names.
6132
+ *
6133
+ * @param workspace - Workspace configuration
6134
+ * @returns Formatted tag prefix
6135
+ *
6136
+ * @example
6137
+ * ```typescript
6138
+ * const workspace = {
6139
+ * name: 'pkg-a',
6140
+ * version: '1.0.0'
6141
+ * };
6142
+ *
6143
+ * const prefix = plugin.getTagPrefix(workspace);
6144
+ * // With default template: 'pkg-a'
6145
+ * // With custom template: 'v1.0.0'
6146
+ * ```
6147
+ */
4083
6148
  getTagPrefix(workspace) {
4084
6149
  return import_scripts_context8.Shell.format(
4085
6150
  this.getConfig("tagPrefix"),
4086
6151
  workspace
4087
6152
  );
4088
6153
  }
6154
+ /**
6155
+ * Generates a changelog for a workspace
6156
+ *
6157
+ * Creates a changelog by:
6158
+ * 1. Getting the appropriate tag name
6159
+ * 2. Retrieving commits since last tag
6160
+ * 3. Formatting commits into changelog entries
6161
+ *
6162
+ * @param workspace - Workspace configuration
6163
+ * @returns Updated workspace with changelog
6164
+ *
6165
+ * @example
6166
+ * ```typescript
6167
+ * const workspace = {
6168
+ * name: 'pkg-a',
6169
+ * path: 'packages/a',
6170
+ * version: '1.0.0'
6171
+ * };
6172
+ *
6173
+ * const updated = await plugin.generateChangelog(workspace);
6174
+ * // {
6175
+ * // ...workspace,
6176
+ * // lastTag: 'pkg-a@1.0.0',
6177
+ * // changelog: '- feat: new feature\n- fix: bug fix'
6178
+ * // }
6179
+ * ```
6180
+ */
4089
6181
  async generateChangelog(workspace) {
4090
6182
  let tagName = await this.getTagName(workspace);
4091
6183
  if (workspace.lastTag) {
@@ -4111,6 +6203,32 @@ var Changelog = class extends import_scripts_context8.ScriptPlugin {
4111
6203
  changelog: changelog.join("\n")
4112
6204
  };
4113
6205
  }
6206
+ /**
6207
+ * Generates a tag name for a workspace
6208
+ *
6209
+ * Uses the configured tag template to generate a tag name
6210
+ * for the workspace. Handles errors by providing a fallback.
6211
+ *
6212
+ * @param workspace - Workspace configuration
6213
+ * @returns Generated tag name
6214
+ *
6215
+ * @example
6216
+ * ```typescript
6217
+ * // With default template
6218
+ * const tag = plugin.generateTagName({
6219
+ * name: 'pkg-a',
6220
+ * version: '1.0.0'
6221
+ * });
6222
+ * // 'pkg-a@1.0.0'
6223
+ *
6224
+ * // With error (fallback)
6225
+ * const tag = plugin.generateTagName({
6226
+ * name: 'pkg-a'
6227
+ * });
6228
+ * // 'pkg-a-v0.0.0'
6229
+ * ```
6230
+ * @private
6231
+ */
4114
6232
  generateTagName(workspace) {
4115
6233
  try {
4116
6234
  const tagTemplate = this.getConfig("tagTemplate");
@@ -4123,6 +6241,38 @@ var Changelog = class extends import_scripts_context8.ScriptPlugin {
4123
6241
  return `${workspace.name}-v0.0.0`;
4124
6242
  }
4125
6243
  }
6244
+ /**
6245
+ * Gets the appropriate tag name for a workspace
6246
+ *
6247
+ * Attempts to find the latest tag for the workspace, falling back
6248
+ * to generating a new tag if none exists. Uses git commands to
6249
+ * find and sort tags by creation date.
6250
+ *
6251
+ * Process:
6252
+ * 1. Generate current tag pattern
6253
+ * 2. Search for existing tags matching pattern
6254
+ * 3. Return latest tag or generate new one
6255
+ *
6256
+ * @param workspace - Workspace configuration
6257
+ * @returns Promise resolving to tag name
6258
+ *
6259
+ * @example
6260
+ * ```typescript
6261
+ * // With existing tags
6262
+ * const tag = await plugin.getTagName({
6263
+ * name: 'pkg-a',
6264
+ * version: '1.0.0'
6265
+ * });
6266
+ * // Returns latest matching tag: 'pkg-a@0.9.0'
6267
+ *
6268
+ * // Without existing tags
6269
+ * const tag = await plugin.getTagName({
6270
+ * name: 'pkg-b',
6271
+ * version: '1.0.0'
6272
+ * });
6273
+ * // Returns new tag: 'pkg-b@1.0.0'
6274
+ * ```
6275
+ */
4126
6276
  async getTagName(workspace) {
4127
6277
  try {
4128
6278
  const currentTagPattern = this.generateTagName(workspace);
@@ -4149,6 +6299,31 @@ var Changelog = class extends import_scripts_context8.ScriptPlugin {
4149
6299
  return fallbackTag;
4150
6300
  }
4151
6301
  }
6302
+ /**
6303
+ * Determines the version increment type
6304
+ *
6305
+ * Checks for increment labels in the following order:
6306
+ * 1. 'increment:major' label
6307
+ * 2. 'increment:minor' label
6308
+ * 3. Configured increment value
6309
+ * 4. Default to 'patch'
6310
+ *
6311
+ * @returns Version increment type
6312
+ *
6313
+ * @example
6314
+ * ```typescript
6315
+ * // With labels
6316
+ * context.options.workspaces.changeLabels = ['increment:major'];
6317
+ * plugin.getIncrement(); // 'major'
6318
+ *
6319
+ * // With configuration
6320
+ * const plugin = new Changelog(context, { increment: 'minor' });
6321
+ * plugin.getIncrement(); // 'minor'
6322
+ *
6323
+ * // Default
6324
+ * plugin.getIncrement(); // 'patch'
6325
+ * ```
6326
+ */
4152
6327
  getIncrement() {
4153
6328
  const lables = this.context.getOptions("workspaces.changeLabels");
4154
6329
  if (Array.isArray(lables) && lables.length > 0) {
@@ -4162,6 +6337,43 @@ var Changelog = class extends import_scripts_context8.ScriptPlugin {
4162
6337
  const increment = this.getConfig("increment", "patch");
4163
6338
  return increment;
4164
6339
  }
6340
+ /**
6341
+ * Generates a changeset file for a workspace
6342
+ *
6343
+ * Creates a changeset file containing version increment
6344
+ * information and changelog content. Handles dry run mode
6345
+ * and existing files.
6346
+ *
6347
+ * File format:
6348
+ * ```yaml
6349
+ * ---
6350
+ * 'package-name': 'increment-type'
6351
+ * ---
6352
+ *
6353
+ * changelog content
6354
+ * ```
6355
+ *
6356
+ * @param workspace - Workspace configuration
6357
+ *
6358
+ * @example
6359
+ * ```typescript
6360
+ * const workspace = {
6361
+ * name: 'pkg-a',
6362
+ * version: '1.0.0',
6363
+ * changelog: '- feat: new feature'
6364
+ * };
6365
+ *
6366
+ * await plugin.generateChangesetFile(workspace);
6367
+ * // Creates .changeset/pkg-a-1.0.0.md
6368
+ * ```
6369
+ *
6370
+ * @example Dry run
6371
+ * ```typescript
6372
+ * context.dryRun = true;
6373
+ * await plugin.generateChangesetFile(workspace);
6374
+ * // Logs file content without creating file
6375
+ * ```
6376
+ */
4165
6377
  async generateChangesetFile(workspace) {
4166
6378
  const { name, version } = workspace;
4167
6379
  const changesetName = `${name}-${version}`.replace(/[\/\\]/g, "_");
@@ -4195,15 +6407,89 @@ var innerTuples = [
4195
6407
  ];
4196
6408
  var defaultName = "release";
4197
6409
  var ReleaseTask = class {
6410
+ /**
6411
+ * Creates a new ReleaseTask instance
6412
+ *
6413
+ * Initializes the release context and sets up plugin configuration.
6414
+ * Supports custom executors and plugin configurations.
6415
+ *
6416
+ * @param options - Release context configuration
6417
+ * @param executor - Custom async executor (optional)
6418
+ * @param defaultTuples - Plugin configuration tuples (optional)
6419
+ *
6420
+ * @example
6421
+ * ```typescript
6422
+ * // Basic initialization
6423
+ * const task = new ReleaseTask({
6424
+ * rootPath: '/path/to/project',
6425
+ * sourceBranch: 'main'
6426
+ * });
6427
+ *
6428
+ * // With custom executor and plugins
6429
+ * const task = new ReleaseTask(
6430
+ * { rootPath: '/path/to/project' },
6431
+ * new AsyncExecutor(),
6432
+ * [tuple(CustomPlugin, { option: 'value' })]
6433
+ * );
6434
+ * ```
6435
+ */
4198
6436
  constructor(options = {}, executor = new import_fe_corekit.AsyncExecutor(), defaultTuples = innerTuples) {
4199
6437
  this.executor = executor;
4200
6438
  this.defaultTuples = defaultTuples;
4201
6439
  this.context = new ReleaseContext(defaultName, options);
4202
6440
  }
6441
+ /**
6442
+ * Release context instance
6443
+ * @protected
6444
+ */
4203
6445
  context;
6446
+ /**
6447
+ * Gets the current release context
6448
+ *
6449
+ * @returns Release context instance
6450
+ *
6451
+ * @example
6452
+ * ```typescript
6453
+ * const task = new ReleaseTask();
6454
+ * const context = task.getContext();
6455
+ *
6456
+ * console.log(context.releaseEnv);
6457
+ * console.log(context.sourceBranch);
6458
+ * ```
6459
+ */
4204
6460
  getContext() {
4205
6461
  return this.context;
4206
6462
  }
6463
+ /**
6464
+ * Loads and configures plugins for the release task
6465
+ *
6466
+ * Combines default and external plugins, initializes them with
6467
+ * the current context, and configures special cases like the
6468
+ * Workspaces plugin.
6469
+ *
6470
+ * Plugin Loading Process:
6471
+ * 1. Merge default and external plugins
6472
+ * 2. Initialize plugins with context
6473
+ * 3. Configure special plugins
6474
+ * 4. Add plugins to executor
6475
+ *
6476
+ * @param externalTuples - Additional plugin configurations
6477
+ * @returns Array of initialized plugins
6478
+ *
6479
+ * @example Basic usage
6480
+ * ```typescript
6481
+ * const task = new ReleaseTask();
6482
+ * const plugins = await task.usePlugins();
6483
+ * ```
6484
+ *
6485
+ * @example Custom plugins
6486
+ * ```typescript
6487
+ * const task = new ReleaseTask();
6488
+ * const plugins = await task.usePlugins([
6489
+ * tuple(CustomPlugin, { option: 'value' })
6490
+ * ]);
6491
+ * ```
6492
+ */
4207
6493
  async usePlugins(externalTuples) {
4208
6494
  externalTuples = externalTuples || this.context.options.plugins || [];
4209
6495
  const plugins = await loaderPluginsFromPluginTuples(this.context, [
@@ -4218,12 +6504,65 @@ var ReleaseTask = class {
4218
6504
  });
4219
6505
  return plugins;
4220
6506
  }
6507
+ /**
6508
+ * Executes the release task
6509
+ *
6510
+ * Internal method that runs the task through the executor.
6511
+ * Preserves the context through the execution chain.
6512
+ *
6513
+ * @returns Execution result
6514
+ * @internal
6515
+ */
4221
6516
  async run() {
4222
6517
  return this.executor.exec(
4223
6518
  this.context,
4224
6519
  (context) => Promise.resolve(context)
4225
6520
  );
4226
6521
  }
6522
+ /**
6523
+ * Main entry point for executing the release task
6524
+ *
6525
+ * Checks environment conditions, loads plugins, and executes
6526
+ * the release process. Supports additional plugin configuration
6527
+ * at execution time.
6528
+ *
6529
+ * Environment Control:
6530
+ * - Checks FE_RELEASE environment variable
6531
+ * - Skips release if FE_RELEASE=false
6532
+ *
6533
+ * @param externalTuples - Additional plugin configurations
6534
+ * @returns Execution result
6535
+ * @throws Error if release is skipped via environment variable
6536
+ *
6537
+ * @example Basic execution
6538
+ * ```typescript
6539
+ * const task = new ReleaseTask();
6540
+ * await task.exec();
6541
+ * ```
6542
+ *
6543
+ * @example With additional plugins
6544
+ * ```typescript
6545
+ * const task = new ReleaseTask();
6546
+ * await task.exec([
6547
+ * tuple(CustomPlugin, { option: 'value' })
6548
+ * ]);
6549
+ * ```
6550
+ *
6551
+ * @example Environment control
6552
+ * ```typescript
6553
+ * // Skip release
6554
+ * process.env.FE_RELEASE = 'false';
6555
+ *
6556
+ * const task = new ReleaseTask();
6557
+ * try {
6558
+ * await task.exec();
6559
+ * } catch (e) {
6560
+ * if (e.message === 'Skip Release') {
6561
+ * console.log('Release skipped via environment variable');
6562
+ * }
6563
+ * }
6564
+ * ```
6565
+ */
4227
6566
  async exec(externalTuples) {
4228
6567
  if (this.context.env.get("FE_RELEASE") === "false") {
4229
6568
  throw new Error("Skip Release");