@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/cli.js CHANGED
@@ -4287,7 +4287,7 @@ import { Command } from "commander";
4287
4287
 
4288
4288
  // package.json
4289
4289
  var description = "A tool for releasing front-end projects, supporting multiple release modes and configurations, simplifying the release process and improving efficiency.";
4290
- var version = "3.0.0";
4290
+ var version = "3.1.2";
4291
4291
 
4292
4292
  // src/cli.ts
4293
4293
  var import_semver = __toESM(require_semver2(), 1);
@@ -4332,6 +4332,31 @@ import {
4332
4332
  ScriptContext
4333
4333
  } from "@qlover/scripts-context";
4334
4334
  var ReleaseContext = class extends ScriptContext {
4335
+ /**
4336
+ * Creates a new ReleaseContext instance
4337
+ *
4338
+ * Initializes the context with provided options and sets up
4339
+ * default values for required configuration:
4340
+ * - rootPath: Defaults to current working directory
4341
+ * - sourceBranch: Uses environment variables or default
4342
+ * - releaseEnv: Uses environment variables or 'development'
4343
+ *
4344
+ * Environment Variable Priority:
4345
+ * - sourceBranch: FE_RELEASE_BRANCH > FE_RELEASE_SOURCE_BRANCH > DEFAULT_SOURCE_BRANCH
4346
+ * - releaseEnv: FE_RELEASE_ENV > NODE_ENV > 'development'
4347
+ *
4348
+ * @param name - Unique identifier for this release context
4349
+ * @param options - Configuration options
4350
+ *
4351
+ * @example
4352
+ * ```typescript
4353
+ * const context = new ReleaseContext('web-app', {
4354
+ * rootPath: '/projects/web-app',
4355
+ * sourceBranch: 'main',
4356
+ * releaseEnv: 'production'
4357
+ * });
4358
+ * ```
4359
+ */
4335
4360
  constructor(name, options) {
4336
4361
  super(name, options);
4337
4362
  if (!this.options.rootPath) {
@@ -4348,27 +4373,124 @@ var ReleaseContext = class extends ScriptContext {
4348
4373
  });
4349
4374
  }
4350
4375
  }
4376
+ /**
4377
+ * Gets the root path of the project
4378
+ *
4379
+ * @returns Absolute path to project root
4380
+ *
4381
+ * @example
4382
+ * ```typescript
4383
+ * const root = context.rootPath;
4384
+ * // '/path/to/project'
4385
+ * ```
4386
+ */
4351
4387
  get rootPath() {
4352
4388
  return this.getOptions("rootPath");
4353
4389
  }
4390
+ /**
4391
+ * Gets the source branch for the release
4392
+ *
4393
+ * @returns Branch name to use as source
4394
+ *
4395
+ * @example
4396
+ * ```typescript
4397
+ * const branch = context.sourceBranch;
4398
+ * // 'main' or custom branch name
4399
+ * ```
4400
+ */
4354
4401
  get sourceBranch() {
4355
4402
  return this.getOptions("sourceBranch");
4356
4403
  }
4404
+ /**
4405
+ * Gets the release environment
4406
+ *
4407
+ * @returns Environment name (e.g., 'development', 'production')
4408
+ *
4409
+ * @example
4410
+ * ```typescript
4411
+ * const env = context.releaseEnv;
4412
+ * // 'development' or custom environment
4413
+ * ```
4414
+ */
4357
4415
  get releaseEnv() {
4358
4416
  return this.getOptions("releaseEnv");
4359
4417
  }
4418
+ /**
4419
+ * Gets all configured workspaces
4420
+ *
4421
+ * @returns Array of workspace configurations or undefined
4422
+ *
4423
+ * @example
4424
+ * ```typescript
4425
+ * const allWorkspaces = context.workspaces;
4426
+ * // [{ name: 'pkg-a', version: '1.0.0', ... }]
4427
+ * ```
4428
+ */
4360
4429
  get workspaces() {
4361
4430
  return this.getOptions("workspaces.workspaces");
4362
4431
  }
4432
+ /**
4433
+ * Gets the current active workspace
4434
+ *
4435
+ * @returns Current workspace configuration or undefined
4436
+ *
4437
+ * @example
4438
+ * ```typescript
4439
+ * const current = context.workspace;
4440
+ * // { name: 'pkg-a', version: '1.0.0', ... }
4441
+ * ```
4442
+ */
4363
4443
  get workspace() {
4364
4444
  return this.getOptions("workspaces.workspace");
4365
4445
  }
4446
+ /**
4447
+ * Sets the workspace configurations
4448
+ *
4449
+ * Updates the workspace list while preserving other workspace settings
4450
+ *
4451
+ * @param workspaces - Array of workspace configurations
4452
+ *
4453
+ * @example
4454
+ * ```typescript
4455
+ * context.setWorkspaces([{
4456
+ * name: 'pkg-a',
4457
+ * version: '1.0.0',
4458
+ * path: 'packages/a'
4459
+ * }]);
4460
+ * ```
4461
+ */
4366
4462
  setWorkspaces(workspaces) {
4367
4463
  this.options.workspaces = {
4368
4464
  ...this.options.workspaces,
4369
4465
  workspaces
4370
4466
  };
4371
4467
  }
