@qlover/fe-release 3.0.0 → 3.1.0

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