@reliverse/publish 2.2.9 → 2.3.1

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.
@@ -0,0 +1,38 @@
1
+ import type { LoadedConfig } from "@reliverse/config";
2
+ export interface ReleaseOptions {
3
+ version?: string | "patch" | "minor" | "major";
4
+ tag?: string;
5
+ npm?: boolean;
6
+ github?: boolean;
7
+ dryRun?: boolean;
8
+ test?: boolean;
9
+ build?: boolean;
10
+ }
11
+ /**
12
+ * Check if git repository is clean
13
+ */
14
+ export declare function checkGitClean(dryRun: boolean): Promise<void>;
15
+ /**
16
+ * Create git tag and push
17
+ */
18
+ export declare function createGitTag(version: string, config: LoadedConfig | null, tagFormat?: string): Promise<void>;
19
+ /**
20
+ * Get GitHub repository name from git remote
21
+ */
22
+ export declare function getGitHubRepo(): Promise<string>;
23
+ /**
24
+ * Create GitHub release using gh CLI
25
+ */
26
+ export declare function createGitHubRelease(version: string, config: LoadedConfig | null): Promise<void>;
27
+ /**
28
+ * Run tests
29
+ */
30
+ export declare function runTests(): Promise<void>;
31
+ /**
32
+ * Build project
33
+ */
34
+ export declare function buildProject(): Promise<void>;
35
+ /**
36
+ * Simple version bump (for release command compatibility)
37
+ */
38
+ export declare function bumpVersionSimple(version: string, type: "patch" | "minor" | "major"): string;
@@ -0,0 +1,62 @@
1
+ import { logger } from "@reliverse/relinka";
2
+ import { $ } from "bun";
3
+ export async function checkGitClean(dryRun) {
4
+ try {
5
+ const status = await $`git status --porcelain`.text();
6
+ if (status.trim() && !dryRun) {
7
+ logger.error("Working directory is not clean. Please commit or stash changes first.");
8
+ process.exit(1);
9
+ }
10
+ } catch {
11
+ logger.error("Not a git repository");
12
+ process.exit(1);
13
+ }
14
+ }
15
+ export async function createGitTag(version, config, tagFormat) {
16
+ const format = tagFormat || config?.release?.tagFormat || "v${version}";
17
+ const tag = format.replace("${version}", version);
18
+ await $`git add package.json`;
19
+ await $`git commit -m "chore: release v${version}"`;
20
+ await $`git tag ${tag}`;
21
+ await $`git push origin main --tags`.nothrow();
22
+ }
23
+ export async function getGitHubRepo() {
24
+ try {
25
+ const remote = await $`git remote get-url origin`.text();
26
+ const match = remote.match(/github\.com[:/]([^\s/]+\/[^\s/]+?)(?:\.git)?(?:\s|$)/);
27
+ return match?.[1] ?? "unknown/repo";
28
+ } catch {
29
+ return "unknown/repo";
30
+ }
31
+ }
32
+ export async function createGitHubRelease(version, config) {
33
+ if (!(config?.release?.github ?? false)) {
34
+ return;
35
+ }
36
+ const tag = `v${version}`;
37
+ try {
38
+ await $`gh --version`.quiet();
39
+ } catch {
40
+ logger.warn("GitHub CLI not found, skipping GitHub release");
41
+ return;
42
+ }
43
+ await $`gh release create ${tag} --title "Release ${tag}" --generate-notes`.nothrow();
44
+ }
45
+ export async function runTests() {
46
+ await $`bun test`;
47
+ }
48
+ export async function buildProject() {
49
+ await $`bun run build`;
50
+ }
51
+ export function bumpVersionSimple(version, type) {
52
+ const parts = version.split(".").map(Number);
53
+ const [major = 0, minor = 0, patch = 0] = parts;
54
+ switch (type) {
55
+ case "patch":
56
+ return `${major}.${minor}.${patch + 1}`;
57
+ case "minor":
58
+ return `${major}.${minor + 1}.0`;
59
+ case "major":
60
+ return `${major + 1}.0.0`;
61
+ }
62
+ }
package/dist/mod.d.ts CHANGED
@@ -23,9 +23,6 @@ export interface PublishConfig extends BaseConfig {
23
23
  config: PackagePublishConfig;
24
24
  }>;
25
25
  }