4468
+ /**
4469
+ * Gets package.json data for the current workspace
4470
+ *
4471
+ * Provides type-safe access to package.json fields with optional
4472
+ * path and default value support.
4473
+ *
4474
+ * @param key - Optional dot-notation path to specific field
4475
+ * @param defaultValue - Default value if field not found
4476
+ * @returns Package data of type T
4477
+ * @throws Error if package.json not found
4478
+ *
4479
+ * @example Basic usage
4480
+ * ```typescript
4481
+ * // Get entire package.json
4482
+ * const pkg = context.getPkg();
4483
+ *
4484
+ * // Get specific field
4485
+ * const version = context.getPkg<string>('version');
4486
+ *
4487
+ * // Get nested field with default
4488
+ * const script = context.getPkg<string>(
4489
+ * 'scripts.build',
4490
+ * 'echo "No build script"'
4491
+ * );
4492
+ * ```
4493
+ */
4372
4494
  getPkg(key, defaultValue) {
4373
4495
  const packageJson = this.workspace?.packageJson;
4374
4496
  if (!packageJson) {
@@ -4379,6 +4501,28 @@ var ReleaseContext = class extends ScriptContext {
4379
4501
  }
4380
4502
  return (0, import_get.default)(packageJson, key, defaultValue);
4381
4503
  }
4504
+ /**
4505
+ * Generates template context for string interpolation
4506
+ *
4507
+ * Combines context options, workspace data, and specific paths
4508
+ * for use in template processing. Includes deprecated fields
4509
+ * for backward compatibility.
4510
+ *
4511
+ * @returns Combined template context
4512
+ *
4513
+ * @example
4514
+ * ```typescript
4515
+ * const context = releaseContext.getTemplateContext();
4516
+ * // {
4517
+ * // publishPath: 'packages/my-pkg',
4518
+ * // env: 'production', // deprecated
4519
+ * // branch: 'main', // deprecated
4520
+ * // releaseEnv: 'production', // use this instead
4521
+ * // sourceBranch: 'main', // use this instead
4522
+ * // ...other options
4523
+ * // }
4524
+ * ```
4525
+ */
4382
4526
  getTemplateContext() {
4383
4527
  return {
4384
4528
  ...this.getOptions(),
@@ -4389,6 +4533,28 @@ var ReleaseContext = class extends ScriptContext {
4389
4533
  branch: this.sourceBranch
4390
4534
  };
4391
4535
  }
4536
+ /**
4537
+ * Executes changeset CLI commands
4538
+ *
4539
+ * Automatically detects and uses appropriate package manager
4540
+ * (pnpm or npx) to run changeset commands.
4541
+ *
4542
+ * @param name - Changeset command name
4543
+ * @param args - Optional command arguments
4544
+ * @returns Command output
4545
+ *
4546
+ * @example Version bump
4547
+ * ```typescript
4548
+ * // Bump version with snapshot
4549
+ * await context.runChangesetsCli('version', ['--snapshot', 'alpha']);
4550
+ *
4551
+ * // Create new changeset
4552
+ * await context.runChangesetsCli('add');
4553
+ *
4554
+ * // Status check
4555
+ * await context.runChangesetsCli('status');
4556
+ * ```
4557
+ */
4392
4558
  async runChangesetsCli(name, args) {
4393
4559
  let packageManager = "pnpm";
4394
4560
  try {
@@ -4415,6 +4581,28 @@ var DEFAULT_RELEASE_CONFIG = {
4415
4581
  batchTagName: "batch-${length}-packages-${timestamp}"
4416
4582
  };
4417
4583
  var ReleaseParams = class {
4584
+ /**
4585
+ * Creates a new ReleaseParams instance
4586
+ *
4587
+ * Initializes with logger for debug output and optional configuration
4588
+ * overrides. Uses default configuration values for any unspecified options.
4589
+ *
4590
+ * @param logger - Logger instance for debug output
4591
+ * @param config - Optional configuration overrides
4592
+ *
4593
+ * @example
4594
+ * ```typescript
4595
+ * // Basic initialization
4596
+ * const params = new ReleaseParams(logger);
4597
+ *
4598
+ * // With custom configuration
4599
+ * const params = new ReleaseParams(logger, {
4600
+ * maxWorkspace: 5,
4601
+ * multiWorkspaceSeparator: '-',
4602
+ * workspaceVersionSeparator: '#'
4603
+ * });
4604
+ * ```
4605
+ */
4418
4606
  constructor(logger, config = {}) {
4419
4607
  this.logger = logger;
4420
4608
  this.config = {
@@ -4422,7 +4610,49 @@ var ReleaseParams = class {
4422
4610
  ...config
4423
4611
  };
4424
4612
  }
4613
+ /**
4614
+ * Current configuration
4615
+ * @private
4616
+ */
4425
4617
  config;
4618
+ /**
4619
+ * Generates a release branch name for a single package
4620
+ *
4621
+ * Uses the branch name template from shared configuration or
4622
+ * falls back to the default template 'release-${tagName}'.
4623
+ * Supports variable interpolation in the template.
4624
+ *
4625
+ * Template Variables:
4626
+ * - ${pkgName}: Package name
4627
+ * - ${releaseName}: Release name (same as pkgName)
4628
+ * - ${tagName}: Release tag
4629
+ * - All shared config properties
4630
+ *
4631
+ * @param releaseName - Name of the package being released
4632
+ * @param tagName - Version tag for the release
4633
+ * @param shared - Shared configuration with branch template
4634
+ * @returns Formatted branch name
4635
+ * @throws Error if branch name template is not a string
4636
+ *
4637
+ * @example
4638
+ * ```typescript
4639
+ * // With default template
4640
+ * const branch = params.getReleaseBranchName(
4641
+ * 'my-pkg',
4642
+ * 'v1.0.0',
4643
+ * {}
4644
+ * );
4645
+ * // 'release-v1.0.0'
4646
+ *
4647
+ * // With custom template
4648
+ * const branch = params.getReleaseBranchName(
4649
+ * 'my-pkg',
4650
+ * 'v1.0.0',
4651
+ * { branchName: '${pkgName}-release-${tagName}' }
4652
+ * );
4653
+ * // 'my-pkg-release-v1.0.0'
4654
+ * ```
4655
+ */
4426
4656
  getReleaseBranchName(releaseName, tagName, shared) {
4427
4657
  const branchNameTpl = shared.branchName || "release-${tagName}";
4428
4658
  if (typeof branchNameTpl !== "string") {
@@ -4437,6 +4667,49 @@ var ReleaseParams = class {
4437
4667
  ...shared
4438
4668
  });
4439
4669
  }
4670
+ /**
4671
+ * Generates a release branch name for multiple packages
4672
+ *
4673
+ * Uses the batch branch name template from configuration.
4674
+ * Supports variable interpolation with additional batch-specific
4675
+ * variables.
4676
+ *
4677
+ * Template Variables:
4678
+ * - ${pkgName}: Package name
4679
+ * - ${releaseName}: Combined release name
4680
+ * - ${tagName}: Combined tag name
4681
+ * - ${length}: Number of packages
4682
+ * - ${timestamp}: Current timestamp
4683
+ * - All shared config properties
4684
+ *
4685
+ * @param releaseName - Combined name of packages
4686
+ * @param tagName - Combined version tag
4687
+ * @param shared - Shared configuration
4688
+ * @param length - Number of packages in batch
4689
+ * @returns Formatted batch branch name
4690
+ * @throws Error if branch name template is not a string
4691
+ *
4692
+ * @example
4693
+ * ```typescript
4694
+ * // With default template
4695
+ * const branch = params.getBatchReleaseBranchName(
4696
+ * 'pkg-a@1.0.0_pkg-b@2.0.0',
4697
+ * 'batch-2-packages-1234567890',
4698
+ * {},
4699
+ * 2
4700
+ * );
4701
+ * // 'batch-pkg-a@1.0.0_pkg-b@2.0.0-2-packages-1234567890'
4702
+ *
4703
+ * // With custom template
4704
+ * const branch = params.getBatchReleaseBranchName(
4705
+ * 'pkg-a@1.0.0_pkg-b@2.0.0',
4706
+ * 'v1.0.0',
4707
+ * {},
4708
+ * 2,
4709
+ * );
4710
+ * // Custom formatted branch name
4711
+ * ```
4712
+ */
4440
4713
  getBatchReleaseBranchName(releaseName, tagName, shared, length) {
4441
4714
  const branchNameTpl = this.config.batchBranchName;
4442
4715
  if (typeof branchNameTpl !== "string") {
@@ -4453,6 +4726,45 @@ var ReleaseParams = class {
4453
4726
  timestamp: Date.now()
4454
4727
  });
4455
4728
  }
4729
+ /**
4730
+ * Generates a release name from workspace configurations
4731
+ *
4732
+ * For single packages, returns the package name.
4733
+ * For multiple packages, combines names and versions up to
4734
+ * the configured maximum number of workspaces.
4735
+ *
4736
+ * Format:
4737
+ * - Single: packageName
4738
+ * - Multiple: pkg1@1.0.0_pkg2@2.0.0_pkg3@3.0.0
4739
+ *
4740
+ * @param composeWorkspaces - Array of workspace configurations
4741
+ * @returns Formatted release name
4742
+ *
4743
+ * @example
4744
+ * ```typescript
4745
+ * // Single package
4746
+ * const name = params.getReleaseName([
4747
+ * { name: 'pkg-a', version: '1.0.0' }
4748
+ * ]);
4749
+ * // 'pkg-a'
4750
+ *
4751
+ * // Multiple packages
4752
+ * const name = params.getReleaseName([
4753
+ * { name: 'pkg-a', version: '1.0.0' },
4754
+ * { name: 'pkg-b', version: '2.0.0' }
4755
+ * ]);
4756
+ * // 'pkg-a@1.0.0_pkg-b@2.0.0'
4757
+ *
4758
+ * // With max limit
4759
+ * const name = params.getReleaseName([
4760
+ * { name: 'pkg-a', version: '1.0.0' },
4761
+ * { name: 'pkg-b', version: '2.0.0' },
4762
+ * { name: 'pkg-c', version: '3.0.0' },
4763
+ * { name: 'pkg-d', version: '4.0.0' }
4764
+ * ]);
4765
+ * // Only first 3: 'pkg-a@1.0.0_pkg-b@2.0.0_pkg-c@3.0.0'
4766
+ * ```
4767
+ */
4456
4768
  getReleaseName(composeWorkspaces) {
4457
4769
  if (composeWorkspaces.length === 1) {
4458
4770
  return composeWorkspaces[0].name;
@@ -4462,6 +4774,36 @@ var ReleaseParams = class {
4462
4774
  ({ name, version: version2 }) => `${name}${workspaceVersionSeparator}${version2}`
4463
4775
  ).join(multiWorkspaceSeparator);
4464
4776
  }
4777
+ /**
4778
+ * Generates a tag name for the release
4779
+ *
4780
+ * For single packages, uses the package version.
4781
+ * For multiple packages, generates a batch tag name using
4782
+ * the configured template.
4783
+ *
4784
+ * Format:
4785
+ * - Single: version
4786
+ * - Multiple: batch-${length}-packages-${timestamp}
4787
+ *
4788
+ * @param composeWorkspaces - Array of workspace configurations
4789
+ * @returns Formatted tag name
4790
+ *
4791
+ * @example
4792
+ * ```typescript
4793
+ * // Single package
4794
+ * const tag = params.getReleaseTagName([
4795
+ * { name: 'pkg-a', version: '1.0.0' }
4796
+ * ]);
4797
+ * // '1.0.0'
4798
+ *
4799
+ * // Multiple packages
4800
+ * const tag = params.getReleaseTagName([
4801
+ * { name: 'pkg-a', version: '1.0.0' },
4802
+ * { name: 'pkg-b', version: '2.0.0' }
4803
+ * ]);
4804
+ * // 'batch-2-packages-1234567890'
4805
+ * ```
4806
+ */
4465
4807
  getReleaseTagName(composeWorkspaces) {
4466
4808
  if (composeWorkspaces.length === 1) {
4467
4809
  return composeWorkspaces[0].version;
@@ -4472,6 +4814,44 @@ var ReleaseParams = class {
4472
4814
  timestamp: Date.now()
4473
4815
  });
4474
4816
  }
4817
+ /**
4818
+ * Generates branch and tag parameters for the release
4819
+ *
4820
+ * Combines the generation of branch name and tag name into
4821
+ * a single operation. Handles both single and multi-package
4822
+ * releases appropriately.
4823
+ *
4824
+ * @param composeWorkspaces - Array of workspace configurations
4825
+ * @param shared - Shared configuration
4826
+ * @returns Object containing tag name and branch name
4827
+ *
4828
+ * @example Single package
4829
+ * ```typescript
4830
+ * const params = releaseParams.getReleaseBranchParams(
4831
+ * [{ name: 'pkg-a', version: '1.0.0' }],
4832
+ * { branchName: 'release-${tagName}' }
4833
+ * );
4834
+ * // {
4835
+ * // tagName: '1.0.0',
4836
+ * // releaseBranch: 'release-1.0.0'
4837
+ * // }
4838
+ * ```
4839
+ *
4840
+ * @example Multiple packages
4841
+ * ```typescript
4842
+ * const params = releaseParams.getReleaseBranchParams(
4843
+ * [
4844
+ * { name: 'pkg-a', version: '1.0.0' },
4845
+ * { name: 'pkg-b', version: '2.0.0' }
4846
+ * ],
4847
+ * {}
4848
+ * );
4849
+ * // {
4850
+ * // tagName: 'batch-2-packages-1234567890',
4851
+ * // releaseBranch: 'batch-pkg-a@1.0.0_pkg-b@2.0.0-2-packages-1234567890'
4852
+ * // }
4853
+ * ```
4854
+ */
4475
4855
  getReleaseBranchParams(composeWorkspaces, shared) {
4476
4856
  const releaseTagName = this.getReleaseTagName(composeWorkspaces);
4477
4857
  const releaseName = this.getReleaseName(composeWorkspaces);
@@ -4486,6 +4866,50 @@ var ReleaseParams = class {
4486
4866
  releaseBranch: releaseBranchName
4487
4867
  };
4488
4868
  }
4869
+ /**
4870
+ * Generates a title for the release pull request
4871
+ *
4872
+ * Uses the configured PR title template or falls back to the default.
4873
+ * Supports variable interpolation with release parameters and context.
4874
+ *
4875
+ * Template Variables:
4876
+ * - ${tagName}: Release tag name
4877
+ * - ${pkgName}: Package/branch name
4878
+ * - All template context properties
4879
+ *
4880
+ * @param releaseBranchParams - Release branch parameters
4881
+ * @param context - Template context for variable interpolation
4882
+ * @returns Formatted PR title
4883
+ *
4884
+ * @example
4885
+ * ```typescript
4886
+ * // With default template
4887
+ * const title = params.getPRTitle(
4888
+ * {
4889
+ * tagName: '1.0.0',
4890
+ * releaseBranch: 'release-1.0.0'
4891
+ * },
4892
+ * {
4893
+ * env: 'production',
4894
+ * pkgName: 'my-package'
4895
+ * }
4896
+ * );
4897
+ * // 'Release production my-package 1.0.0'
4898
+ *
4899
+ * // With custom template
4900
+ * const title = params.getPRTitle(
4901
+ * {
4902
+ * tagName: '1.0.0',
4903
+ * releaseBranch: 'release-1.0.0'
4904
+ * },
4905
+ * {
4906
+ * env: 'staging',
4907
+ * pkgName: 'my-package'
4908
+ * }
4909
+ * );
4910
+ * // Custom formatted title
4911
+ * ```
4912
+ */
4489
4913
  getPRTitle(releaseBranchParams, context) {
4490
4914
  const prTitleTpl = this.config.PRTitle || DEFAULT_PR_TITLE;
4491
4915
  return Shell.format(prTitleTpl, {
@@ -4495,10 +4919,67 @@ var ReleaseParams = class {
4495
4919
  });
4496
4920
  }
4497
4921
  /**
4498
- * Gets the body for the release pull request.
4922
+ * Generates the body content for the release pull request
4923
+ *
4924
+ * Handles both single and multi-package releases, combining
4925
+ * changelogs appropriately. For batch releases, formats each
4926
+ * package's changelog according to the batch template.
4927
+ *
4928
+ * Template Variables:
4929
+ * - ${tagName}: Release tag or combined workspace versions
4930
+ * - ${changelog}: Single changelog or combined batch changelogs
4931
+ * - All template context properties
4932
+ *
4933
+ * @param composeWorkspaces - Array of workspace configurations
4934
+ * @param releaseBranchParams - Release branch parameters
4935
+ * @param context - Template context for variable interpolation
4936
+ * @returns Formatted PR body content
4499
4937
  *
4500
- * @param options - The options containing tag name and changelog.
4501
- * @returns The formatted release pull request body.
4938
+ * @example Single package
4939
+ * ```typescript
4940
+ * const body = params.getPRBody(
4941
+ * [{
4942
+ * name: 'pkg-a',
4943
+ * version: '1.0.0',
4944
+ * changelog: '- Feature: New functionality\n- Fix: Bug fix'
4945
+ * }],
4946
+ * {
4947
+ * tagName: '1.0.0',
4948
+ * releaseBranch: 'release-1.0.0'
4949
+ * },
4950
+ * context
4951
+ * );
4952
+ * // Custom formatted body with single changelog
4953
+ * ```
4954
+ *
4955
+ * @example Multiple packages
4956
+ * ```typescript
4957
+ * const body = params.getPRBody(
4958
+ * [
4959
+ * {
4960
+ * name: 'pkg-a',
4961
+ * version: '1.0.0',
4962
+ * changelog: '- Feature: Package A changes'
4963
+ * },
4964
+ * {
4965
+ * name: 'pkg-b',
4966
+ * version: '2.0.0',
4967
+ * changelog: '- Feature: Package B changes'
4968
+ * }
4969
+ * ],
4970
+ * {
4971
+ * tagName: 'batch-2-packages-1234567890',
4972
+ * releaseBranch: 'batch-release'
4973
+ * },
4974
+ * context
4975
+ * );
4976
+ * // Formatted body with combined changelogs:
4977
+ * // ## pkg-a 1.0.0
4978
+ * // - Feature: Package A changes
4979
+ * //
4980
+ * // ## pkg-b 2.0.0
4981
+ * // - Feature: Package B changes
4982
+ * ```
4502
4983
  */
4503
4984
  getPRBody(composeWorkspaces, releaseBranchParams, context) {
4504
4985
  const PRBodyTpl = this.config.PRBody;
@@ -4524,10 +5005,36 @@ var ReleaseParams = class {
4524
5005
  import { Shell as Shell2 } from "@qlover/scripts-context";
4525
5006
  import { Octokit } from "@octokit/rest";
4526
5007
  var GithubManager = class {
5008
+ /**
5009
+ * Creates a new GithubManager instance
5010
+ *
5011
+ * @param context - Release context containing configuration
5012
+ *
5013
+ * @example
5014
+ * ```typescript
5015
+ * const manager = new GithubManager(context);
5016
+ * ```
5017
+ */
4527
5018
  constructor(context) {
4528
5019
  this.context = context;
4529
5020
  }
5021
+ /** Lazy-loaded Octokit instance */
4530
5022
  _octokit = null;
5023
+ /**
5024
+ * Gets GitHub repository information
5025
+ *
5026
+ * Retrieves the owner and repository name from the context.
5027
+ * This information is required for most GitHub API operations.
5028
+ *
5029
+ * @returns Repository owner and name
5030
+ * @throws Error if owner or repo name is not set
5031
+ *
5032
+ * @example
5033
+ * ```typescript
5034
+ * const info = manager.getGitHubUserInfo();
5035
+ * // { owner: 'org-name', repo: 'repo-name' }
5036
+ * ```
5037
+ */
4531
5038
  getGitHubUserInfo() {
4532
5039
  const { authorName, repoName } = this.context.getOptions();
4533
5040
  if (!authorName || !repoName) {
@@ -4538,6 +5045,28 @@ var GithubManager = class {
4538
5045
  repo: repoName
4539
5046
  };
4540
5047
  }
5048
+ /**
5049
+ * Gets GitHub API token
5050
+ *
5051
+ * Retrieves the GitHub API token from environment variables.
5052
+ * The token name can be configured via the tokenRef option.
5053
+ *
5054
+ * @returns GitHub API token
5055
+ * @throws Error if token is not set
5056
+ *
5057
+ * @example Default token
5058
+ * ```typescript
5059
+ * const token = manager.getToken();
5060
+ * // Uses GITHUB_TOKEN env var
5061
+ * ```
5062
+ *
5063
+ * @example Custom token
5064
+ * ```typescript
5065
+ * context.options.githubPR.tokenRef = 'CUSTOM_TOKEN';
5066
+ * const token = manager.getToken();
5067
+ * // Uses CUSTOM_TOKEN env var
5068
+ * ```
5069
+ */
4541
5070
  getToken() {
4542
5071
  const { tokenRef = "GITHUB_TOKEN" } = this.context.getOptions("githubPR");
4543
5072
  const token = this.context.env.get(tokenRef);
@@ -4548,6 +5077,24 @@ var GithubManager = class {
4548
5077
  }
4549
5078
  return token;
4550
5079
  }
5080
+ /**
5081
+ * Gets Octokit instance
5082
+ *
5083
+ * Lazily initializes and returns an Octokit instance configured
5084
+ * with the GitHub token and timeout settings.
5085
+ *
5086
+ * @returns Configured Octokit instance
5087
+ * @throws Error if token retrieval fails
5088
+ *
5089
+ * @example
5090
+ * ```typescript
5091
+ * const octokit = manager.octokit;
5092
+ * await octokit.rest.issues.create({
5093
+ * ...manager.getGitHubUserInfo(),
5094
+ * title: 'Issue title'
5095
+ * });
5096
+ * ```
5097
+ */
4551
5098
  get octokit() {
4552
5099
  if (this._octokit) {
4553
5100
  return this._octokit;
@@ -4562,9 +5109,37 @@ var GithubManager = class {
4562
5109
  this._octokit = new Octokit(options);
4563
5110
  return this._octokit;
4564
5111
  }
5112
+ /**
5113
+ * Gets logger instance
5114
+ *
5115
+ * Provides access to the context's logger for consistent
5116
+ * logging across the manager.
5117
+ *
5118
+ * @returns Logger instance
5119
+ *
5120
+ * @example
5121
+ * ```typescript
5122
+ * manager.logger.info('Creating release...');
5123
+ * manager.logger.debug('API response:', response);
5124
+ * ```
5125
+ */
4565
5126
  get logger() {
4566
5127
  return this.context.logger;
4567
5128
  }
5129
+ /**
5130
+ * Gets shell interface
5131
+ *
5132
+ * Provides access to the context's shell interface for
5133
+ * executing Git commands and other shell operations.
5134
+ *
5135
+ * @returns Shell interface
5136
+ *
5137
+ * @example
5138
+ * ```typescript
5139
+ * await manager.shell.exec('git fetch origin');
5140
+ * await manager.shell.exec(['git', 'push', 'origin', 'main']);
5141
+ * ```
5142
+ */
4568
5143
  get shell() {
4569
5144
  return this.context.shell;
4570
5145
  }
@@ -4573,6 +5148,23 @@ var GithubManager = class {
4573
5148
  *
4574
5149
  * @default `squash`
4575
5150
  */
5151
+ /**
5152
+ * Gets auto-merge type for pull requests
5153
+ *
5154
+ * Determines how pull requests should be merged when
5155
+ * auto-merge is enabled. Defaults to 'squash'.
5156
+ *
5157
+ * @returns Auto-merge type ('merge', 'squash', or 'rebase')
5158
+ *
5159
+ * @example
5160
+ * ```typescript
5161
+ * const mergeType = manager.autoMergeType;
5162
+ * // 'squash' (default)
5163
+ *
5164
+ * context.options.autoMergeType = 'rebase';
5165
+ * manager.autoMergeType; // 'rebase'
5166
+ * ```
5167
+ */
4576
5168
  get autoMergeType() {
4577
5169
  return this.context.getOptions().autoMergeType || DEFAULT_AUTO_MERGE_TYPE;
4578
5170
  }
@@ -4581,6 +5173,25 @@ var GithubManager = class {
4581
5173
  *
4582
5174
  * @default `999999`
4583
5175
  */
5176
+ /**
5177
+ * Gets pull request number for dry runs
5178
+ *
5179
+ * Returns a placeholder PR number when running in dry-run mode.
5180
+ * This allows testing PR-related functionality without creating
5181
+ * actual pull requests.
5182
+ *
5183
+ * @returns Dry run PR number (default: '999999')
5184
+ *
5185
+ * @example
5186
+ * ```typescript
5187
+ * context.dryRun = true;
5188
+ * const prNumber = manager.dryRunPRNumber;
5189
+ * // '999999'
5190
+ *
5191
+ * context.options.githubPR.dryRunPRNumber = '123456';
5192
+ * manager.dryRunPRNumber; // '123456'
5193
+ * ```
5194
+ */
4584
5195
  get dryRunPRNumber() {
4585
5196
  return this.context.getOptions("githubPR.dryRunPRNumber", "999999");
4586
5197
  }
@@ -4589,6 +5200,23 @@ var GithubManager = class {
4589
5200
  *
4590
5201
  * @default `false`
4591
5202
  */
5203
+ /**
5204
+ * Gets auto-merge setting for release PRs
5205
+ *
5206
+ * Determines whether release pull requests should be
5207
+ * automatically merged after creation. Defaults to false.
5208
+ *
5209
+ * @returns True if auto-merge is enabled
5210
+ *
5211
+ * @example
5212
+ * ```typescript
5213
+ * const autoMerge = manager.autoMergeReleasePR;
5214
+ * // false (default)
5215
+ *
5216
+ * context.options.autoMergeReleasePR = true;
5217
+ * manager.autoMergeReleasePR; // true
5218
+ * ```
5219
+ */
4592
5220
  get autoMergeReleasePR() {
4593
5221
  return this.context.getOptions("autoMergeReleasePR") || DEFAULT_AUTO_MERGE_RELEASE_PR;
4594
5222
  }
@@ -4598,6 +5226,30 @@ var GithubManager = class {
4598
5226
  * @param prNumber - The pull request number to merge.
4599
5227
  * @param releaseBranch - The branch to merge into.
4600
5228
  */
5229
+ /**
5230
+ * Merges a pull request
5231
+ *
5232
+ * Merges the specified pull request using the configured
5233
+ * merge method. In dry-run mode, logs the merge action
5234
+ * without performing it.
5235
+ *
5236
+ * @param prNumber - Pull request number
5237
+ * @param releaseBranch - Branch to merge
5238
+ * @throws Error if merge fails
5239
+ *
5240
+ * @example Basic merge
5241
+ * ```typescript
5242
+ * await manager.mergePR('123', 'release-1.0.0');
5243
+ * // Merges PR #123 using configured merge method
5244
+ * ```
5245
+ *
5246
+ * @example Dry run
5247
+ * ```typescript
5248
+ * context.dryRun = true;
5249
+ * await manager.mergePR('123', 'release-1.0.0');
5250
+ * // Logs merge action without performing it
5251
+ * ```
5252
+ */
4601
5253
  async mergePR(prNumber, releaseBranch) {
4602
5254
  if (!prNumber) {
4603
5255
  this.logger.error("Failed to create Pull Request.", prNumber);
@@ -4617,6 +5269,24 @@ var GithubManager = class {
4617
5269
  merge_method: mergeMethod
4618
5270
  });
4619
5271
  }
5272
+ /**
5273
+ * Gets commits from a pull request
5274
+ *
5275
+ * Retrieves all commits associated with the specified pull request.
5276
+ * Useful for generating changelogs or analyzing changes.
5277
+ *
5278
+ * @param prNumber - Pull request number
5279
+ * @returns Promise resolving to array of commit information
5280
+ * @throws Error if request fails
5281
+ *
5282
+ * @example
5283
+ * ```typescript
5284
+ * const commits = await manager.getPullRequestCommits(123);
5285
+ * commits.forEach(commit => {
5286
+ * console.log(commit.sha, commit.commit.message);
5287
+ * });
5288
+ * ```
5289
+ */
4620
5290
  async getPullRequestCommits(prNumber) {
4621
5291
  const pr = await this.octokit.rest.pulls.listCommits({
4622
5292
  ...this.getGitHubUserInfo(),
@@ -4624,6 +5294,23 @@ var GithubManager = class {
4624
5294
  });
4625
5295
  return pr.data;
4626
5296
  }
5297
+ /**
5298
+ * Gets detailed information about a commit
5299
+ *
5300
+ * Retrieves detailed information about a specific commit,
5301
+ * including files changed, author details, and commit message.
5302
+ *
5303
+ * @param commitSha - Commit SHA
5304
+ * @returns Promise resolving to commit information
5305
+ * @throws Error if request fails
5306
+ *
5307
+ * @example
5308
+ * ```typescript
5309
+ * const info = await manager.getCommitInfo('abc123');
5310
+ * console.log(info.commit.message);
5311
+ * console.log(info.files.map(f => f.filename));
5312
+ * ```
5313
+ */
4627
5314
  async getCommitInfo(commitSha) {
4628
5315
  const pr = await this.octokit.rest.repos.getCommit({
4629
5316
  ...this.getGitHubUserInfo(),
@@ -4631,6 +5318,24 @@ var GithubManager = class {
4631
5318
  });
4632
5319
  return pr.data;
4633
5320
  }
5321
+ /**
5322
+ * Gets pull request information
5323
+ *
5324
+ * Retrieves detailed information about a pull request,
5325
+ * including title, body, labels, and review status.
5326
+ *
5327
+ * @param prNumber - Pull request number
5328
+ * @returns Promise resolving to pull request information
5329
+ * @throws Error if request fails
5330
+ *
5331
+ * @example
5332
+ * ```typescript
5333
+ * const pr = await manager.getPullRequest(123);
5334
+ * console.log(pr.title);
5335
+ * console.log(pr.labels.map(l => l.name));
5336
+ * console.log(pr.mergeable_state);
5337
+ * ```
5338
+ */
4634
5339
  async getPullRequest(prNumber) {
4635
5340
  const pr = await this.octokit.rest.pulls.get({
4636
5341
  ...this.getGitHubUserInfo(),
@@ -4644,6 +5349,27 @@ var GithubManager = class {
4644
5349
  * @param prNumber - The pull request number to check.
4645
5350
  * @param releaseBranch - The branch to check against.
4646
5351
  */
5352
+ /**
5353
+ * Checks pull request status and cleans up
5354
+ *
5355
+ * Verifies pull request status and deletes the release branch
5356
+ * if the PR has been merged. Used for post-merge cleanup.
5357
+ *
5358
+ * Process:
5359
+ * 1. Verify PR exists and status
5360
+ * 2. Delete release branch if PR merged
5361
+ * 3. Log cleanup results
5362
+ *
5363
+ * @param prNumber - Pull request number
5364
+ * @param releaseBranch - Branch to clean up
5365
+ * @throws Error if verification or cleanup fails
5366
+ *
5367
+ * @example
5368
+ * ```typescript
5369
+ * await manager.checkedPR('123', 'release-1.0.0');
5370
+ * // Verifies PR #123 and deletes release-1.0.0 if merged
5371
+ * ```
5372
+ */
4647
5373
  async checkedPR(prNumber, releaseBranch) {
4648
5374
  try {
4649
5375
  await this.getPullRequest(Number(prNumber));
@@ -4744,10 +5470,55 @@ var GithubManager = class {
4744
5470
  throw error;
4745
5471
  }
4746
5472
  }
5473
+ /**
5474
+ * Truncates long PR/release body text
5475
+ *
5476
+ * GitHub has a limit on PR and release body length.
5477
+ * This method ensures the text stays within limits by
5478
+ * truncating if necessary.
5479
+ *
5480
+ * @param body - Body text to truncate
5481
+ * @returns Truncated text (if > 124000 chars)
5482
+ *
5483
+ * @example
5484
+ * ```typescript
5485
+ * const body = manager.truncateBody(veryLongText);
5486
+ * // Returns truncated text if > 124000 chars
5487
+ * // Adds '...' to indicate truncation
5488
+ * ```
5489
+ * @private
5490
+ */
4747
5491
  truncateBody(body) {
4748
5492
  if (body && body.length >= 124e3) return body.substring(0, 124e3) + "...";
4749
5493
  return body;
4750
5494
  }
5495
+ /**
5496
+ * Builds GitHub release options
5497
+ *
5498
+ * Combines default release options with provided overrides
5499
+ * and context configuration. Handles formatting of release
5500
+ * name, body, and other settings.
5501
+ *
5502
+ * @param options - Override options for release
5503
+ * @returns Complete release options
5504
+ *
5505
+ * @example
5506
+ * ```typescript
5507
+ * const opts = manager.getOctokitReleaseOptions({
5508
+ * tag_name: 'v1.0.0',
5509
+ * body: 'Release notes...'
5510
+ * });
5511
+ * // Returns merged options with defaults:
5512
+ * // {
5513
+ * // name: 'Release v1.0.0',
5514
+ * // body: 'Release notes...',
5515
+ * // draft: false,
5516
+ * // prerelease: false,
5517
+ * // ...
5518
+ * // }
5519
+ * ```
5520
+ * @private
5521
+ */
4751
5522
  getOctokitReleaseOptions(options) {
4752
5523
  const {
4753
5524
  releaseName,
@@ -4773,6 +5544,37 @@ var GithubManager = class {
4773
5544
  ...this.getGitHubUserInfo()
4774
5545
  };
4775
5546
  }
5547
+ /**
5548
+ * Creates a GitHub release
5549
+ *
5550
+ * Creates a new GitHub release for a workspace with:
5551
+ * - Formatted release name
5552
+ * - Changelog as release notes
5553
+ * - Proper tag
5554
+ * - Configurable settings (draft, prerelease, etc.)
5555
+ *
5556
+ * Handles dry run mode and error cases gracefully.
5557
+ *
5558
+ * @param workspace - Workspace to create release for
5559
+ * @throws Error if tag name is missing or creation fails
5560
+ *
5561
+ * @example Basic release
5562
+ * ```typescript
5563
+ * await manager.createRelease({
5564
+ * name: 'pkg-a',
5565
+ * version: '1.0.0',
5566
+ * tagName: 'v1.0.0',
5567
+ * changelog: '...'
5568
+ * });
5569
+ * ```
5570
+ *
5571
+ * @example Dry run
5572
+ * ```typescript
5573
+ * context.dryRun = true;
5574
+ * await manager.createRelease(workspace);
5575
+ * // Logs release info without creating
5576
+ * ```
5577
+ */
4776
5578
  async createRelease(workspace) {
4777
5579
  const meragedOptions = this.getOctokitReleaseOptions({
4778
5580
  tag_name: workspace.tagName,
@@ -4812,6 +5614,30 @@ var GithubManager = class {
4812
5614
  var import_isString = __toESM(require_isString(), 1);
4813
5615
  import { ScriptPlugin } from "@qlover/scripts-context";
4814
5616
  var GitBase = class extends ScriptPlugin {
5617
+ /**
5618
+ * Plugin initialization hook
5619
+ *
5620
+ * Runs before plugin execution to set up repository context:
5621
+ * 1. Retrieves repository information
5622
+ * 2. Gets current branch
5623
+ * 3. Switches to current branch if needed
5624
+ * 4. Updates context with repository info
5625
+ *
5626
+ * @throws Error if repository information cannot be retrieved
5627
+ *
5628
+ * @example
5629
+ * ```typescript
5630
+ * class MyPlugin extends GitBase<GitBaseProps> {
5631
+ * async onExec() {
5632
+ * // onBefore has already:
5633
+ * // - Set up repository info
5634
+ * // - Switched to correct branch
5635
+ * // - Updated context
5636
+ * await this.doSomething();
5637
+ * }
5638
+ * }
5639
+ * ```
5640
+ */
4815
5641
  async onBefore() {
4816
5642
  const repoInfo = await this.getUserInfo();
4817
5643
  if (!repoInfo) {
@@ -4832,12 +5658,44 @@ var GitBase = class extends ScriptPlugin {
4832
5658
  currentBranch
4833
5659
  });
4834
5660
  }
5661
+ /**
5662
+ * Gets the current Git branch name
5663
+ *
5664
+ * Retrieves the name of the currently checked out Git branch.
5665
+ * Includes a small delay to ensure Git's internal state is updated.
5666
+ *
5667
+ * @returns Promise resolving to branch name
5668
+ * @throws Error if branch name cannot be retrieved
5669
+ *
5670
+ * @example
5671
+ * ```typescript
5672
+ * const branch = await plugin.getCurrentBranch();
5673
+ * // 'main' or 'feature/new-feature'
5674
+ * ```
5675
+ */
4835
5676
  async getCurrentBranch() {
4836
5677
  await new Promise((resolve2) => setTimeout(resolve2, 100));
4837
5678
  return this.context.shell.exec("git rev-parse --abbrev-ref HEAD", {
4838
5679
  dryRun: false
4839
5680
  });
4840
5681
  }
5682
+ /**
5683
+ * Gets the Git remote URL
5684
+ *
5685
+ * Retrieves the URL of the 'origin' remote from Git configuration.
5686
+ * This URL is used to identify the GitHub repository.
5687
+ *
5688
+ * @returns Promise resolving to remote URL
5689
+ * @throws Error if remote URL cannot be retrieved
5690
+ *
5691
+ * @example
5692
+ * ```typescript
5693
+ * const url = await plugin.getRemoteUrl();
5694
+ * // 'https://github.com/org/repo.git'
5695
+ * // or
5696
+ * // 'git@github.com:org/repo.git'
5697
+ * ```
5698
+ */
4841
5699
  async getRemoteUrl() {
4842
5700
  return (await this.context.shell.exec("git config --get remote.origin.url", {
4843
5701
  dryRun: false
@@ -4891,9 +5749,53 @@ var GitBase = class extends ScriptPlugin {
4891
5749
  * @param value - The value to check.
4892
5750
  * @returns True if the value is a valid string, otherwise false.
4893
5751
  */
5752
+ /**
5753
+ * Type guard for valid string values
5754
+ *
5755
+ * Checks if a value is a non-empty string. Used for validating
5756
+ * repository information and other string inputs.
5757
+ *
5758
+ * @param value - Value to check
5759
+ * @returns True if value is a non-empty string
5760
+ *
5761
+ * @example
5762
+ * ```typescript
5763
+ * if (plugin.isValidString(value)) {
5764
+ * // value is definitely a non-empty string
5765
+ * console.log(value.toUpperCase());
5766
+ * }
5767
+ * ```
5768
+ */
4894
5769
  isValidString(value) {
4895
5770
  return !!value && (0, import_isString.default)(value);
4896
5771
  }
5772
+ /**
5773
+ * Creates a Git commit
5774
+ *
5775
+ * Creates a new Git commit with the specified message and optional
5776
+ * additional arguments. The message is automatically JSON-stringified
5777
+ * to handle special characters properly.
5778
+ *
5779
+ * @param message - Commit message
5780
+ * @param args - Additional Git commit arguments
5781
+ * @returns Promise resolving to command output
5782
+ *
5783
+ * @example Basic commit
5784
+ * ```typescript
5785
+ * await plugin.commit('feat: add new feature');
5786
+ * ```
5787
+ *
5788
+ * @example Commit with arguments
5789
+ * ```typescript
5790
+ * await plugin.commit('fix: update deps', ['--no-verify']);
5791
+ * ```
5792
+ *
5793
+ * @example Commit with special characters
5794
+ * ```typescript
5795
+ * await plugin.commit('fix: handle "quotes" & symbols');
5796
+ * // Message is automatically escaped
5797
+ * ```
5798
+ */
4897
5799
  commit(message, args = []) {
4898
5800
  return this.context.shell.exec([
4899
5801
  "git",
@@ -4928,14 +5830,50 @@ var CHANGELOG_ALL_FIELDS = [
4928
5830
  "tag"
4929
5831
  ];
4930
5832
  var GitChangelog = class {
5833
+ /**
5834
+ * Creates a new GitChangelog instance
5835
+ *
5836
+ * @param options - Configuration options including shell and logger
5837
+ *
5838
+ * @example
5839
+ * ```typescript
5840
+ * const changelog = new GitChangelog({
5841
+ * shell: new Shell(),
5842
+ * logger: new Logger(),
5843
+ * directory: 'packages/my-pkg',
5844
+ * noMerges: true
5845
+ * });
5846
+ * ```
5847
+ */
4931
5848
  constructor(options) {
4932
5849
  this.options = options;
4933
5850
  }
4934
5851
  /**
4935
- * Get the git log
5852
+ * Retrieves Git commit history with specified options
5853
+ *
5854
+ * Fetches commit information between specified tags or commits,
5855
+ * with support for filtering and field selection.
5856
+ *
5857
+ * @param options - Configuration options for Git log retrieval
5858
+ * @returns Array of commit objects with requested fields
4936
5859
  *
4937
- * @param options
4938
- * @returns
5860
+ * @example Basic usage
5861
+ * ```typescript
5862
+ * const commits = await changelog.getGitLog({
5863
+ * from: 'v1.0.0',
5864
+ * to: 'v2.0.0',
5865
+ * directory: 'packages/my-pkg',
5866
+ * noMerges: true
5867
+ * });
5868
+ * ```
5869
+ *
5870
+ * @example Custom fields
5871
+ * ```typescript
5872
+ * const commits = await changelog.getGitLog({
5873
+ * fields: ['hash', 'subject', 'authorName'],
5874
+ * directory: 'src'
5875
+ * });
5876
+ * ```
4939
5877
  */
4940
5878
  async getGitLog(options = {}) {
4941
5879
  const { directory, noMerges = true, fields } = options;
@@ -4956,6 +5894,38 @@ var GitChangelog = class {
4956
5894
  this.options.logger?.debug("GitChangelog commits", commits);
4957
5895
  return commits;
4958
5896
  }
5897
+ /**
5898
+ * Retrieves and parses Git commits with metadata
5899
+ *
5900
+ * Gets commit history and enhances it with parsed conventional
5901
+ * commit information and PR metadata.
5902
+ *
5903
+ * @param options - Configuration options for Git log retrieval
5904
+ * @returns Array of enhanced commit objects with parsed metadata
5905
+ *
5906
+ * @example Basic usage
5907
+ * ```typescript
5908
+ * const commits = await changelog.getCommits({
5909
+ * from: 'v1.0.0',
5910
+ * to: 'v2.0.0'
5911
+ * });
5912
+ * // [
5913
+ * // {
5914
+ * // base: { hash: '...', subject: '...' },
5915
+ * // commitlint: { type: 'feat', scope: 'api', ... },
5916
+ * // commits: []
5917
+ * // }
5918
+ * // ]
5919
+ * ```
5920
+ *
5921
+ * @example Filtered commits
5922
+ * ```typescript
5923
+ * const commits = await changelog.getCommits({
5924
+ * directory: 'packages/my-pkg',
5925
+ * noMerges: true
5926
+ * });
5927
+ * ```
5928
+ */
4959
5929
  async getCommits(options) {
4960
5930
  const gitCommits = await this.getGitLog(options);
4961
5931
  return gitCommits.map((commit) => {
@@ -4968,6 +5938,28 @@ var GitChangelog = class {
4968
5938
  };
4969
5939
  });
4970
5940
  }
5941
+ /**
5942
+ * Creates a base commit object from message and optional data
5943
+ *
5944
+ * Utility method to create a standardized commit object with
5945
+ * basic metadata. Used internally for commit value creation.
5946
+ *
5947
+ * @param message - Commit message
5948
+ * @param target - Optional additional commit data
5949
+ * @returns Base commit object
5950
+ * @protected
5951
+ *
5952
+ * @example
5953
+ * ```typescript
5954
+ * const commit = changelog.createBaseCommit(
5955
+ * 'feat: new feature',
5956
+ * {
5957
+ * hash: 'abc123',
5958
+ * authorName: 'John Doe'
5959
+ * }
5960
+ * );
5961
+ * ```
5962
+ */
4971
5963
  createBaseCommit(message, target) {
4972
5964
  return {
4973
5965
  subject: message,
@@ -4977,16 +5969,66 @@ var GitChangelog = class {
4977
5969
  };
4978
5970
  }
4979
5971
  /**
4980
- * Tabify the body
5972
+ * Indents each line of a text block
5973
+ *
5974
+ * Adds specified number of spaces to the start of each line
5975
+ * in a multi-line string. Used for formatting commit body text.
4981
5976
  *
4982
5977
  * @since 2.3.2
4983
- * @param body
4984
- * @param size
4985
- * @returns
5978
+ * @param body - Text to indent
5979
+ * @param size - Number of spaces to add (default: 2)
5980
+ * @returns Indented text
5981
+ *
5982
+ * @example
5983
+ * ```typescript
5984
+ * const text = changelog.tabify(
5985
+ * 'Line 1\nLine 2\nLine 3',
5986
+ * 4
5987
+ * );
5988
+ * // ' Line 1\n Line 2\n Line 3'
5989
+ * ```
4986
5990
  */
4987
5991
  tabify(body, size = 2) {
4988
5992
  return body.split("\n").map((line) => " ".repeat(size) + line.trim()).join("\n");
4989
5993
  }
5994
+ /**
5995
+ * Parses a commit message into conventional commit format
5996
+ *
5997
+ * Extracts type, scope, message, and body from a commit message
5998
+ * following the conventional commit specification.
5999
+ *
6000
+ * Format: type(scope): message
6001
+ *
6002
+ * @param subject - Commit subject line
6003
+ * @param rawBody - Full commit message body
6004
+ * @returns Parsed conventional commit data
6005
+ *
6006
+ * @example Basic commit
6007
+ * ```typescript
6008
+ * const commit = changelog.parseCommitlint(
6009
+ * 'feat(api): add new endpoint'
6010
+ * );
6011
+ * // {
6012
+ * // type: 'feat',
6013
+ * // scope: 'api',
6014
+ * // message: 'add new endpoint'
6015
+ * // }
6016
+ * ```
6017
+ *
6018
+ * @example With body
6019
+ * ```typescript
6020
+ * const commit = changelog.parseCommitlint(
6021
+ * 'fix(core): memory leak',
6022
+ * 'Fixed memory leak in core module\n\nBREAKING CHANGE: API changed'
6023
+ * );
6024
+ * // {
6025
+ * // type: 'fix',
6026
+ * // scope: 'core',
6027
+ * // message: 'memory leak',
6028
+ * // body: ' Fixed memory leak in core module\n\n BREAKING CHANGE: API changed'
6029
+ * // }
6030
+ * ```
6031
+ */
4990
6032
  parseCommitlint(subject, rawBody = "") {
4991
6033
  const [title] = subject.trim().split("\n");
4992
6034
  const bodyLines = rawBody.startsWith(title) ? rawBody.replace(title, "") : rawBody;
@@ -5005,6 +6047,51 @@ var GitChangelog = class {
5005
6047
  body: bodyLines ? this.tabify(bodyLines) : void 0
5006
6048
  };
5007
6049
  }
6050
+ /**
6051
+ * Creates a complete commit value object from hash and message
6052
+ *
6053
+ * Combines commit hash, parsed conventional commit data, and
6054
+ * PR information into a single commit value object.
6055
+ *
6056
+ * @param hash - Commit hash
6057
+ * @param message - Full commit message
6058
+ * @returns Complete commit value object
6059
+ *
6060
+ * @example Basic commit
6061
+ * ```typescript
6062
+ * const commit = changelog.toCommitValue(
6063
+ * 'abc123',
6064
+ * 'feat(api): new endpoint'
6065
+ * );
6066
+ * // {
6067
+ * // base: {
6068
+ * // hash: 'abc123',
6069
+ * // abbrevHash: 'abc123',
6070
+ * // subject: 'feat(api): new endpoint'
6071
+ * // },
6072
+ * // commitlint: {
6073
+ * // type: 'feat',
6074
+ * // scope: 'api',
6075
+ * // message: 'new endpoint'
6076
+ * // },
6077
+ * // commits: []
6078
+ * // }
6079
+ * ```
6080
+ *
6081
+ * @example PR commit
6082
+ * ```typescript
6083
+ * const commit = changelog.toCommitValue(
6084
+ * 'def456',
6085
+ * 'fix(core): memory leak (#123)'
6086
+ * );
6087
+ * // {
6088
+ * // base: { hash: 'def456', ... },
6089
+ * // commitlint: { type: 'fix', ... },
6090
+ * // commits: [],
6091
+ * // prNumber: '123'
6092
+ * // }
6093
+ * ```
6094
+ */
5008
6095
  toCommitValue(hash, message) {
5009
6096
  const [title] = message.trim().split("\n");
5010
6097
  const prMatch = title.match(/\(#(\d+)\)/);
@@ -5022,6 +6109,33 @@ var GitChangelog = class {
5022
6109
  prNumber: prMatch?.[1]
5023
6110
  };
5024
6111
  }
6112
+ /**
6113
+ * Resolves a Git tag or reference to a valid commit reference
6114
+ *
6115
+ * Attempts to resolve a tag name to a valid Git reference.
6116
+ * Falls back to root commit or HEAD if tag doesn't exist.
6117
+ *
6118
+ * @param tag - Tag name to resolve
6119
+ * @param fallback - Fallback value ('root' or 'HEAD')
6120
+ * @returns Resolved Git reference
6121
+ * @protected
6122
+ *
6123
+ * @example Basic tag resolution
6124
+ * ```typescript
6125
+ * const ref = await changelog.resolveTag('v1.0.0');
6126
+ * // 'v1.0.0' if tag exists
6127
+ * // 'HEAD' if tag doesn't exist
6128
+ * ```
6129
+ *
6130
+ * @example Root commit fallback
6131
+ * ```typescript
6132
+ * const ref = await changelog.resolveTag(
6133
+ * 'non-existent-tag',
6134
+ * 'root'
6135
+ * );
6136
+ * // First commit hash if tag doesn't exist
6137
+ * ```
6138
+ */
5025
6139
  async resolveTag(tag, fallback) {
5026
6140
  if (tag) {
5027
6141
  try {
@@ -5042,9 +6156,75 @@ var import_groupBy = __toESM(require_groupBy(), 1);
5042
6156
  import { Shell as Shell3 } from "@qlover/scripts-context";
5043
6157
  var DEFAULT_TEMPLATE = "\n- ${scopeHeader} ${commitlint.message} ${commitLink} ${prLink}";
5044
6158
  var GitChangelogFormatter = class {
6159
+ /**
6160
+ * Creates a new GitChangelogFormatter instance
6161
+ *
6162
+ * @param options - Configuration options including shell interface
6163
+ *
6164
+ * @example
6165
+ * ```typescript
6166
+ * const formatter = new GitChangelogFormatter({
6167
+ * shell: new Shell(),
6168
+ * repoUrl: 'https://github.com/org/repo',
6169
+ * types: [
6170
+ * { type: 'feat', section: '### Features' }
6171
+ * ],
6172
+ * formatTemplate: '- ${commitlint.message}'
6173
+ * });
6174
+ * ```
6175
+ */
5045
6176
  constructor(options) {
5046
6177
  this.options = options;
5047
6178
  }
6179
+ /**
6180
+ * Formats an array of commits into changelog entries
6181
+ *
6182
+ * Groups commits by type and formats them according to the
6183
+ * configured template and options. Supports commit body
6184
+ * inclusion and type-based sections.
6185
+ *
6186
+ * @param commits - Array of commit values to format
6187
+ * @param options - Optional formatting options
6188
+ * @returns Array of formatted changelog lines
6189
+ *
6190
+ * @example Basic formatting
6191
+ * ```typescript
6192
+ * const changelog = formatter.format([
6193
+ * {
6194
+ * base: { hash: 'abc123' },
6195
+ * commitlint: {
6196
+ * type: 'feat',
6197
+ * scope: 'api',
6198
+ * message: 'new endpoint'
6199
+ * }
6200
+ * }
6201
+ * ]);
6202
+ * // [
6203
+ * // '### Features',
6204
+ * // '- **api:** new endpoint ([abc123](...))'
6205
+ * // ]
6206
+ * ```
6207
+ *
6208
+ * @example With commit body
6209
+ * ```typescript
6210
+ * const changelog = formatter.format(
6211
+ * [{
6212
+ * commitlint: {
6213
+ * type: 'fix',
6214
+ * message: 'memory leak',
6215
+ * body: 'Fixed memory allocation\nAdded cleanup'
6216
+ * }
6217
+ * }],
6218
+ * { commitBody: true }
6219
+ * );
6220
+ * // [
6221
+ * // '### Bug Fixes',
6222
+ * // '- memory leak',
6223
+ * // ' Fixed memory allocation',
6224
+ * // ' Added cleanup'
6225
+ * // ]
6226
+ * ```
6227
+ */
5048
6228
  format(commits, options) {
5049
6229
  const { types = [], commitBody = false } = { ...this.options, ...options };
5050
6230
  const changelog = [];
@@ -5071,6 +6251,41 @@ var GitChangelogFormatter = class {
5071
6251
  });
5072
6252
  return changelog;
5073
6253
  }
6254
+ /**
6255
+ * Formats a single commit into a changelog entry
6256
+ *
6257
+ * Applies the configured template to a commit, including
6258
+ * scope formatting, PR links, and commit hash links.
6259
+ *
6260
+ * @param commit - Commit value to format
6261
+ * @param options - Optional formatting options
6262
+ * @returns Formatted changelog entry
6263
+ *
6264
+ * @example Basic formatting
6265
+ * ```typescript
6266
+ * const entry = formatter.formatCommit({
6267
+ * base: { hash: 'abc123' },
6268
+ * commitlint: {
6269
+ * type: 'feat',
6270
+ * scope: 'api',
6271
+ * message: 'new endpoint'
6272
+ * }
6273
+ * });
6274
+ * // '- **api:** new endpoint ([abc123](...))'
6275
+ * ```
6276
+ *
6277
+ * @example With PR number
6278
+ * ```typescript
6279
+ * const entry = formatter.formatCommit({
6280
+ * base: { hash: 'def456' },
6281
+ * commitlint: {
6282
+ * message: 'fix bug'
6283
+ * },
6284
+ * prNumber: '123'
6285
+ * });
6286
+ * // '- fix bug ([def456](...)) (#123)'
6287
+ * ```
6288
+ */
5074
6289
  formatCommit(commit, options) {
5075
6290
  const {
5076
6291
  commitlint,
@@ -5097,12 +6312,65 @@ var GitChangelogFormatter = class {
5097
6312
  prLink
5098
6313
  });
5099
6314
  }
6315
+ /**
6316
+ * Formats a target string as a Markdown link
6317
+ *
6318
+ * Creates a Markdown-formatted link with optional URL.
6319
+ * If no URL is provided, formats as a plain reference.
6320
+ *
6321
+ * @param target - Text to display
6322
+ * @param url - Optional URL for the link
6323
+ * @returns Formatted Markdown link
6324
+ *
6325
+ * @example With URL
6326
+ * ```typescript
6327
+ * const link = formatter.foramtLink('abc123', 'https://github.com/org/repo/commit/abc123');
6328
+ * // '([abc123](https://github.com/org/repo/commit/abc123))'
6329
+ * ```
6330
+ *
6331
+ * @example Without URL
6332
+ * ```typescript
6333
+ * const link = formatter.foramtLink('abc123');
6334
+ * // '(abc123)'
6335
+ * ```
6336
+ */
5100
6337
  foramtLink(target, url) {
5101
6338
  return url ? `([${target}](${url}))` : `(${target})`;
5102
6339
  }
6340
+ /**
6341
+ * Formats a commit hash as a Markdown link
6342
+ *
6343
+ * @deprecated Use foramtLink instead
6344
+ * @param target - Commit hash to display
6345
+ * @param url - Optional URL to the commit
6346
+ * @returns Formatted Markdown link
6347
+ *
6348
+ * @example
6349
+ * ```typescript
6350
+ * const link = formatter.formatCommitLink(
6351
+ * 'abc123',
6352
+ * 'https://github.com/org/repo/commit/abc123'
6353
+ * );
6354
+ * // '([abc123](https://github.com/org/repo/commit/abc123))'
6355
+ * ```
6356
+ */
5103
6357
  formatCommitLink(target, url) {
5104
6358
  return url ? `([${target}](${url}))` : `(${target})`;
5105
6359
  }
6360
+ /**
6361
+ * Formats a commit scope in Markdown
6362
+ *
6363
+ * Wraps the scope in bold syntax and adds a colon.
6364
+ *
6365
+ * @param scope - Scope to format
6366
+ * @returns Formatted scope in Markdown
6367
+ *
6368
+ * @example
6369
+ * ```typescript
6370
+ * const scope = formatter.formatScope('api');
6371
+ * // '**api:**'
6372
+ * ```
6373
+ */
5106
6374
  formatScope(scope) {
5107
6375
  return `**${scope}:**`;
5108
6376
  }
@@ -5156,7 +6424,30 @@ var Pather = class {
5156
6424
  return child[boundaryIndex] === sep;
5157
6425
  }
5158
6426
  /**
5159
- * Normalised `startsWith` helper.
6427
+ * Normalized path prefix check
6428
+ *
6429
+ * Checks if sourcePath starts with targetPath after normalization.
6430
+ * Handles cross-platform path separators and trailing separators.
6431
+ *
6432
+ * @param sourcePath - Path to check
6433
+ * @param targetPath - Prefix path to match
6434
+ * @returns True if sourcePath starts with targetPath
6435
+ *
6436
+ * @example Basic usage
6437
+ * ```typescript
6438
+ * const pather = new Pather();
6439
+ *
6440
+ * pather.startsWith('src/utils/file.ts', 'src') // true
6441
+ * pather.startsWith('src\\utils\\file.ts', 'src') // true
6442
+ * pather.startsWith('lib/utils/file.ts', 'src') // false
6443
+ * ```
6444
+ *
6445
+ * @example Trailing separators
6446
+ * ```typescript
6447
+ * pather.startsWith('src/utils', 'src/') // true
6448
+ * pather.startsWith('src/utils/', 'src') // true
6449
+ * pather.startsWith('src2/utils', 'src') // false
6450
+ * ```
5160
6451
  */
5161
6452
  startsWith(sourcePath, targetPath) {
5162
6453
  let src = this.toLocalPath(sourcePath);
@@ -5170,7 +6461,44 @@ var Pather = class {
5170
6461
  return src.startsWith(tgt);
5171
6462
  }
5172
6463
  /**
5173
- * Segment-aware containment check (not mere substring).
6464
+ * Segment-aware path containment check
6465
+ *
6466
+ * Checks if sourcePath contains targetPath as a complete path segment.
6467
+ * Unlike simple substring matching, this ensures proper path boundaries.
6468
+ * For example, 'src/abc' does not contain 'src/a' even though 'src/a'
6469
+ * is a substring.
6470
+ *
6471
+ * Features:
6472
+ * - Cross-platform path handling
6473
+ * - Proper segment boundary checking
6474
+ * - Trailing separator normalization
6475
+ * - Exact match support
6476
+ *
6477
+ * @param sourcePath - Path to search in
6478
+ * @param targetPath - Path to search for
6479
+ * @returns True if sourcePath contains targetPath as a segment
6480
+ *
6481
+ * @example Basic usage
6482
+ * ```typescript
6483
+ * const pather = new Pather();
6484
+ *
6485
+ * pather.containsPath('src/utils/file.ts', 'utils') // true
6486
+ * pather.containsPath('src/utils/file.ts', 'src/utils') // true
6487
+ * pather.containsPath('src/utils/file.ts', 'til') // false
6488
+ * ```
6489
+ *
6490
+ * @example Segment boundaries
6491
+ * ```typescript
6492
+ * pather.containsPath('src/abc/file.ts', 'src/a') // false
6493
+ * pather.containsPath('src/abc/file.ts', 'src/abc') // true
6494
+ * ```
6495
+ *
6496
+ * @example Trailing separators
6497
+ * ```typescript
6498
+ * pather.containsPath('src/utils/', 'utils') // true
6499
+ * pather.containsPath('src/utils', 'utils/') // true
6500
+ * pather.containsPath('src/utils/', 'utils/') // true
6501
+ * ```
5174
6502
  */
5175
6503
  containsPath(sourcePath, targetPath) {
5176
6504
  let src = this.toLocalPath(sourcePath);
@@ -5196,18 +6524,49 @@ var Pather = class {
5196
6524
  // src/plugins/githubPR/GithubChangelog.ts
5197
6525
  var DOMAIN = "https://github.com";
5198
6526
  var GithubChangelog = class _GithubChangelog extends GitChangelog {
6527
+ /**
6528
+ * Creates a new GitHub changelog generator
6529
+ *
6530
+ * @param options - Changelog generation options
6531
+ * @param githubManager - GitHub API manager
6532
+ *
6533
+ * @example
6534
+ * ```typescript
6535
+ * const changelog = new GithubChangelog({
6536
+ * shell,
6537
+ * logger,
6538
+ * mergePRcommit: true,
6539
+ * githubRootPath: 'https://github.com/org/repo'
6540
+ * }, githubManager);
6541
+ * ```
6542
+ */
5199
6543
  constructor(options, githubManager) {
5200
6544
  super(options);
5201
6545
  this.options = options;
5202
6546
  this.githubManager = githubManager;
5203
6547
  }
6548
+ /** Path manipulation utility */
5204
6549
  pather = new Pather();
5205
6550
  /**
5206
- * Filter commits by directory
5207
- * @param commits - commits
5208
- * @param directory - directory
5209
- * @returns filtered commits
6551
+ * Filters commits by directory
6552
+ *
6553
+ * Filters commits based on whether they contain changes in
6554
+ * the specified directory. Uses GitHub API to get detailed
6555
+ * commit information.
6556
+ *
6557
+ * @param commits - Array of commits to filter
6558
+ * @param directory - Directory path to filter by
6559
+ * @returns Promise resolving to filtered commits
5210
6560
  * @since 2.4.0
6561
+ *
6562
+ * @example
6563
+ * ```typescript
6564
+ * const commits = await changelog.filterCommitsByDirectory(
6565
+ * allCommits,
6566
+ * 'packages/pkg-a'
6567
+ * );
6568
+ * // Only commits that modified files in packages/pkg-a
6569
+ * ```
5211
6570
  */
5212
6571
  async filterCommitsByDirectory(commits, directory) {
5213
6572
  const result = [];
@@ -5225,6 +6584,41 @@ var GithubChangelog = class _GithubChangelog extends GitChangelog {
5225
6584
  }
5226
6585
  return result;
5227
6586
  }
6587
+ /**
6588
+ * Gets complete commit information with PR details
6589
+ *
6590
+ * Retrieves commits and enhances them with pull request
6591
+ * information. For commits associated with PRs, includes
6592
+ * all PR commits and filters by directory.
6593
+ *
6594
+ * Process:
6595
+ * 1. Get base commits
6596
+ * 2. Extract PR numbers
6597
+ * 3. Fetch PR commits
6598
+ * 4. Filter by directory
6599
+ * 5. Flatten results
6600
+ *
6601
+ * @param options - Changelog options
6602
+ * @returns Promise resolving to enhanced commits
6603
+ *
6604
+ * @example Basic usage
6605
+ * ```typescript
6606
+ * const commits = await changelog.getFullCommit({
6607
+ * from: 'v1.0.0',
6608
+ * directory: 'packages/pkg-a'
6609
+ * });
6610
+ * // Returns commits with PR information
6611
+ * ```
6612
+ *
6613
+ * @example With PR merging
6614
+ * ```typescript
6615
+ * const commits = await changelog.getFullCommit({
6616
+ * mergePRcommit: true,
6617
+ * directory: 'packages/pkg-a'
6618
+ * });
6619
+ * // Includes all PR commits
6620
+ * ```
6621
+ */
5228
6622
  async getFullCommit(options) {
5229
6623
  const _options = { ...this.options, ...options };
5230
6624
  const allCommits = await this.getCommits(_options);
@@ -5252,6 +6646,41 @@ var GithubChangelog = class _GithubChangelog extends GitChangelog {
5252
6646
  );
5253
6647
  return newallCommits.flat();
5254
6648
  }
6649
+ /**
6650
+ * Transforms workspaces with GitHub changelogs
6651
+ *
6652
+ * Processes each workspace to add GitHub-specific changelog
6653
+ * information. Includes:
6654
+ * - GitHub repository URL
6655
+ * - PR-aware commit history
6656
+ * - Formatted changelog with links
6657
+ *
6658
+ * Process:
6659
+ * 1. Build GitHub root path
6660
+ * 2. Configure changelog options
6661
+ * 3. Get commits for each workspace
6662
+ * 4. Format changelog with links
6663
+ * 5. Update workspace objects
6664
+ *
6665
+ * @param workspaces - Array of workspaces to process
6666
+ * @param context - Release context
6667
+ * @returns Promise resolving to updated workspaces
6668
+ *
6669
+ * @example
6670
+ * ```typescript
6671
+ * const workspaces = await changelog.transformWorkspace(
6672
+ * [
6673
+ * {
6674
+ * name: 'pkg-a',
6675
+ * path: 'packages/a',
6676
+ * lastTag: 'v1.0.0'
6677
+ * }
6678
+ * ],
6679
+ * context
6680
+ * );
6681
+ * // Returns workspaces with GitHub-formatted changelogs
6682
+ * ```
6683
+ */
5255
6684
  async transformWorkspace(workspaces, context) {
5256
6685
  const githubRootPath = [
5257
6686
  DOMAIN,
@@ -5301,6 +6730,26 @@ import { Shell as Shell4 } from "@qlover/scripts-context";
5301
6730
  var DEFAULT_RELEASE_NAME = "Release ${name} v${version}";
5302
6731
  var DEFAULT_COMMIT_MESSAGE = "chore(tag): ${name} v${version}";
5303
6732
  var GithubPR = class extends GitBase {
6733
+ /**
6734
+ * Creates a new GithubPR plugin instance
6735
+ *
6736
+ * Initializes the plugin with GitHub-specific configuration and
6737
+ * sets up release parameters and GitHub manager.
6738
+ *
6739
+ * @param context - Release context
6740
+ * @param props - Plugin configuration
6741
+ *
6742
+ * @example
6743
+ * ```typescript
6744
+ * const plugin = new GithubPR(context, {
6745
+ * releasePR: true,
6746
+ * releaseName: 'Release v${version}',
6747
+ * commitMessage: 'chore: release v${version}',
6748
+ * draft: false,
6749
+ * preRelease: false
6750
+ * });
6751
+ * ```
6752
+ */
5304
6753
  constructor(context, props) {
5305
6754
  super(context, "githubPR", {
5306
6755
  releaseName: DEFAULT_RELEASE_NAME,
@@ -5316,15 +6765,67 @@ var GithubPR = class extends GitBase {
5316
6765
  }
5317
6766
  releaseParams;
5318
6767
  githubManager;
6768
+ /**
6769
+ * Determines if the plugin should be enabled
6770
+ *
6771
+ * Plugin is enabled unless explicitly skipped via configuration.
6772
+ * This allows for conditional PR creation and release publishing.
6773
+ *
6774
+ * @param _name - Plugin name (unused)
6775
+ * @returns True if plugin should be enabled
6776
+ *
6777
+ * @example
6778
+ * ```typescript
6779
+ * const plugin = new GithubPR(context, { skip: true });
6780
+ * plugin.enabled(); // false
6781
+ *
6782
+ * const plugin2 = new GithubPR(context, {});
6783
+ * plugin2.enabled(); // true
6784
+ * ```
6785
+ */
5319
6786
  enabled(_name) {
5320
6787
  if (this.getConfig("skip")) {
5321
6788
  return false;
5322
6789
  }
5323
6790
  return true;
5324
6791
  }
6792
+ /**
6793
+ * Determines if the plugin is in publish mode
6794
+ *
6795
+ * In publish mode, the plugin publishes releases directly.
6796
+ * In non-publish mode (releasePR=true), it creates pull requests.
6797
+ *
6798
+ * @returns True if in publish mode
6799
+ *
6800
+ * @example
6801
+ * ```typescript
6802
+ * const plugin = new GithubPR(context, { releasePR: true });
6803
+ * plugin.isPublish; // false (PR mode)
6804
+ *
6805
+ * const plugin2 = new GithubPR(context, { releasePR: false });
6806
+ * plugin2.isPublish; // true (publish mode)
6807
+ * ```
6808
+ */
5325
6809
  get isPublish() {
5326
6810
  return !this.getConfig("releasePR");
5327
6811
  }
6812
+ /**
6813
+ * Checks if the current repository is a GitHub repository
6814
+ *
6815
+ * Verifies that the remote URL contains 'github.com' to ensure
6816
+ * GitHub-specific features can be used.
6817
+ *
6818
+ * @returns Promise resolving to true if GitHub repository
6819
+ *
6820
+ * @example
6821
+ * ```typescript
6822
+ * const isGithub = await plugin.isGithubRepository();
6823
+ * if (isGithub) {
6824
+ * // Use GitHub-specific features
6825
+ * }
6826
+ * ```
6827
+ * @private
6828
+ */
5328
6829
  async isGithubRepository() {
5329
6830
  try {
5330
6831
  const remoteUrl = await this.getRemoteUrl();
@@ -5333,6 +6834,24 @@ var GithubPR = class extends GitBase {
5333
6834
  return false;
5334
6835
  }
5335
6836
  }
6837
+ /**
6838
+ * Plugin initialization hook
6839
+ *
6840
+ * Performs pre-execution setup:
6841
+ * 1. Verifies repository is on GitHub
6842
+ * 2. Runs parent class initialization
6843
+ * 3. Sets up NPM token for publishing
6844
+ *
6845
+ * @throws Error if not a GitHub repository
6846
+ * @throws Error if NPM_TOKEN missing in publish mode
6847
+ *
6848
+ * @example
6849
+ * ```typescript
6850
+ * const plugin = new GithubPR(context, {});
6851
+ * await plugin.onBefore();
6852
+ * // Throws if not GitHub repo or missing NPM token
6853
+ * ```
6854
+ */
5336
6855
  async onBefore() {
5337
6856
  this.logger.debug("GithubPR onBefore");
5338
6857
  const isGithub = await this.isGithubRepository();
@@ -5352,6 +6871,24 @@ var GithubPR = class extends GitBase {
5352
6871
  );
5353
6872
  }
5354
6873
  }
6874
+ /**
6875
+ * Main plugin execution hook
6876
+ *
6877
+ * Processes changelogs for all workspaces using GitHub-specific
6878
+ * formatting and updates the context with the results.
6879
+ *
6880
+ * Process:
6881
+ * 1. Initialize GitHub changelog processor
6882
+ * 2. Transform workspace changelogs
6883
+ * 3. Update context with new workspace info
6884
+ *
6885
+ * @example
6886
+ * ```typescript
6887
+ * const plugin = new GithubPR(context, {});
6888
+ * await plugin.onExec();
6889
+ * // Transforms changelogs with GitHub links
6890
+ * ```
6891
+ */
5355
6892
  async onExec() {
5356
6893
  const workspaces = this.context.workspaces;
5357
6894
  const githubChangelog = new GithubChangelog(
@@ -5364,6 +6901,27 @@ var GithubPR = class extends GitBase {
5364
6901
  });
5365
6902
  this.context.setWorkspaces(newWorkspaces);
5366
6903
  }
6904
+ /**
6905
+ * Success hook after plugin execution
6906
+ *
6907
+ * Handles either PR creation or release publishing based on
6908
+ * configuration. In publish mode, publishes to NPM and creates
6909
+ * GitHub releases. In PR mode, creates release pull requests.
6910
+ *
6911
+ * @example PR mode
6912
+ * ```typescript
6913
+ * const plugin = new GithubPR(context, { releasePR: true });
6914
+ * await plugin.onSuccess();
6915
+ * // Creates release PR
6916
+ * ```
6917
+ *
6918
+ * @example Publish mode
6919
+ * ```typescript
6920
+ * const plugin = new GithubPR(context, { releasePR: false });
6921
+ * await plugin.onSuccess();
6922
+ * // Publishes to NPM and creates GitHub release
6923
+ * ```
6924
+ */
5367
6925
  async onSuccess() {
5368
6926
  if (this.isPublish) {
5369
6927
  await this.publishPR(this.context.workspaces);
@@ -5371,6 +6929,28 @@ var GithubPR = class extends GitBase {
5371
6929
  }
5372
6930
  await this.releasePR(this.context.workspaces);
5373
6931
  }
6932
+ /**
6933
+ * Creates a release pull request
6934
+ *
6935
+ * Handles the complete process of creating a release PR:
6936
+ * 1. Creates release commit
6937
+ * 2. Creates release branch
6938
+ * 3. Creates and configures pull request
6939
+ *
6940
+ * @param workspaces - Array of workspace configurations
6941
+ *
6942
+ * @example
6943
+ * ```typescript
6944
+ * const workspaces = [{
6945
+ * name: 'pkg-a',
6946
+ * version: '1.0.0',
6947
+ * changelog: '...'
6948
+ * }];
6949
+ *
6950
+ * await plugin.releasePR(workspaces);
6951
+ * // Creates PR with release changes
6952
+ * ```
6953
+ */
5374
6954
  async releasePR(workspaces) {
5375
6955
  await this.step({
5376
6956
  label: "Release Commit",
@@ -5382,6 +6962,28 @@ var GithubPR = class extends GitBase {
5382
6962
  });
5383
6963
  await this.releasePullRequest(workspaces, releaseBranchParams);
5384
6964
  }
6965
+ /**
6966
+ * Publishes releases to NPM and GitHub
6967
+ *
6968
+ * In non-dry-run mode:
6969
+ * 1. Publishes packages to NPM
6970
+ * 2. Pushes tags to GitHub
6971
+ * 3. Creates GitHub releases
6972
+ *
6973
+ * @param workspaces - Array of workspace configurations
6974
+ *
6975
+ * @example
6976
+ * ```typescript
6977
+ * const workspaces = [{
6978
+ * name: 'pkg-a',
6979
+ * version: '1.0.0',
6980
+ * changelog: '...'
6981
+ * }];
6982
+ *
6983
+ * await plugin.publishPR(workspaces);
6984
+ * // Publishes to NPM and creates GitHub releases
6985
+ * ```
6986
+ */
5385
6987
  async publishPR(workspaces) {
5386
6988
  if (!this.getConfig("dryRunCreatePR")) {
5387
6989
  await this.context.runChangesetsCli("publish");
@@ -5397,6 +6999,34 @@ var GithubPR = class extends GitBase {
5397
6999
  )
5398
7000
  });
5399
7001
  }
7002
+ /**
7003
+ * Creates release commit(s)
7004
+ *
7005
+ * Creates either a single commit for all workspaces or
7006
+ * individual commits per workspace. Uses configured commit
7007
+ * message template.
7008
+ *
7009
+ * @param workspaces - Array of workspace configurations
7010
+ *
7011
+ * @example Single workspace
7012
+ * ```typescript
7013
+ * await plugin.relesaeCommit([{
7014
+ * name: 'pkg-a',
7015
+ * version: '1.0.0'
7016
+ * }]);
7017
+ * // Creates: "chore(tag): pkg-a v1.0.0"
7018
+ * ```
7019
+ *
7020
+ * @example Multiple workspaces
7021
+ * ```typescript
7022
+ * await plugin.relesaeCommit([
7023
+ * { name: 'pkg-a', version: '1.0.0' },
7024
+ * { name: 'pkg-b', version: '2.0.0' }
7025
+ * ]);
7026
+ * // Creates: "chore(tag): pkg-a v1.0.0,pkg-b v2.0.0"
7027
+ * ```
7028
+ * @private
7029
+ */
5400
7030
  async relesaeCommit(workspaces) {
5401
7031
  const commitArgs = this.getConfig("commitArgs", []);
5402
7032
  if (workspaces.length === 1) {
@@ -5408,6 +7038,34 @@ var GithubPR = class extends GitBase {
5408
7038
  const commitMessage = `chore(tag): ${workspaces.map((w) => `${w.name} v${w.version}`).join(",")}`;
5409
7039
  await this.commit(commitMessage, commitArgs);
5410
7040
  }
7041
+ /**
7042
+ * Creates and optionally merges a release pull request
7043
+ *
7044
+ * Creates a PR with release changes and handles auto-merge
7045
+ * if configured. Adds release and change labels to the PR.
7046
+ *
7047
+ * @param workspaces - Array of workspace configurations
7048
+ * @param releaseBranchParams - Branch and tag information
7049
+ *
7050
+ * @example Manual merge
7051
+ * ```typescript
7052
+ * await plugin.releasePullRequest(
7053
+ * workspaces,
7054
+ * { releaseBranch: 'release-v1.0.0', tagName: 'v1.0.0' }
7055
+ * );
7056
+ * // Creates PR for manual merge
7057
+ * ```
7058
+ *
7059
+ * @example Auto-merge
7060
+ * ```typescript
7061
+ * const plugin = new GithubPR(context, {
7062
+ * autoMergeReleasePR: true
7063
+ * });
7064
+ *
7065
+ * await plugin.releasePullRequest(workspaces, params);
7066
+ * // Creates and auto-merges PR
7067
+ * ```
7068
+ */
5411
7069
  async releasePullRequest(workspaces, releaseBranchParams) {
5412
7070
  const prNumber = await this.step({
5413
7071
  label: "Create Release PR",
@@ -5429,6 +7087,34 @@ var GithubPR = class extends GitBase {
5429
7087
  `Please manually merge PR(#${prNumber}) and complete the publishing process afterwards`
5430
7088
  );
5431
7089
  }
7090
+ /**
7091
+ * Creates a commit for a single workspace
7092
+ *
7093
+ * Uses the configured commit message template to create a
7094
+ * commit for the workspace's changes.
7095
+ *
7096
+ * @param workspace - Workspace configuration
7097
+ * @param commitArgs - Additional Git commit arguments
7098
+ * @returns Promise resolving to commit output
7099
+ *
7100
+ * @example Basic commit
7101
+ * ```typescript
7102
+ * await plugin.commitWorkspace({
7103
+ * name: 'pkg-a',
7104
+ * version: '1.0.0'
7105
+ * });
7106
+ * // Creates: "chore(tag): pkg-a v1.0.0"
7107
+ * ```
7108
+ *
7109
+ * @example With arguments
7110
+ * ```typescript
7111
+ * await plugin.commitWorkspace(
7112
+ * { name: 'pkg-a', version: '1.0.0' },
7113
+ * ['--no-verify']
7114
+ * );
7115
+ * ```
7116
+ * @private
7117
+ */
5432
7118
  async commitWorkspace(workspace, commitArgs = []) {
5433
7119
  const commitMessage = Shell4.format(
5434
7120
  this.getConfig("commitMessage", DEFAULT_COMMIT_MESSAGE),
@@ -5446,6 +7132,37 @@ var GithubPR = class extends GitBase {
5446
7132
  *
5447
7133
  * @returns The release branch.
5448
7134
  */
7135
+ /**
7136
+ * Creates a release branch for changes
7137
+ *
7138
+ * Creates a new branch from the current branch for release
7139
+ * changes. The branch name is generated from the configured
7140
+ * template and workspace information.
7141
+ *
7142
+ * Process:
7143
+ * 1. Generate branch parameters
7144
+ * 2. Fetch required branches
7145
+ * 3. Create and push release branch
7146
+ *
7147
+ * @param workspaces - Array of workspace configurations
7148
+ * @returns Promise resolving to branch parameters
7149
+ *
7150
+ * @example
7151
+ * ```typescript
7152
+ * const params = await plugin.createReleaseBranch([{
7153
+ * name: 'pkg-a',
7154
+ * version: '1.0.0'
7155
+ * }]);
7156
+ * // {
7157
+ * // tagName: 'pkg-a@1.0.0',
7158
+ * // releaseBranch: 'release-pkg-a-1.0.0'
7159
+ * // }
7160
+ * ```
7161
+ *
7162
+ * @throws Error if tag name is invalid
7163
+ * @throws Error if branch creation fails
7164
+ * @private
7165
+ */
5449
7166
  async createReleaseBranch(workspaces) {
5450
7167
  const params = this.releaseParams.getReleaseBranchParams(
5451
7168
  workspaces,
@@ -5488,6 +7205,42 @@ var GithubPR = class extends GitBase {
5488
7205
  * @param releaseBranchParams - The release branch params.
5489
7206
  * @returns The created pull request number.
5490
7207
  */
7208
+ /**
7209
+ * Creates a release pull request
7210
+ *
7211
+ * Creates a pull request with:
7212
+ * 1. Release label
7213
+ * 2. Change labels (if configured)
7214
+ * 3. Generated title and body
7215
+ * 4. Proper branch configuration
7216
+ *
7217
+ * @param workspaces - Array of workspace configurations
7218
+ * @param releaseBranchParams - Branch and tag information
7219
+ * @returns Promise resolving to PR number
7220
+ *
7221
+ * @example Basic PR
7222
+ * ```typescript
7223
+ * const prNumber = await plugin.createReleasePR(
7224
+ * workspaces,
7225
+ * { releaseBranch: 'release-v1.0.0', tagName: 'v1.0.0' }
7226
+ * );
7227
+ * // Creates PR with default labels
7228
+ * ```
7229
+ *
7230
+ * @example With change labels
7231
+ * ```typescript
7232
+ * const plugin = new GithubPR(context, {
7233
+ * pushChangeLabels: true
7234
+ * });
7235
+ *
7236
+ * const prNumber = await plugin.createReleasePR(
7237
+ * workspaces,
7238
+ * params
7239
+ * );
7240
+ * // Creates PR with release and change labels
7241
+ * ```
7242
+ * @private
7243
+ */
5491
7244
  async createReleasePR(workspaces, releaseBranchParams) {
5492
7245
  const label = await this.githubManager.createReleasePRLabel();
5493
7246
  let labels = [label.name];
@@ -5521,21 +7274,137 @@ import { join as join2, resolve, relative } from "path";
5521
7274
 
5522
7275
  // src/implments/ReleaseLabel.ts
5523
7276
  var ReleaseLabel = class {
7277
+ /**
7278
+ * Creates a new ReleaseLabel instance
7279
+ *
7280
+ * @param options - Configuration options for label management
7281
+ *
7282
+ * @example
7283
+ * ```typescript
7284
+ * const label = new ReleaseLabel({
7285
+ * // Label template with ${name} placeholder
7286
+ * changePackagesLabel: 'changed:${name}',
7287
+ *
7288
+ * // Package directories to monitor
7289
+ * packagesDirectories: ['packages/a', 'packages/b'],
7290
+ *
7291
+ * // Optional custom comparison logic
7292
+ * compare: (file, pkg) => file.includes(pkg)
7293
+ * });
7294
+ * ```
7295
+ */
5524
7296
  constructor(options) {
5525
7297
  this.options = options;
5526
7298
  }
7299
+ /**
7300
+ * Compares a changed file path against a package path
7301
+ *
7302
+ * Uses custom comparison function if provided, otherwise
7303
+ * checks if the file path starts with the package path.
7304
+ *
7305
+ * @param changedFilePath - Path of the changed file
7306
+ * @param packagePath - Path of the package to check against
7307
+ * @returns True if the file belongs to the package
7308
+ *
7309
+ * @example
7310
+ * ```typescript
7311
+ * // Default comparison
7312
+ * label.compare('packages/a/src/index.ts', 'packages/a');
7313
+ * // true
7314
+ *
7315
+ * // Custom comparison
7316
+ * const label = new ReleaseLabel({
7317
+ * ...options,
7318
+ * compare: (file, pkg) => file.includes(pkg)
7319
+ * });
7320
+ * label.compare('src/packages/a/index.ts', 'packages/a');
7321
+ * // true
7322
+ * ```
7323
+ */
5527
7324
  compare(changedFilePath, packagePath) {
5528
7325
  if (typeof this.options.compare === "function") {
5529
7326
  return this.options.compare(changedFilePath, packagePath);
5530
7327
  }
5531
7328
  return changedFilePath.startsWith(packagePath);
5532
7329
  }
7330
+ /**
7331
+ * Generates a change label for a single package
7332
+ *
7333
+ * Replaces ${name} placeholder in the label template with
7334
+ * the package path.
7335
+ *
7336
+ * @param packagePath - Path of the package
7337
+ * @param label - Optional custom label template
7338
+ * @returns Formatted change label
7339
+ *
7340
+ * @example
7341
+ * ```typescript
7342
+ * // Default label template
7343
+ * label.toChangeLabel('packages/a');
7344
+ * // 'changed:packages/a'
7345
+ *
7346
+ * // Custom label template
7347
+ * label.toChangeLabel('packages/a', 'modified:${name}');
7348
+ * // 'modified:packages/a'
7349
+ * ```
7350
+ */
5533
7351
  toChangeLabel(packagePath, label = this.options.changePackagesLabel) {
5534
7352
  return label.replace("${name}", packagePath);
5535
7353
  }
7354
+ /**
7355
+ * Generates change labels for multiple packages
7356
+ *
7357
+ * Maps each package path to a formatted change label.
7358
+ *
7359
+ * @param packages - Array of package paths
7360
+ * @param label - Optional custom label template
7361
+ * @returns Array of formatted change labels
7362
+ *
7363
+ * @example
7364
+ * ```typescript
7365
+ * // Default label template
7366
+ * label.toChangeLabels(['packages/a', 'packages/b']);
7367
+ * // ['changed:packages/a', 'changed:packages/b']
7368
+ *
7369
+ * // Custom label template
7370
+ * label.toChangeLabels(
7371
+ * ['packages/a', 'packages/b'],
7372
+ * 'modified:${name}'
7373
+ * );
7374
+ * // ['modified:packages/a', 'modified:packages/b']
7375
+ * ```
7376
+ */
5536
7377
  toChangeLabels(packages, label = this.options.changePackagesLabel) {
5537
7378
  return packages.map((pkg) => this.toChangeLabel(pkg, label));
5538
7379
  }
7380
+ /**
7381
+ * Identifies packages affected by changed files
7382
+ *
7383
+ * Checks each changed file against package paths to determine
7384
+ * which packages have been modified.
7385
+ *
7386
+ * @param changedFiles - Array or Set of changed file paths
7387
+ * @param packages - Optional array of package paths to check
7388
+ * @returns Array of affected package paths
7389
+ *
7390
+ * @example
7391
+ * ```typescript
7392
+ * // Check against default packages
7393
+ * label.pick(['packages/a/src/index.ts']);
7394
+ * // ['packages/a']
7395
+ *
7396
+ * // Check specific packages
7397
+ * label.pick(
7398
+ * ['packages/a/index.ts', 'packages/b/test.ts'],
7399
+ * ['packages/a', 'packages/c']
7400
+ * );
7401
+ * // ['packages/a']
7402
+ *
7403
+ * // Using Set of files
7404
+ * label.pick(new Set(['packages/a/index.ts']));
7405
+ * // ['packages/a']
7406
+ * ```
7407
+ */
5539
7408
  pick(changedFiles, packages = this.options.packagesDirectories) {
5540
7409
  const result = [];
5541
7410
  for (const pkgPath of packages) {
@@ -5904,6 +7773,31 @@ import {
5904
7773
  } from "@qlover/scripts-context";
5905
7774
  var contentTmplate = "---\n'${name}': '${increment}'\n---\n\n${changelog}";
5906
7775
  var Changelog = class extends ScriptPlugin3 {
7776
+ /**
7777
+ * Creates a new Changelog plugin instance
7778
+ *
7779
+ * Initializes the plugin with default configuration values and
7780
+ * merges them with provided options.
7781
+ *
7782
+ * Default values:
7783
+ * - increment: 'patch'
7784
+ * - changesetRoot: '.changeset'
7785
+ * - tagTemplate: '${name}@${version}'
7786
+ * - tagPrefix: '${name}'
7787
+ * - tagMatch: '${name}@*'
7788
+ *
7789
+ * @param context - Release context
7790
+ * @param props - Plugin configuration
7791
+ *
7792
+ * @example
7793
+ * ```typescript
7794
+ * const plugin = new Changelog(context, {
7795
+ * increment: 'minor',
7796
+ * changesetRoot: 'custom/changeset',
7797
+ * tagTemplate: 'v${version}'
7798
+ * });
7799
+ * ```
7800
+ */
5907
7801
  constructor(context, props) {
5908
7802
  super(context, "changelog", {
5909
7803
  increment: "patch",
@@ -5914,15 +7808,78 @@ var Changelog = class extends ScriptPlugin3 {
5914
7808
  ...props
5915
7809
  });
5916
7810
  }
7811
+ /**
7812
+ * Gets the absolute path to the changeset root directory
7813
+ *
7814
+ * Combines the project root path with the configured changeset
7815
+ * directory path.
7816
+ *
7817
+ * @returns Absolute path to changeset directory
7818
+ *
7819
+ * @example
7820
+ * ```typescript
7821
+ * const root = plugin.changesetRoot;
7822
+ * // '/path/to/project/.changeset'
7823
+ * ```
7824
+ */
5917
7825
  get changesetRoot() {
5918
7826
  return join4(this.context.rootPath, this.getConfig("changesetRoot"));
5919
7827
  }
7828
+ /**
7829
+ * Gets the path to the changeset configuration file
7830
+ *
7831
+ * Returns the absolute path to the config.json file in the
7832
+ * changeset directory.
7833
+ *
7834
+ * @returns Path to changeset config file
7835
+ *
7836
+ * @example
7837
+ * ```typescript
7838
+ * const configPath = plugin.changesetConfigPath;
7839
+ * // '/path/to/project/.changeset/config.json'
7840
+ * ```
7841
+ */
5920
7842
  get changesetConfigPath() {
5921
7843
  return join4(this.changesetRoot, "config.json");
5922
7844
  }
7845
+ /**
7846
+ * Determines if the plugin should be enabled
7847
+ *
7848
+ * Plugin is enabled unless explicitly skipped via configuration.
7849
+ * This allows for conditional changelog generation.
7850
+ *
7851
+ * @returns True if plugin should be enabled
7852
+ *
7853
+ * @example
7854
+ * ```typescript
7855
+ * const plugin = new Changelog(context, { skip: true });
7856
+ * plugin.enabled(); // false
7857
+ *
7858
+ * const plugin2 = new Changelog(context, {});
7859
+ * plugin2.enabled(); // true
7860
+ * ```
7861
+ */
5923
7862
  enabled() {
5924
7863
  return !this.getConfig("skip");
5925
7864
  }
7865
+ /**
7866
+ * Plugin initialization hook
7867
+ *
7868
+ * Verifies that the changeset directory exists before proceeding
7869
+ * with changelog generation.
7870
+ *
7871
+ * @throws Error if changeset directory does not exist
7872
+ *
7873
+ * @example
7874
+ * ```typescript
7875
+ * const plugin = new Changelog(context, {
7876
+ * changesetRoot: '.changeset'
7877
+ * });
7878
+ *
7879
+ * await plugin.onBefore();
7880
+ * // Throws if .changeset directory doesn't exist
7881
+ * ```
7882
+ */
5926
7883
  async onBefore() {
5927
7884
  if (!existsSync(this.changesetRoot)) {
5928
7885
  throw new Error(
@@ -5931,6 +7888,33 @@ var Changelog = class extends ScriptPlugin3 {
5931
7888
  }
5932
7889
  this.logger.debug(`${this.changesetRoot} exists`);
5933
7890
  }
7891
+ /**
7892
+ * Updates workspace information with latest versions
7893
+ *
7894
+ * Reads the latest version information from each workspace's
7895
+ * package.json and updates the workspace objects with new
7896
+ * versions and tag names.
7897
+ *
7898
+ * @param workspaces - Array of workspace configurations
7899
+ * @returns Updated workspace configurations
7900
+ *
7901
+ * @example
7902
+ * ```typescript
7903
+ * const workspaces = [
7904
+ * { name: 'pkg-a', path: 'packages/a', version: '1.0.0' }
7905
+ * ];
7906
+ *
7907
+ * const updated = plugin.mergeWorkspaces(workspaces);
7908
+ * // [
7909
+ * // {
7910
+ * // name: 'pkg-a',
7911
+ * // path: 'packages/a',
7912
+ * // version: '1.1.0', // Updated version
7913
+ * // tagName: 'pkg-a@1.1.0'
7914
+ * // }
7915
+ * // ]
7916
+ * ```
7917
+ */
5934
7918
  mergeWorkspaces(workspaces) {
5935
7919
  return workspaces.map((workspace) => {
5936
7920
  const newPackgeJson = WorkspaceCreator.toWorkspace(
@@ -5947,6 +7931,25 @@ var Changelog = class extends ScriptPlugin3 {
5947
7931
  return newWorkspace;
5948
7932
  });
5949
7933
  }
7934
+ /**
7935
+ * Main plugin execution hook
7936
+ *
7937
+ * Generates changelogs for all workspaces in parallel and updates
7938
+ * the context with the results.
7939
+ *
7940
+ * Process:
7941
+ * 1. Generate changelogs for each workspace
7942
+ * 2. Update context with new workspace information
7943
+ *
7944
+ * @param _context - Execution context
7945
+ *
7946
+ * @example
7947
+ * ```typescript
7948
+ * const plugin = new Changelog(context, {});
7949
+ * await plugin.onExec(execContext);
7950
+ * // Generates changelogs for all workspaces
7951
+ * ```
7952
+ */
5950
7953
  async onExec(_context) {
5951
7954
  const workspaces = await this.step({
5952
7955
  label: "Generate Changelogs",
@@ -5958,6 +7961,28 @@ var Changelog = class extends ScriptPlugin3 {
5958
7961
  });
5959
7962
  this.context.setWorkspaces(workspaces);
5960
7963
  }
7964
+ /**
7965
+ * Success hook after plugin execution
7966
+ *
7967
+ * Handles post-changelog generation tasks:
7968
+ * 1. Creates changeset files (if not skipped)
7969
+ * 2. Updates package versions
7970
+ * 3. Restores unchanged packages (if configured)
7971
+ * 4. Updates workspace information
7972
+ *
7973
+ * @example
7974
+ * ```typescript
7975
+ * const plugin = new Changelog(context, {
7976
+ * skipChangeset: false,
7977
+ * ignoreNonUpdatedPackages: true
7978
+ * });
7979
+ *
7980
+ * await plugin.onSuccess();
7981
+ * // - Creates changeset files
7982
+ * // - Updates versions
7983
+ * // - Restores unchanged packages
7984
+ * ```
7985
+ */
5961
7986
  async onSuccess() {
5962
7987
  const workspaces = this.context.workspaces;
5963
7988
  if (!this.getConfig("skipChangeset")) {
@@ -5981,6 +8006,25 @@ var Changelog = class extends ScriptPlugin3 {
5981
8006
  this.logger.debug("new workspaces", newWorkspaces);
5982
8007
  this.context.setWorkspaces(newWorkspaces);
5983
8008
  }
8009
+ /**
8010
+ * Restores unchanged packages to their original state
8011
+ *
8012
+ * When ignoreNonUpdatedPackages is enabled, this method:
8013
+ * 1. Identifies packages without changes
8014
+ * 2. Uses git restore to revert them to original state
8015
+ *
8016
+ * @example
8017
+ * ```typescript
8018
+ * // With changed and unchanged packages
8019
+ * context.options.workspaces = {
8020
+ * packages: ['pkg-a', 'pkg-b', 'pkg-c'],
8021
+ * changedPaths: ['pkg-a', 'pkg-b']
8022
+ * };
8023
+ *
8024
+ * await plugin.restoreIgnorePackages();
8025
+ * // Restores 'pkg-c' to original state
8026
+ * ```
8027
+ */
5984
8028
  async restoreIgnorePackages() {
5985
8029
  const { changedPaths = [], packages = [] } = this.context.getOptions(
5986
8030
  "workspaces"
@@ -5993,12 +8037,60 @@ var Changelog = class extends ScriptPlugin3 {
5993
8037
  await this.shell.exec(["git", "restore", ...noChangedPackages]);
5994
8038
  }
5995
8039
  }
8040
+ /**
8041
+ * Gets the tag prefix for a workspace
8042
+ *
8043
+ * Formats the configured tag prefix template with workspace
8044
+ * information. Used for generating Git tag names.
8045
+ *
8046
+ * @param workspace - Workspace configuration
8047
+ * @returns Formatted tag prefix
8048
+ *
8049
+ * @example
8050
+ * ```typescript
8051
+ * const workspace = {
8052
+ * name: 'pkg-a',
8053
+ * version: '1.0.0'
8054
+ * };
8055
+ *
8056
+ * const prefix = plugin.getTagPrefix(workspace);
8057
+ * // With default template: 'pkg-a'
8058
+ * // With custom template: 'v1.0.0'
8059
+ * ```
8060
+ */
5996
8061
  getTagPrefix(workspace) {
5997
8062
  return Shell5.format(
5998
8063
  this.getConfig("tagPrefix"),
5999
8064
  workspace
6000
8065
  );
6001
8066
  }
8067
+ /**
8068
+ * Generates a changelog for a workspace
8069
+ *
8070
+ * Creates a changelog by:
8071
+ * 1. Getting the appropriate tag name
8072
+ * 2. Retrieving commits since last tag
8073
+ * 3. Formatting commits into changelog entries
8074
+ *
8075
+ * @param workspace - Workspace configuration
8076
+ * @returns Updated workspace with changelog
8077
+ *
8078
+ * @example
8079
+ * ```typescript
8080
+ * const workspace = {
8081
+ * name: 'pkg-a',
8082
+ * path: 'packages/a',
8083
+ * version: '1.0.0'
8084
+ * };
8085
+ *
8086
+ * const updated = await plugin.generateChangelog(workspace);
8087
+ * // {
8088
+ * // ...workspace,
8089
+ * // lastTag: 'pkg-a@1.0.0',
8090
+ * // changelog: '- feat: new feature\n- fix: bug fix'
8091
+ * // }
8092
+ * ```
8093
+ */
6002
8094
  async generateChangelog(workspace) {
6003
8095
  let tagName = await this.getTagName(workspace);
6004
8096
  if (workspace.lastTag) {
@@ -6024,6 +8116,32 @@ var Changelog = class extends ScriptPlugin3 {
6024
8116
  changelog: changelog.join("\n")
6025
8117
  };
6026
8118
  }
8119
+ /**
8120
+ * Generates a tag name for a workspace
8121
+ *
8122
+ * Uses the configured tag template to generate a tag name
8123
+ * for the workspace. Handles errors by providing a fallback.
8124
+ *
8125
+ * @param workspace - Workspace configuration
8126
+ * @returns Generated tag name
8127
+ *
8128
+ * @example
8129
+ * ```typescript
8130
+ * // With default template
8131
+ * const tag = plugin.generateTagName({
8132
+ * name: 'pkg-a',
8133
+ * version: '1.0.0'
8134
+ * });
8135
+ * // 'pkg-a@1.0.0'
8136
+ *
8137
+ * // With error (fallback)
8138
+ * const tag = plugin.generateTagName({
8139
+ * name: 'pkg-a'
8140
+ * });
8141
+ * // 'pkg-a-v0.0.0'
8142
+ * ```
8143
+ * @private
8144
+ */
6027
8145
  generateTagName(workspace) {
6028
8146
  try {
6029
8147
  const tagTemplate = this.getConfig("tagTemplate");
@@ -6036,6 +8154,38 @@ var Changelog = class extends ScriptPlugin3 {
6036
8154
  return `${workspace.name}-v0.0.0`;
6037
8155
  }
6038
8156
  }
8157
+ /**
8158
+ * Gets the appropriate tag name for a workspace
8159
+ *
8160
+ * Attempts to find the latest tag for the workspace, falling back
8161
+ * to generating a new tag if none exists. Uses git commands to
8162
+ * find and sort tags by creation date.
8163
+ *
8164
+ * Process:
8165
+ * 1. Generate current tag pattern
8166
+ * 2. Search for existing tags matching pattern
8167
+ * 3. Return latest tag or generate new one
8168
+ *
8169
+ * @param workspace - Workspace configuration
8170
+ * @returns Promise resolving to tag name
8171
+ *
8172
+ * @example
8173
+ * ```typescript
8174
+ * // With existing tags
8175
+ * const tag = await plugin.getTagName({
8176
+ * name: 'pkg-a',
8177
+ * version: '1.0.0'
8178
+ * });
8179
+ * // Returns latest matching tag: 'pkg-a@0.9.0'
8180
+ *
8181
+ * // Without existing tags
8182
+ * const tag = await plugin.getTagName({
8183
+ * name: 'pkg-b',
8184
+ * version: '1.0.0'
8185
+ * });
8186
+ * // Returns new tag: 'pkg-b@1.0.0'
8187
+ * ```
8188
+ */
6039
8189
  async getTagName(workspace) {
6040
8190
  try {
6041
8191
  const currentTagPattern = this.generateTagName(workspace);
@@ -6062,6 +8212,31 @@ var Changelog = class extends ScriptPlugin3 {
6062
8212
  return fallbackTag;
6063
8213
  }
6064
8214
  }
8215
+ /**
8216
+ * Determines the version increment type
8217
+ *
8218
+ * Checks for increment labels in the following order:
8219
+ * 1. 'increment:major' label
8220
+ * 2. 'increment:minor' label
8221
+ * 3. Configured increment value
8222
+ * 4. Default to 'patch'
8223
+ *
8224
+ * @returns Version increment type
8225
+ *
8226
+ * @example
8227
+ * ```typescript
8228
+ * // With labels
8229
+ * context.options.workspaces.changeLabels = ['increment:major'];
8230
+ * plugin.getIncrement(); // 'major'
8231
+ *
8232
+ * // With configuration
8233
+ * const plugin = new Changelog(context, { increment: 'minor' });
8234
+ * plugin.getIncrement(); // 'minor'
8235
+ *
8236
+ * // Default
8237
+ * plugin.getIncrement(); // 'patch'
8238
+ * ```
8239
+ */
6065
8240
  getIncrement() {
6066
8241
  const lables = this.context.getOptions("workspaces.changeLabels");
6067
8242
  if (Array.isArray(lables) && lables.length > 0) {
@@ -6075,6 +8250,43 @@ var Changelog = class extends ScriptPlugin3 {
6075
8250
  const increment = this.getConfig("increment", "patch");
6076
8251
  return increment;
6077
8252
  }
8253
+ /**
8254
+ * Generates a changeset file for a workspace
8255
+ *
8256
+ * Creates a changeset file containing version increment
8257
+ * information and changelog content. Handles dry run mode
8258
+ * and existing files.
8259
+ *
8260
+ * File format:
8261
+ * ```yaml
8262
+ * ---
8263
+ * 'package-name': 'increment-type'
8264
+ * ---
8265
+ *
8266
+ * changelog content
8267
+ * ```
8268
+ *
8269
+ * @param workspace - Workspace configuration
8270
+ *
8271
+ * @example
8272
+ * ```typescript
8273
+ * const workspace = {
8274
+ * name: 'pkg-a',
8275
+ * version: '1.0.0',
8276
+ * changelog: '- feat: new feature'
8277
+ * };
8278
+ *
8279
+ * await plugin.generateChangesetFile(workspace);
8280
+ * // Creates .changeset/pkg-a-1.0.0.md
8281
+ * ```
8282
+ *
8283
+ * @example Dry run
8284
+ * ```typescript
8285
+ * context.dryRun = true;
8286
+ * await plugin.generateChangesetFile(workspace);
8287
+ * // Logs file content without creating file
8288
+ * ```
8289
+ */
6078
8290
  async generateChangesetFile(workspace) {
6079
8291
  const { name, version: version2 } = workspace;
6080
8292
  const changesetName = `${name}-${version2}`.replace(/[\/\\]/g, "_");
@@ -6108,15 +8320,89 @@ var innerTuples = [
6108
8320
  ];
6109
8321
  var defaultName = "release";
6110
8322
  var ReleaseTask = class {
8323
+ /**
8324
+ * Creates a new ReleaseTask instance
8325
+ *
8326
+ * Initializes the release context and sets up plugin configuration.
8327
+ * Supports custom executors and plugin configurations.
8328
+ *
8329
+ * @param options - Release context configuration
8330
+ * @param executor - Custom async executor (optional)
8331
+ * @param defaultTuples - Plugin configuration tuples (optional)
8332
+ *
8333
+ * @example
8334
+ * ```typescript
8335
+ * // Basic initialization
8336
+ * const task = new ReleaseTask({
8337
+ * rootPath: '/path/to/project',
8338
+ * sourceBranch: 'main'
8339
+ * });
8340
+ *
8341
+ * // With custom executor and plugins
8342
+ * const task = new ReleaseTask(
8343
+ * { rootPath: '/path/to/project' },
8344
+ * new AsyncExecutor(),
8345
+ * [tuple(CustomPlugin, { option: 'value' })]
8346
+ * );
8347
+ * ```
8348
+ */
6111
8349
  constructor(options = {}, executor = new AsyncExecutor(), defaultTuples = innerTuples) {
6112
8350
  this.executor = executor;
6113
8351
  this.defaultTuples = defaultTuples;
6114
8352
  this.context = new ReleaseContext(defaultName, options);
6115
8353
  }
8354
+ /**
8355
+ * Release context instance
8356
+ * @protected
8357
+ */
6116
8358
  context;
8359
+ /**
8360
+ * Gets the current release context
8361
+ *
8362
+ * @returns Release context instance
8363
+ *
8364
+ * @example
8365
+ * ```typescript
8366
+ * const task = new ReleaseTask();
8367
+ * const context = task.getContext();
8368
+ *
8369
+ * console.log(context.releaseEnv);
8370
+ * console.log(context.sourceBranch);
8371
+ * ```
8372
+ */
6117
8373
  getContext() {
6118
8374
  return this.context;
6119
8375
  }
8376
+ /**
8377
+ * Loads and configures plugins for the release task
8378
+ *
8379
+ * Combines default and external plugins, initializes them with
8380
+ * the current context, and configures special cases like the
8381
+ * Workspaces plugin.
8382
+ *
8383
+ * Plugin Loading Process:
8384
+ * 1. Merge default and external plugins
8385
+ * 2. Initialize plugins with context
8386
+ * 3. Configure special plugins
8387
+ * 4. Add plugins to executor
8388
+ *
8389
+ * @param externalTuples - Additional plugin configurations
8390
+ * @returns Array of initialized plugins
8391
+ *
8392
+ * @example Basic usage
8393
+ * ```typescript
8394
+ * const task = new ReleaseTask();
8395
+ * const plugins = await task.usePlugins();
8396
+ * ```
8397
+ *
8398
+ * @example Custom plugins
8399
+ * ```typescript
8400
+ * const task = new ReleaseTask();
8401
+ * const plugins = await task.usePlugins([
8402
+ * tuple(CustomPlugin, { option: 'value' })
8403
+ * ]);
8404
+ * ```
8405
+ */
6120
8406
  async usePlugins(externalTuples) {
6121
8407
  externalTuples = externalTuples || this.context.options.plugins || [];
6122
8408
  const plugins = await loaderPluginsFromPluginTuples(this.context, [
@@ -6131,12 +8417,65 @@ var ReleaseTask = class {
6131
8417
  });
6132
8418
  return plugins;
6133
8419
  }
8420
+ /**
8421
+ * Executes the release task
8422
+ *
8423
+ * Internal method that runs the task through the executor.
8424
+ * Preserves the context through the execution chain.
8425
+ *
8426
+ * @returns Execution result
8427
+ * @internal
8428
+ */
6134
8429
  async run() {
6135
8430
  return this.executor.exec(
6136
8431
  this.context,
6137
8432
  (context) => Promise.resolve(context)
6138
8433
  );
6139
8434
  }
8435
+ /**
8436
+ * Main entry point for executing the release task
8437
+ *
8438
+ * Checks environment conditions, loads plugins, and executes
8439
+ * the release process. Supports additional plugin configuration
8440
+ * at execution time.
8441
+ *
8442
+ * Environment Control:
8443
+ * - Checks FE_RELEASE environment variable
8444
+ * - Skips release if FE_RELEASE=false
8445
+ *
8446
+ * @param externalTuples - Additional plugin configurations
8447
+ * @returns Execution result
8448
+ * @throws Error if release is skipped via environment variable
8449
+ *
8450
+ * @example Basic execution
8451
+ * ```typescript
8452
+ * const task = new ReleaseTask();
8453
+ * await task.exec();
8454
+ * ```
8455
+ *
8456
+ * @example With additional plugins
8457
+ * ```typescript
8458
+ * const task = new ReleaseTask();
8459
+ * await task.exec([
8460
+ * tuple(CustomPlugin, { option: 'value' })
8461
+ * ]);
8462
+ * ```
8463
+ *
8464
+ * @example Environment control
8465
+ * ```typescript
8466
+ * // Skip release
8467
+ * process.env.FE_RELEASE = 'false';
8468
+ *
8469
+ * const task = new ReleaseTask();
8470
+ * try {
8471
+ * await task.exec();
8472
+ * } catch (e) {
8473
+ * if (e.message === 'Skip Release') {
8474
+ * console.log('Release skipped via environment variable');
8475
+ * }
8476
+ * }
8477
+ * ```
8478
+ */
6140
8479
  async exec(externalTuples) {
6141
8480
  if (this.context.env.get("FE_RELEASE") === "false") {
6142
8481
  throw new Error("Skip Release");