26
- export interface DlerConfig {
27
- publish?: PublishConfig;
28
- }
29
26
  export interface PublishOptions {
30
27
  dryRun?: boolean;
31
28
  tag?: string;
@@ -49,8 +46,14 @@ export interface PublishOptions {
49
46
  bunRegistry?: string;
50
47
  skipTip2FA?: boolean;
51
48
  filter?: string | string[];
49
+ release?: boolean;
50
+ test?: boolean;
51
+ build?: boolean;
52
+ github?: boolean;
53
+ gitTag?: boolean;
54
+ version?: string | "patch" | "minor" | "major";
52
55
  }
53
- export type { PackageKind, RegistryType, } from "@reliverse/config/impl/publish";
56
+ export type { PackageKind, RegistryType } from "@reliverse/config/impl/publish";
54
57
  export interface PublishResult {
55
58
  success: boolean;
56
59
  packageName: string;
@@ -74,3 +77,4 @@ export declare function publishPackage(packagePath: string, options?: PublishOpt
74
77
  * Publish all workspace packages
75
78
  */
76
79
  export declare function publishAllPackages(cwd?: string, ignore?: string | string[], options?: PublishOptions): Promise<PublishAllResult>;
80
+ export { buildProject, bumpVersionSimple, checkGitClean, createGitHubRelease, createGitTag, getGitHubRepo, runTests, } from "./impl/release.js";
package/dist/mod.js CHANGED
@@ -15,6 +15,15 @@ import { re } from "@reliverse/relico";
15
15
  import { logger } from "@reliverse/relinka";
16
16
  import { readPackageJSON, writePackageJSON } from "@reliverse/typerso";
17
17
  import { config } from "dotenv";
18
+ import {
19
+ buildProject,
20
+ bumpVersionSimple,
21
+ checkGitClean,
22
+ createGitHubRelease,
23
+ createGitTag,
24
+ getGitHubRepo,
25
+ runTests
26
+ } from "./impl/release.js";
18
27
  const THROW_2FA_ERROR = false;
19
28
  let hasShown2FATip = false;
20
29
  async function runBunPublishCommand(packagePath, args, verbose, withNpmLogs) {
@@ -23,9 +32,7 @@ async function runBunPublishCommand(packagePath, args, verbose, withNpmLogs) {
23
32
  logger.debug(`Spawning bun publish command: bun ${args.join(" ")}`);
24
33
  logger.debug(`Working directory: ${packagePath}`);
25
34
  if (withNpmLogs) {
26
- logger.debug(
27
- "With npm logs enabled - output will be displayed directly"
28
- );
35
+ logger.debug("With npm logs enabled - output will be displayed directly");
29
36
  }
30
37
  }
31
38
  const proc = Bun.spawn(args, {
@@ -148,15 +155,11 @@ function validateKindRegistryCombination(kind, registry, verbose) {
148
155
  };
149
156
  const allowedRegistries = allowedCombinations[kind];
150
157
  if (verbose) {
151
- logger.debug(
152
- `Allowed registries for kind "${kind}": ${allowedRegistries.join(", ")}`
153
- );
158
+ logger.debug(`Allowed registries for kind "${kind}": ${allowedRegistries.join(", ")}`);
154
159
  }
155
160
  if (!allowedRegistries.includes(registry)) {
156
161
  if (verbose) {
157
- logger.debug(
158
- `Validation failed: registry "${registry}" not allowed for kind "${kind}"`
159
- );
162
+ logger.debug(`Validation failed: registry "${registry}" not allowed for kind "${kind}"`);
160
163
  }
161
164
  return {
162
165
  valid: false,
@@ -177,9 +180,7 @@ async function validateDistFolder(packagePath, verbose) {
177
180
  const stat = await Bun.file(distPath).stat();
178
181
  const isValid = stat.isDirectory();
179
182
  if (verbose) {
180
- logger.debug(
181
- `Dist folder ${isValid ? "exists and is a directory" : "is not a directory"}`
182
- );
183
+ logger.debug(`Dist folder ${isValid ? "exists and is a directory" : "is not a directory"}`);
183
184
  }
184
185
  return isValid;
185
186
  } catch (error) {
@@ -210,7 +211,7 @@ function validatePackageJsonFields(pkg, _packageName, kind, verbose) {
210
211
  "Package has 'private: true' - cannot publish. Run 'dler build' to prepare the package."
211
212
  );
212
213
  }
213
- if (!pkg.files || !Array.isArray(pkg.files) || pkg.files.length === 0) {
214
+ if (!(pkg.files && Array.isArray(pkg.files)) || pkg.files.length === 0) {
214
215
  errors.push("Missing or empty 'files' field - Run 'dler build' to add it.");
215
216
  } else {
216
217
  if (verbose) {
@@ -226,23 +227,19 @@ function validatePackageJsonFields(pkg, _packageName, kind, verbose) {
226
227
  logger.debug(`Has bin field: ${hasBinField}`);
227
228
  logger.debug(`Has exports field: ${!!pkg.exports}`);
228
229
  }
229
- if (!pkg.exports && !hasBinField) {
230
+ if (!(pkg.exports || hasBinField)) {
230
231
  errors.push("Missing 'exports' field - Run 'dler build' to add it.");
231
232
  }
232
233
  if (!pkg.publishConfig) {
233
234
  errors.push("Missing 'publishConfig' field - Run 'dler build' to add it.");
234
235
  } else if (!pkg.publishConfig.access) {
235
- errors.push(
236
- "Missing 'publishConfig.access' field - Run 'dler build' to add it."
237
- );
236
+ errors.push("Missing 'publishConfig.access' field - Run 'dler build' to add it.");
238
237
  } else if (verbose) {
239
238
  logger.debug(`PublishConfig access: ${pkg.publishConfig.access}`);
240
239
  }
241
240
  if (kind === "cli") {
242
241
  if (!pkg.bin) {
243
- errors.push(
244
- "CLI package missing 'bin' field - Run 'dler build' to add it."
245
- );
242
+ errors.push("CLI package missing 'bin' field - Run 'dler build' to add it.");
246
243
  } else if (typeof pkg.bin === "object") {
247
244
  const binEntries = Object.keys(pkg.bin);
248
245
  if (verbose) {
@@ -280,25 +277,19 @@ async function restoreOriginalDependencies(packagePath, originalDependencies, or
280
277
  if (pkg) {
281
278
  if (originalDependencies !== void 0) {
282
279
  if (verbose) {
283
- logger.debug(
284
- `Restoring ${Object.keys(originalDependencies).length} dependencies`
285
- );
280
+ logger.debug(`Restoring ${Object.keys(originalDependencies).length} dependencies`);
286
281
  }
287
282
  pkg.dependencies = originalDependencies;
288
283
  }
289
284
  if (originalDevDependencies !== void 0) {
290
285
  if (verbose) {
291
- logger.debug(
292
- `Restoring ${Object.keys(originalDevDependencies).length} devDependencies`
293
- );
286
+ logger.debug(`Restoring ${Object.keys(originalDevDependencies).length} devDependencies`);
294
287
  }
295
288
  pkg.devDependencies = originalDevDependencies;
296
289
  }
297
290
  if (originalScripts !== void 0) {
298
291
  if (verbose) {
299
- logger.debug(
300
- `Restoring ${Object.keys(originalScripts).length} scripts`
301
- );
292
+ logger.debug(`Restoring ${Object.keys(originalScripts).length} scripts`);
302
293
  }
303
294
  pkg.scripts = originalScripts;
304
295
  }
@@ -355,9 +346,7 @@ async function resolveWorkspaceVersion(packagePath, depName, workspacePackages,
355
346
  const workspacePkg = workspacePackages.find((pkg) => pkg.name === depName);
356
347
  if (workspacePkg?.pkg?.version) {
357
348
  if (verbose) {
358
- logger.debug(
359
- `Resolved workspace version for ${depName}: ${workspacePkg.pkg.version}`
360
- );
349
+ logger.debug(`Resolved workspace version for ${depName}: ${workspacePkg.pkg.version}`);
361
350
  }
362
351
  return workspacePkg.pkg.version;
363
352
  }
@@ -401,12 +390,11 @@ async function resolveCatalogVersion(packagePath, depName, verbose) {
401
390
  const catalogVersion = catalog[depName];
402
391
  if (catalogVersion) {
403
392
  if (verbose) {
404
- logger.debug(
405
- `Resolved catalog version for ${depName}: ${catalogVersion}`
406
- );
393
+ logger.debug(`Resolved catalog version for ${depName}: ${catalogVersion}`);
407
394
  }
408
395
  return catalogVersion;
409
- } else if (verbose) {
396
+ }
397
+ if (verbose) {
410
398
  logger.debug(`Catalog found but ${depName} not in catalog`);
411
399
  }
412
400
  }
@@ -453,32 +441,30 @@ async function preparePackageForPublishing(packagePath, options, workspacePackag
453
441
  if (options.verbose) {
454
442
  logger.debug("Created backup of original package.json");
455
443
  }
456
- if (shouldBumpVersion && options.bump && !options.bumpDisable && !options.dryRun && pkg.version) {
457
- if (options.verbose) {
458
- logger.debug(`Bumping version: ${pkg.version} -> ${options.bump}`);
459
- }
460
- const bumpResult = bumpVersion(pkg.version, options.bump);
461
- if (!bumpResult) {
444
+ if (shouldBumpVersion && !options.dryRun && bumpedVersions && pkg.name) {
445
+ const bumpedVersion = bumpedVersions.get(pkg.name);
446
+ if (bumpedVersion) {
462
447
  if (options.verbose) {
463
- logger.debug(`Invalid version bump: ${options.bump}`);
448
+ logger.log(
449
+ re.blue(
450
+ ` Bumping version from ${re.bold(pkg.version || "none")} to ${re.bold(re.green(bumpedVersion))}`
451
+ )
452
+ );
453
+ }
454
+ pkg.version = bumpedVersion;
455
+ } else if (options.bump && !options.bumpDisable && pkg.version) {
456
+ const bumpResult = bumpVersion(pkg.version, options.bump);
457
+ if (bumpResult) {
458
+ if (options.verbose) {
459
+ logger.log(
460
+ re.blue(
461
+ ` Bumping version from ${re.bold(pkg.version)} to ${re.bold(re.green(bumpResult.bumped))} (${options.bump})`
462
+ )
463
+ );
464
+ }
465
+ pkg.version = bumpResult.bumped;
464
466
  }
465
- return {
466
- success: false,
467
- error: `Invalid version bump: ${options.bump}`
468
- };
469
- }
470
- if (options.verbose) {
471
- logger.log(
472
- re.blue(
473
- ` Bumping version from ${re.bold(pkg.version)} to ${re.bold(re.green(bumpResult.bumped))} (${options.bump})`
474
- )
475
- );
476
467
  }
477
- pkg.version = bumpResult.bumped;
478
- } else if (options.verbose) {
479
- logger.debug(
480
- `Skipping version bump: shouldBumpVersion=${shouldBumpVersion}, bump=${options.bump}, bumpDisable=${options.bumpDisable}, dryRun=${options.dryRun}`
481
- );
482
468
  }
483
469
  if (!pkg.publishConfig) {
484
470
  pkg.publishConfig = {};
@@ -498,8 +484,8 @@ async function preparePackageForPublishing(packagePath, options, workspacePackag
498
484
  `Stored originals: ${originalDependencies ? Object.keys(originalDependencies).length : 0} deps, ${originalDevDependencies ? Object.keys(originalDevDependencies).length : 0} devDeps, ${originalScripts ? Object.keys(originalScripts).length : 0} scripts`
499
485
  );
500
486
  }
501
- delete pkg.devDependencies;
502
- delete pkg.scripts;
487
+ pkg.devDependencies = void 0;
488
+ pkg.scripts = void 0;
503
489
  if (options.verbose) {
504
490
  logger.debug("Removed devDependencies and scripts for publishing");
505
491
  }
@@ -626,9 +612,7 @@ export async function publishPackage(packagePath, options = {}, bumpedVersions)
626
612
  workspacePackages = await getWorkspacePackages(monorepoRoot);
627
613
  if (options.verbose) {
628
614
  logger.log(
629
- re.blue(
630
- ` Found ${re.bold(workspacePackages.length)} workspace packages`
631
- )
615
+ re.blue(` Found ${re.bold(String(workspacePackages.length))} workspace packages`)
632
616
  );
633
617
  }
634
618
  } else if (options.verbose) {
@@ -703,17 +687,11 @@ export async function publishPackage(packagePath, options = {}, bumpedVersions)
703
687
  await Bun.write(targetPath, content);
704
688
  copiedFiles.push(targetPath);
705
689
  if (options.verbose) {
706
- logger.log(
707
- re.blue(` Copied ${re.bold(fileName)} from monorepo root`)
708
- );
709
- logger.debug(
710
- `Copied ${fileName} from ${sourcePath} to ${targetPath}`
711
- );
690
+ logger.log(re.blue(` Copied ${re.bold(fileName)} from monorepo root`));
691
+ logger.debug(`Copied ${fileName} from ${sourcePath} to ${targetPath}`);
712
692
  }
713
693
  } else if (options.verbose) {
714
- logger.debug(
715
- `${fileName} already exists in package, skipping copy`
716
- );
694
+ logger.debug(`${fileName} already exists in package, skipping copy`);
717
695
  }
718
696
  } else if (options.verbose) {
719
697
  logger.debug(`${fileName} not found in monorepo root`);
@@ -738,9 +716,7 @@ export async function publishPackage(packagePath, options = {}, bumpedVersions)
738
716
  );
739
717
  if (!pkgValidation.valid) {
740
718
  if (options.verbose) {
741
- logger.debug(
742
- `Package validation failed with ${pkgValidation.errors.length} error(s)`
743
- );
719
+ logger.debug(`Package validation failed with ${pkgValidation.errors.length} error(s)`);
744
720
  }
745
721
  return {
746
722
  success: false,
@@ -786,9 +762,7 @@ export async function publishPackage(packagePath, options = {}, bumpedVersions)
786
762
  };
787
763
  }
788
764
  if (options.verbose) {
789
- logger.debug(
790
- `Package prepared successfully, version: ${prepResult.version}`
791
- );
765
+ logger.debug(`Package prepared successfully, version: ${prepResult.version}`);
792
766
  }
793
767
  originalDependencies = prepResult.originalDependencies;
794
768
  originalDevDependencies = prepResult.originalDevDependencies;
@@ -848,16 +822,14 @@ export async function publishPackage(packagePath, options = {}, bumpedVersions)
848
822
  hasShown2FATip = true;
849
823
  logger.log(
850
824
  `
851
- ${re.cyan.bold("\u{1F4A1} 2FA Authentication Tip")}
825
+ ${re.bold(re.cyan("\u{1F4A1} 2FA Authentication Tip"))}
852
826
  If you have 2FA enabled on npm, you may be prompted for a one-time password during publishing (on each publish attempt).
853
827
  ${re.bold("Quick fix:")}
854
828
  When prompted, check the box: "Do not challenge npm publish operations from your IP address for the next 5 minutes"
855
829
  ${re.bold("Permanent solution:")}
856
830
  https://npmjs.com \u2192 Avatar \u2192 Account \u2192 Modify 2FA \u2192 Uncheck "Require two-factor authentication for write actions"
857
831
 
858
- ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
859
-
860
- `)}`
832
+ ${re.bold("Use --skip-tip-2fa to skip this information and the 3s wait.\n\n")}`
861
833
  );
862
834
  await new Promise((resolve2) => setTimeout(resolve2, 3e3));
863
835
  }
@@ -871,9 +843,7 @@ ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
871
843
  if (result.exitCode !== 0) {
872
844
  const errorOutput = result.stderr || result.stdout;
873
845
  if (options.verbose) {
874
- logger.debug(
875
- `Publish command failed with exit code ${result.exitCode}`
876
- );
846
+ logger.debug(`Publish command failed with exit code ${result.exitCode}`);
877
847
  logger.debug(`Error output: ${errorOutput}`);
878
848
  }
879
849
  if (THROW_2FA_ERROR && errorOutput.includes("This operation requires a one-time password.")) {
@@ -898,7 +868,7 @@ ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
898
868
  }
899
869
  const output = result.stdout || result.stderr;
900
870
  if (options.verbose) {
901
- logger.debug(`Publish command succeeded`);
871
+ logger.debug("Publish command succeeded");
902
872
  logger.debug(`Output length: ${output.length} bytes`);
903
873
  }
904
874
  const versionMatch = output.match(/published\s+([^\s]+)/i) || output.match(/@([0-9]+\.[0-9]+\.[0-9]+)/);
@@ -908,22 +878,16 @@ ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
908
878
  }
909
879
  if (options.verbose) {
910
880
  logger.log(
911
- re.green(
912
- `\u2713 Published ${re.bold(packageName)}@${re.bold(version || "unknown")}`
913
- )
881
+ re.green(`\u2713 Published ${re.bold(packageName)}@${re.bold(version || "unknown")}`)
914
882
  );
915
883
  } else {
916
884
  logger.log(
917
- re.green(
918
- `\u2713 Published ${re.bold(packageName)}${version ? `@${re.bold(version)}` : ""}`
919
- )
885
+ re.green(`\u2713 Published ${re.bold(packageName)}${version ? `@${re.bold(version)}` : ""}`)
920
886
  );
921
887
  }
922
888
  } catch (error) {
923
889
  if (options.verbose) {
924
- logger.debug(
925
- `Publish error: ${error instanceof Error ? error.message : String(error)}`
926
- );
890
+ logger.debug(`Publish error: ${error instanceof Error ? error.message : String(error)}`);
927
891
  }
928
892
  throw new Error(
929
893
  `Failed to publish ${packageName}: ${error instanceof Error ? error.message : String(error)}`
@@ -1005,9 +969,7 @@ ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
1005
969
  await Bun.file(copiedFile).unlink();
1006
970
  if (options.verbose) {
1007
971
  const fileName = copiedFile.split(/[/\\]/).pop();
1008
- logger.log(
1009
- re.blue(` Removed copied file: ${re.bold(fileName || "")}`)
1010
- );
972
+ logger.log(re.blue(` Removed copied file: ${re.bold(fileName || "")}`));
1011
973
  logger.debug(`Removed copied file: ${copiedFile}`);
1012
974
  }
1013
975
  } catch (error) {
@@ -1045,6 +1007,33 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1045
1007
  logger.debug("Loading dler.ts configuration...");
1046
1008
  }
1047
1009
  const dlerConfig = await loadDlerConfig(cwd);
1010
+ if (options.release) {
1011
+ const shouldTest = options.test !== false;
1012
+ const shouldBuild = options.build !== false;
1013
+ if (shouldTest || shouldBuild) {
1014
+ await checkGitClean(options.dryRun ?? false);
1015
+ }
1016
+ if (shouldTest && !options.dryRun) {
1017
+ logger.info("Running tests...");
1018
+ try {
1019
+ await runTests();
1020
+ logger.success("\u2713 Tests passed");
1021
+ } catch (error) {
1022
+ logger.error("\u2717 Tests failed");
1023
+ throw error;
1024
+ }
1025
+ }
1026
+ if (shouldBuild && !options.dryRun) {
1027
+ logger.info("Building project...");
1028
+ try {
1029
+ await buildProject();
1030
+ logger.success("\u2713 Build complete");
1031
+ } catch (error) {
1032
+ logger.error("\u2717 Build failed");
1033
+ throw error;
1034
+ }
1035
+ }
1036
+ }
1048
1037
  if (options.verbose) {
1049
1038
  logger.debug("Discovering workspace packages...");
1050
1039
  }
@@ -1062,7 +1051,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1062
1051
  const filterPatterns = Array.isArray(options.filter) ? options.filter : [options.filter];
1063
1052
  logger.info(
1064
1053
  re.blue(
1065
- ` Filtering to ${re.bold(filteredPackages.length)} packages matching: ${re.bold(filterPatterns.join(", "))}`
1054
+ ` Filtering to ${re.bold(String(filteredPackages.length))} packages matching: ${re.bold(filterPatterns.join(", "))}`
1066
1055
  )
1067
1056
  );
1068
1057
  }
@@ -1077,11 +1066,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1077
1066
  warningCount: 0
1078
1067
  };
1079
1068
  }
1080
- logger.info(
1081
- re.blue(
1082
- `Found ${re.bold(filteredPackages.length)} package(s) to publish`
1083
- )
1084
- );
1069
+ logger.info(re.blue(`Found ${re.bold(String(filteredPackages.length))} package(s) to publish`));
1085
1070
  const results = [];
1086
1071
  const concurrency = options.concurrency || 3;
1087
1072
  if (options.verbose) {
@@ -1091,21 +1076,15 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1091
1076
  const packageConfig = getPackagePublishConfig(pkg.name, dlerConfig);
1092
1077
  if (packageConfig?.enable === false) {
1093
1078
  if (options.verbose) {
1094
- logger.info(
1095
- re.yellow(`Skipping ${re.bold(pkg.name)} (disabled in config)`)
1096
- );
1097
- logger.debug(
1098
- `Package config for ${pkg.name}: ${JSON.stringify(packageConfig)}`
1099
- );
1079
+ logger.info(re.yellow(`Skipping ${re.bold(pkg.name)} (disabled in config)`));
1080
+ logger.debug(`Package config for ${pkg.name}: ${JSON.stringify(packageConfig)}`);
1100
1081
  }
1101
1082
  return false;
1102
1083
  }
1103
1084
  return true;
1104
1085
  });
1105
1086
  if (options.verbose) {
1106
- logger.debug(
1107
- `After enable filter: ${packagesToPublish.length} package(s) enabled`
1108
- );
1087
+ logger.debug(`After enable filter: ${packagesToPublish.length} package(s) enabled`);
1109
1088
  }
1110
1089
  if (packagesToPublish.length === 0) {
1111
1090
  logger.warn("No packages enabled for publishing");
@@ -1118,9 +1097,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1118
1097
  };
1119
1098
  }
1120
1099
  logger.info(
1121
- re.blue(
1122
- `Publishing ${re.bold(packagesToPublish.length)} enabled package(s)`
1123
- )
1100
+ re.blue(`Publishing ${re.bold(String(packagesToPublish.length))} enabled package(s)`)
1124
1101
  );
1125
1102
  const bumpedVersions = /* @__PURE__ */ new Map();
1126
1103
  if (options.verbose) {
@@ -1128,41 +1105,63 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1128
1105
  }
1129
1106
  for (const pkg of packagesToPublish) {
1130
1107
  const mergedOptions = mergePublishOptions(options, pkg.name, dlerConfig);
1131
- const bumpType = mergedOptions.bump || (mergedOptions.bumpDisable ? void 0 : "patch");
1132
- if (options.verbose) {
1133
- logger.debug(
1134
- `Pre-bumping ${pkg.name}: current=${pkg.pkg.version}, bumpType=${bumpType ?? "none"}, bumpDisable=${mergedOptions.bumpDisable}`
1135
- );
1136
- }
1137
- if (bumpType && !mergedOptions.bumpDisable && pkg.pkg.version) {
1138
- try {
1139
- const nextVersion = getNextVersion(pkg.pkg.version, bumpType);
1140
- if (nextVersion) {
1141
- bumpedVersions.set(pkg.name, nextVersion);
1142
- if (options.verbose) {
1143
- logger.log(
1144
- re.blue(
1145
- ` ${re.bold(pkg.name)}: ${pkg.pkg.version} -> ${re.green.bold(nextVersion)}`
1146
- )
1147
- );
1148
- }
1149
- } else if (options.verbose) {
1150
- logger.debug(`Failed to calculate next version for ${pkg.name}`);
1151
- }
1152
- } catch (error) {
1153
- if (options.verbose) {
1154
- logger.debug(
1155
- `Error bumping ${pkg.name}: ${error instanceof Error ? error.message : String(error)}`
1108
+ if (options.release && options.version) {
1109
+ let releaseVersion;
1110
+ if (typeof options.version === "string" && !["patch", "minor", "major"].includes(options.version)) {
1111
+ releaseVersion = options.version;
1112
+ } else {
1113
+ if (pkg.pkg.version) {
1114
+ releaseVersion = bumpVersionSimple(
1115
+ pkg.pkg.version,
1116
+ options.version || "patch"
1156
1117
  );
1118
+ } else {
1119
+ releaseVersion = "1.0.0";
1157
1120
  }
1158
1121
  }
1159
- } else if (pkg.pkg.version) {
1160
- bumpedVersions.set(pkg.name, pkg.pkg.version);
1122
+ bumpedVersions.set(pkg.name, releaseVersion);
1123
+ if (options.verbose) {
1124
+ logger.log(
1125
+ re.blue(
1126
+ ` ${re.bold(pkg.name)}: ${pkg.pkg.version || "none"} -> ${re.bold(re.green(releaseVersion))} (release)`
1127
+ )
1128
+ );
1129
+ }
1130
+ } else {
1131
+ const bumpType = mergedOptions.bump || (mergedOptions.bumpDisable ? void 0 : "patch");
1161
1132
  if (options.verbose) {
1162
1133
  logger.debug(
1163
- `Using current version for ${pkg.name}: ${pkg.pkg.version}`
1134
+ `Pre-bumping ${pkg.name}: current=${pkg.pkg.version}, bumpType=${bumpType ?? "none"}, bumpDisable=${mergedOptions.bumpDisable}`
1164
1135
  );
1165
1136
  }
1137
+ if (bumpType && !mergedOptions.bumpDisable && pkg.pkg.version) {
1138
+ try {
1139
+ const nextVersion = getNextVersion(pkg.pkg.version, bumpType);
1140
+ if (nextVersion) {
1141
+ bumpedVersions.set(pkg.name, nextVersion);
1142
+ if (options.verbose) {
1143
+ logger.log(
1144
+ re.blue(
1145
+ ` ${re.bold(pkg.name)}: ${pkg.pkg.version} -> ${re.bold(re.green(nextVersion))}`
1146
+ )
1147
+ );
1148
+ }
1149
+ } else if (options.verbose) {
1150
+ logger.debug(`Failed to calculate next version for ${pkg.name}`);
1151
+ }
1152
+ } catch (error) {
1153
+ if (options.verbose) {
1154
+ logger.debug(
1155
+ `Error bumping ${pkg.name}: ${error instanceof Error ? error.message : String(error)}`
1156
+ );
1157
+ }
1158
+ }
1159
+ } else if (pkg.pkg.version) {
1160
+ bumpedVersions.set(pkg.name, pkg.pkg.version);
1161
+ if (options.verbose) {
1162
+ logger.debug(`Using current version for ${pkg.name}: ${pkg.pkg.version}`);
1163
+ }
1164
+ }
1166
1165
  }
1167
1166
  }
1168
1167
  if (options.verbose) {
@@ -1170,9 +1169,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1170
1169
  }
1171
1170
  const totalBatches = Math.ceil(packagesToPublish.length / concurrency);
1172
1171
  if (options.verbose) {
1173
- logger.debug(
1174
- `Processing ${totalBatches} batch(es) with concurrency ${concurrency}`
1175
- );
1172
+ logger.debug(`Processing ${totalBatches} batch(es) with concurrency ${concurrency}`);
1176
1173
  }
1177
1174
  for (let i = 0; i < packagesToPublish.length; i += concurrency) {
1178
1175
  const batch = packagesToPublish.slice(i, i + concurrency);
@@ -1183,18 +1180,12 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1183
1180
  );
1184
1181
  }
1185
1182
  const batchPromises = batch.map(async (pkg) => {
1186
- const mergedOptions = mergePublishOptions(
1187
- options,
1188
- pkg.name,
1189
- dlerConfig
1190
- );
1191
- if (!mergedOptions.bump && !mergedOptions.bumpDisable) {
1183
+ const mergedOptions = mergePublishOptions(options, pkg.name, dlerConfig);
1184
+ if (!(mergedOptions.bump || mergedOptions.bumpDisable)) {
1192
1185
  mergedOptions.bump = "patch";
1193
1186
  }
1194
1187
  if (options.verbose) {
1195
- logger.debug(
1196
- `Merged options for ${pkg.name}: ${JSON.stringify(mergedOptions)}`
1197
- );
1188
+ logger.debug(`Merged options for ${pkg.name}: ${JSON.stringify(mergedOptions)}`);
1198
1189
  }
1199
1190
  if (mergedOptions.verbose) {
1200
1191
  logger.info(re.blue(`Publishing ${re.bold(pkg.name)}...`));
@@ -1213,9 +1204,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1213
1204
  const hasBatchErrors = batchResults.some((r) => !r.success);
1214
1205
  if (hasBatchErrors) {
1215
1206
  if (options.verbose) {
1216
- logger.debug(
1217
- `Batch ${batchNumber} had errors, stopping processing of remaining batches`
1218
- );
1207
+ logger.debug(`Batch ${batchNumber} had errors, stopping processing of remaining batches`);
1219
1208
  }
1220
1209
  break;
1221
1210
  }
@@ -1231,6 +1220,45 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1231
1220
  `Publish all completed: ${successCount} success, ${errorCount} error(s), ${warningCount} warning(s)`
1232
1221
  );
1233
1222
  }
1223
+ if (options.release && !hasErrors && !options.dryRun) {
1224
+ const shouldGitTag = options.gitTag !== false;
1225
+ const shouldGitHub = options.github ?? dlerConfig?.release?.github ?? false;
1226
+ let publishedVersion;
1227
+ if (options.version && typeof options.version === "string" && !["patch", "minor", "major"].includes(options.version)) {
1228
+ publishedVersion = options.version;
1229
+ } else {
1230
+ const firstSuccess = results.find((r) => r.success && r.version);
1231
+ publishedVersion = firstSuccess?.version;
1232
+ }
1233
+ if (publishedVersion) {
1234
+ if (shouldGitTag) {
1235
+ logger.info("Creating git tag...");
1236
+ try {
1237
+ await createGitTag(publishedVersion, dlerConfig, options.tag);
1238
+ logger.success("\u2713 Git tag created");
1239
+ } catch (error) {
1240
+ logger.error("\u2717 Git tag failed");
1241
+ if (options.verbose) {
1242
+ logger.error(error instanceof Error ? error.message : String(error));
1243
+ }
1244
+ }
1245
+ }
1246
+ if (shouldGitHub) {
1247
+ logger.info("Creating GitHub release...");
1248
+ try {
1249
+ await createGitHubRelease(publishedVersion, dlerConfig);
1250
+ logger.success("\u2713 GitHub release created");
1251
+ const repo = await getGitHubRepo();
1252
+ logger.info(`GitHub: https://github.com/${repo}/releases/tag/v${publishedVersion}`);
1253
+ } catch (error) {
1254
+ logger.error("\u2717 GitHub release failed");
1255
+ if (options.verbose) {
1256
+ logger.error(error instanceof Error ? error.message : String(error));
1257
+ }
1258
+ }
1259
+ }
1260
+ }
1261
+ }
1234
1262
  return {
1235
1263
  results,
1236
1264
  hasErrors,
@@ -1240,9 +1268,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1240
1268
  };
1241
1269
  } catch (error) {
1242
1270
  if (options.verbose) {
1243
- logger.debug(
1244
- `Publish all failed: ${error instanceof Error ? error.message : String(error)}`
1245
- );
1271
+ logger.debug(`Publish all failed: ${error instanceof Error ? error.message : String(error)}`);
1246
1272
  }
1247
1273
  logger.error(
1248
1274
  re.red("Failed to publish packages:"),
@@ -1257,3 +1283,12 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1257
1283
  };
1258
1284
  }
1259
1285
  }
1286
+ export {
1287
+ buildProject,
1288
+ bumpVersionSimple,
1289
+ checkGitClean,
1290
+ createGitHubRelease,
1291
+ createGitTag,
1292
+ getGitHubRepo,
1293
+ runTests
1294
+ } from "./impl/release.js";
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@reliverse/publish",
3
- "version": "2.2.9",
3
+ "version": "2.3.1",
4
4
  "private": false,
5
+ "license": "MIT",
6
+ "files": [
7
+ "dist",
8
+ "package.json"
9
+ ],
5
10
  "type": "module",
6
11
  "exports": {
7
12
  ".": {
@@ -9,22 +14,17 @@
9
14
  "default": "./dist/mod.js"
10
15
  }
11
16
  },
12
- "dependencies": {
13
- "c12": "^3.3.3",
14
- "dotenv": "^17.2.3",
15
- "@reliverse/typerso": "2.2.9",
16
- "@reliverse/config": "2.2.9",
17
- "@reliverse/relinka": "2.2.9",
18
- "@reliverse/bump": "2.2.10",
19
- "@reliverse/relico": "2.2.9",
20
- "@reliverse/build": "2.2.9"
21
- },
22
17
  "publishConfig": {
23
18
  "access": "public"
24
19
  },
25
- "files": [
26
- "dist",
27
- "package.json"
28
- ],
29
- "license": "MIT"
20
+ "dependencies": {
21
+ "@reliverse/build": "2.3.1",
22
+ "@reliverse/bump": "2.3.1",
23
+ "@reliverse/config": "2.3.1",
24
+ "@reliverse/relico": "2.3.1",
25
+ "@reliverse/relinka": "2.3.1",
26
+ "@reliverse/typerso": "2.3.1",
27
+ "c12": "^3.3.3",
28
+ "dotenv": "^17.2.3"
29
+ }
30
30
  }