@reliverse/publish 2.2.9 → 2.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
  }
@@ -573,6 +559,48 @@ async function preparePackageForPublishing(packagePath, options, workspacePackag
573
559
  }
574
560
  pkg.exports = transformExportsForBuild(pkg.exports);
575
561
  }
562
+ if (pkg.bin) {
563
+ if (options.verbose) {
564
+ logger.debug("Transforming bin field for publishing");
565
+ }
566
+ if (typeof pkg.bin === "string") {
567
+ pkg.bin = pkg.bin.replace(/\.ts$/, ".js").replace(/^\.\/src\//, "./dist/");
568
+ } else if (typeof pkg.bin === "object" && pkg.bin !== null) {
569
+ for (const [key, value] of Object.entries(pkg.bin)) {
570
+ if (typeof value === "string") {
571
+ pkg.bin[key] = value.replace(/\.ts$/, ".js").replace(/^\.\/src\//, "./dist/");
572
+ }
573
+ }
574
+ }
575
+ }
576
+ if (pkg.module && typeof pkg.module === "string") {
577
+ if (options.verbose) {
578
+ logger.debug("Transforming module field for publishing");
579
+ }
580
+ pkg.module = pkg.module.replace(/\.ts$/, ".js").replace(/^\.\/src\//, "./dist/");
581
+ }
582
+ if (pkg.types && typeof pkg.types === "string") {
583
+ if (options.verbose) {
584
+ logger.debug("Transforming types field for publishing");
585
+ }
586
+ pkg.types = pkg.types.replace(/\.ts$/, ".d.ts").replace(/^\.\/src\//, "./dist/");
587
+ }
588
+ if (pkg.files && Array.isArray(pkg.files)) {
589
+ if (!pkg.files.includes("README.md")) {
590
+ try {
591
+ const readmePath = resolve(packagePath, "README.md");
592
+ await Bun.file(readmePath).stat();
593
+ pkg.files.push("README.md");
594
+ if (options.verbose) {
595
+ logger.debug("Added README.md to files field");
596
+ }
597
+ } catch (error) {
598
+ if (options.verbose) {
599
+ logger.debug("README.md not found, skipping addition to files field");
600
+ }
601
+ }
602
+ }
603
+ }
576
604
  await writePackageJSON(resolve(packagePath, "package.json"), pkg);
577
605
  if (options.verbose) {
578
606
  logger.debug("Successfully wrote modified package.json");
@@ -626,9 +654,7 @@ export async function publishPackage(packagePath, options = {}, bumpedVersions)
626
654
  workspacePackages = await getWorkspacePackages(monorepoRoot);
627
655
  if (options.verbose) {
628
656
  logger.log(
629
- re.blue(
630
- ` Found ${re.bold(workspacePackages.length)} workspace packages`
631
- )
657
+ re.blue(` Found ${re.bold(String(workspacePackages.length))} workspace packages`)
632
658
  );
633
659
  }
634
660
  } else if (options.verbose) {
@@ -703,17 +729,11 @@ export async function publishPackage(packagePath, options = {}, bumpedVersions)
703
729
  await Bun.write(targetPath, content);
704
730
  copiedFiles.push(targetPath);
705
731
  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
- );
732
+ logger.log(re.blue(` Copied ${re.bold(fileName)} from monorepo root`));
733
+ logger.debug(`Copied ${fileName} from ${sourcePath} to ${targetPath}`);
712
734
  }
713
735
  } else if (options.verbose) {
714
- logger.debug(
715
- `${fileName} already exists in package, skipping copy`
716
- );
736
+ logger.debug(`${fileName} already exists in package, skipping copy`);
717
737
  }
718
738
  } else if (options.verbose) {
719
739
  logger.debug(`${fileName} not found in monorepo root`);
@@ -738,9 +758,7 @@ export async function publishPackage(packagePath, options = {}, bumpedVersions)
738
758
  );
739
759
  if (!pkgValidation.valid) {
740
760
  if (options.verbose) {
741
- logger.debug(
742
- `Package validation failed with ${pkgValidation.errors.length} error(s)`
743
- );
761
+ logger.debug(`Package validation failed with ${pkgValidation.errors.length} error(s)`);
744
762
  }
745
763
  return {
746
764
  success: false,
@@ -786,9 +804,7 @@ export async function publishPackage(packagePath, options = {}, bumpedVersions)
786
804
  };
787
805
  }
788
806
  if (options.verbose) {
789
- logger.debug(
790
- `Package prepared successfully, version: ${prepResult.version}`
791
- );
807
+ logger.debug(`Package prepared successfully, version: ${prepResult.version}`);
792
808
  }
793
809
  originalDependencies = prepResult.originalDependencies;
794
810
  originalDevDependencies = prepResult.originalDevDependencies;
@@ -848,16 +864,14 @@ export async function publishPackage(packagePath, options = {}, bumpedVersions)
848
864
  hasShown2FATip = true;
849
865
  logger.log(
850
866
  `
851
- ${re.cyan.bold("\u{1F4A1} 2FA Authentication Tip")}
867
+ ${re.bold(re.cyan("\u{1F4A1} 2FA Authentication Tip"))}
852
868
  If you have 2FA enabled on npm, you may be prompted for a one-time password during publishing (on each publish attempt).
853
869
  ${re.bold("Quick fix:")}
854
870
  When prompted, check the box: "Do not challenge npm publish operations from your IP address for the next 5 minutes"
855
871
  ${re.bold("Permanent solution:")}
856
872
  https://npmjs.com \u2192 Avatar \u2192 Account \u2192 Modify 2FA \u2192 Uncheck "Require two-factor authentication for write actions"
857
873
 
858
- ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
859
-
860
- `)}`
874
+ ${re.bold("Use --skip-tip-2fa to skip this information and the 3s wait.\n\n")}`
861
875
  );
862
876
  await new Promise((resolve2) => setTimeout(resolve2, 3e3));
863
877
  }
@@ -871,9 +885,7 @@ ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
871
885
  if (result.exitCode !== 0) {
872
886
  const errorOutput = result.stderr || result.stdout;
873
887
  if (options.verbose) {
874
- logger.debug(
875
- `Publish command failed with exit code ${result.exitCode}`
876
- );
888
+ logger.debug(`Publish command failed with exit code ${result.exitCode}`);
877
889
  logger.debug(`Error output: ${errorOutput}`);
878
890
  }
879
891
  if (THROW_2FA_ERROR && errorOutput.includes("This operation requires a one-time password.")) {
@@ -898,7 +910,7 @@ ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
898
910
  }
899
911
  const output = result.stdout || result.stderr;
900
912
  if (options.verbose) {
901
- logger.debug(`Publish command succeeded`);
913
+ logger.debug("Publish command succeeded");
902
914
  logger.debug(`Output length: ${output.length} bytes`);
903
915
  }
904
916
  const versionMatch = output.match(/published\s+([^\s]+)/i) || output.match(/@([0-9]+\.[0-9]+\.[0-9]+)/);
@@ -908,22 +920,16 @@ ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
908
920
  }
909
921
  if (options.verbose) {
910
922
  logger.log(
911
- re.green(
912
- `\u2713 Published ${re.bold(packageName)}@${re.bold(version || "unknown")}`
913
- )
923
+ re.green(`\u2713 Published ${re.bold(packageName)}@${re.bold(version || "unknown")}`)
914
924
  );
915
925
  } else {
916
926
  logger.log(
917
- re.green(
918
- `\u2713 Published ${re.bold(packageName)}${version ? `@${re.bold(version)}` : ""}`
919
- )
927
+ re.green(`\u2713 Published ${re.bold(packageName)}${version ? `@${re.bold(version)}` : ""}`)
920
928
  );
921
929
  }
922
930
  } catch (error) {
923
931
  if (options.verbose) {
924
- logger.debug(
925
- `Publish error: ${error instanceof Error ? error.message : String(error)}`
926
- );
932
+ logger.debug(`Publish error: ${error instanceof Error ? error.message : String(error)}`);
927
933
  }
928
934
  throw new Error(
929
935
  `Failed to publish ${packageName}: ${error instanceof Error ? error.message : String(error)}`
@@ -1005,9 +1011,7 @@ ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
1005
1011
  await Bun.file(copiedFile).unlink();
1006
1012
  if (options.verbose) {
1007
1013
  const fileName = copiedFile.split(/[/\\]/).pop();
1008
- logger.log(
1009
- re.blue(` Removed copied file: ${re.bold(fileName || "")}`)
1010
- );
1014
+ logger.log(re.blue(` Removed copied file: ${re.bold(fileName || "")}`));
1011
1015
  logger.debug(`Removed copied file: ${copiedFile}`);
1012
1016
  }
1013
1017
  } catch (error) {
@@ -1045,6 +1049,33 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1045
1049
  logger.debug("Loading dler.ts configuration...");
1046
1050
  }
1047
1051
  const dlerConfig = await loadDlerConfig(cwd);
1052
+ if (options.release) {
1053
+ const shouldTest = options.test !== false;
1054
+ const shouldBuild = options.build !== false;
1055
+ if (shouldTest || shouldBuild) {
1056
+ await checkGitClean(options.dryRun ?? false);
1057
+ }
1058
+ if (shouldTest && !options.dryRun) {
1059
+ logger.info("Running tests...");
1060
+ try {
1061
+ await runTests();
1062
+ logger.success("\u2713 Tests passed");
1063
+ } catch (error) {
1064
+ logger.error("\u2717 Tests failed");
1065
+ throw error;
1066
+ }
1067
+ }
1068
+ if (shouldBuild && !options.dryRun) {
1069
+ logger.info("Building project...");
1070
+ try {
1071
+ await buildProject();
1072
+ logger.success("\u2713 Build complete");
1073
+ } catch (error) {
1074
+ logger.error("\u2717 Build failed");
1075
+ throw error;
1076
+ }
1077
+ }
1078
+ }
1048
1079
  if (options.verbose) {
1049
1080
  logger.debug("Discovering workspace packages...");
1050
1081
  }
@@ -1062,7 +1093,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1062
1093
  const filterPatterns = Array.isArray(options.filter) ? options.filter : [options.filter];
1063
1094
  logger.info(
1064
1095
  re.blue(
1065
- ` Filtering to ${re.bold(filteredPackages.length)} packages matching: ${re.bold(filterPatterns.join(", "))}`
1096
+ ` Filtering to ${re.bold(String(filteredPackages.length))} packages matching: ${re.bold(filterPatterns.join(", "))}`
1066
1097
  )
1067
1098
  );
1068
1099
  }
@@ -1077,11 +1108,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1077
1108
  warningCount: 0
1078
1109
  };
1079
1110
  }
1080
- logger.info(
1081
- re.blue(
1082
- `Found ${re.bold(filteredPackages.length)} package(s) to publish`
1083
- )
1084
- );
1111
+ logger.info(re.blue(`Found ${re.bold(String(filteredPackages.length))} package(s) to publish`));
1085
1112
  const results = [];
1086
1113
  const concurrency = options.concurrency || 3;
1087
1114
  if (options.verbose) {
@@ -1091,21 +1118,15 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1091
1118
  const packageConfig = getPackagePublishConfig(pkg.name, dlerConfig);
1092
1119
  if (packageConfig?.enable === false) {
1093
1120
  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
- );
1121
+ logger.info(re.yellow(`Skipping ${re.bold(pkg.name)} (disabled in config)`));
1122
+ logger.debug(`Package config for ${pkg.name}: ${JSON.stringify(packageConfig)}`);
1100
1123
  }
1101
1124
  return false;
1102
1125
  }
1103
1126
  return true;
1104
1127
  });
1105
1128
  if (options.verbose) {
1106
- logger.debug(
1107
- `After enable filter: ${packagesToPublish.length} package(s) enabled`
1108
- );
1129
+ logger.debug(`After enable filter: ${packagesToPublish.length} package(s) enabled`);
1109
1130
  }
1110
1131
  if (packagesToPublish.length === 0) {
1111
1132
  logger.warn("No packages enabled for publishing");
@@ -1118,9 +1139,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1118
1139
  };
1119
1140
  }
1120
1141
  logger.info(
1121
- re.blue(
1122
- `Publishing ${re.bold(packagesToPublish.length)} enabled package(s)`
1123
- )
1142
+ re.blue(`Publishing ${re.bold(String(packagesToPublish.length))} enabled package(s)`)
1124
1143
  );
1125
1144
  const bumpedVersions = /* @__PURE__ */ new Map();
1126
1145
  if (options.verbose) {
@@ -1128,41 +1147,63 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1128
1147
  }
1129
1148
  for (const pkg of packagesToPublish) {
1130
1149
  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)}`
1150
+ if (options.release && options.version) {
1151
+ let releaseVersion;
1152
+ if (typeof options.version === "string" && !["patch", "minor", "major"].includes(options.version)) {
1153
+ releaseVersion = options.version;
1154
+ } else {
1155
+ if (pkg.pkg.version) {
1156
+ releaseVersion = bumpVersionSimple(
1157
+ pkg.pkg.version,
1158
+ options.version || "patch"
1156
1159
  );
1160
+ } else {
1161
+ releaseVersion = "1.0.0";
1157
1162
  }
1158
1163
  }
1159
- } else if (pkg.pkg.version) {
1160
- bumpedVersions.set(pkg.name, pkg.pkg.version);
1164
+ bumpedVersions.set(pkg.name, releaseVersion);
1165
+ if (options.verbose) {
1166
+ logger.log(
1167
+ re.blue(
1168
+ ` ${re.bold(pkg.name)}: ${pkg.pkg.version || "none"} -> ${re.bold(re.green(releaseVersion))} (release)`
1169
+ )
1170
+ );
1171
+ }
1172
+ } else {
1173
+ const bumpType = mergedOptions.bump || (mergedOptions.bumpDisable ? void 0 : "patch");
1161
1174
  if (options.verbose) {
1162
1175
  logger.debug(
1163
- `Using current version for ${pkg.name}: ${pkg.pkg.version}`
1176
+ `Pre-bumping ${pkg.name}: current=${pkg.pkg.version}, bumpType=${bumpType ?? "none"}, bumpDisable=${mergedOptions.bumpDisable}`
1164
1177
  );
1165
1178
  }
1179
+ if (bumpType && !mergedOptions.bumpDisable && pkg.pkg.version) {
1180
+ try {
1181
+ const nextVersion = getNextVersion(pkg.pkg.version, bumpType);
1182
+ if (nextVersion) {
1183
+ bumpedVersions.set(pkg.name, nextVersion);
1184
+ if (options.verbose) {
1185
+ logger.log(
1186
+ re.blue(
1187
+ ` ${re.bold(pkg.name)}: ${pkg.pkg.version} -> ${re.bold(re.green(nextVersion))}`
1188
+ )
1189
+ );
1190
+ }
1191
+ } else if (options.verbose) {
1192
+ logger.debug(`Failed to calculate next version for ${pkg.name}`);
1193
+ }
1194
+ } catch (error) {
1195
+ if (options.verbose) {
1196
+ logger.debug(
1197
+ `Error bumping ${pkg.name}: ${error instanceof Error ? error.message : String(error)}`
1198
+ );
1199
+ }
1200
+ }
1201
+ } else if (pkg.pkg.version) {
1202
+ bumpedVersions.set(pkg.name, pkg.pkg.version);
1203
+ if (options.verbose) {
1204
+ logger.debug(`Using current version for ${pkg.name}: ${pkg.pkg.version}`);
1205
+ }
1206
+ }
1166
1207
  }
1167
1208
  }
1168
1209
  if (options.verbose) {
@@ -1170,9 +1211,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1170
1211
  }
1171
1212
  const totalBatches = Math.ceil(packagesToPublish.length / concurrency);
1172
1213
  if (options.verbose) {
1173
- logger.debug(
1174
- `Processing ${totalBatches} batch(es) with concurrency ${concurrency}`
1175
- );
1214
+ logger.debug(`Processing ${totalBatches} batch(es) with concurrency ${concurrency}`);
1176
1215
  }
1177
1216
  for (let i = 0; i < packagesToPublish.length; i += concurrency) {
1178
1217
  const batch = packagesToPublish.slice(i, i + concurrency);
@@ -1183,18 +1222,12 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1183
1222
  );
1184
1223
  }
1185
1224
  const batchPromises = batch.map(async (pkg) => {
1186
- const mergedOptions = mergePublishOptions(
1187
- options,
1188
- pkg.name,
1189
- dlerConfig
1190
- );
1191
- if (!mergedOptions.bump && !mergedOptions.bumpDisable) {
1225
+ const mergedOptions = mergePublishOptions(options, pkg.name, dlerConfig);
1226
+ if (!(mergedOptions.bump || mergedOptions.bumpDisable)) {
1192
1227
  mergedOptions.bump = "patch";
1193
1228
  }
1194
1229
  if (options.verbose) {
1195
- logger.debug(
1196
- `Merged options for ${pkg.name}: ${JSON.stringify(mergedOptions)}`
1197
- );
1230
+ logger.debug(`Merged options for ${pkg.name}: ${JSON.stringify(mergedOptions)}`);
1198
1231
  }
1199
1232
  if (mergedOptions.verbose) {
1200
1233
  logger.info(re.blue(`Publishing ${re.bold(pkg.name)}...`));
@@ -1213,9 +1246,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1213
1246
  const hasBatchErrors = batchResults.some((r) => !r.success);
1214
1247
  if (hasBatchErrors) {
1215
1248
  if (options.verbose) {
1216
- logger.debug(
1217
- `Batch ${batchNumber} had errors, stopping processing of remaining batches`
1218
- );
1249
+ logger.debug(`Batch ${batchNumber} had errors, stopping processing of remaining batches`);
1219
1250
  }
1220
1251
  break;
1221
1252
  }
@@ -1231,6 +1262,45 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1231
1262
  `Publish all completed: ${successCount} success, ${errorCount} error(s), ${warningCount} warning(s)`
1232
1263
  );
1233
1264
  }
1265
+ if (options.release && !hasErrors && !options.dryRun) {
1266
+ const shouldGitTag = options.gitTag !== false;
1267
+ const shouldGitHub = options.github ?? dlerConfig?.release?.github ?? false;
1268
+ let publishedVersion;
1269
+ if (options.version && typeof options.version === "string" && !["patch", "minor", "major"].includes(options.version)) {
1270
+ publishedVersion = options.version;
1271
+ } else {
1272
+ const firstSuccess = results.find((r) => r.success && r.version);
1273
+ publishedVersion = firstSuccess?.version;
1274
+ }
1275
+ if (publishedVersion) {
1276
+ if (shouldGitTag) {
1277
+ logger.info("Creating git tag...");
1278
+ try {
1279
+ await createGitTag(publishedVersion, dlerConfig, options.tag);
1280
+ logger.success("\u2713 Git tag created");
1281
+ } catch (error) {
1282
+ logger.error("\u2717 Git tag failed");
1283
+ if (options.verbose) {
1284
+ logger.error(error instanceof Error ? error.message : String(error));
1285
+ }
1286
+ }
1287
+ }
1288
+ if (shouldGitHub) {
1289
+ logger.info("Creating GitHub release...");
1290
+ try {
1291
+ await createGitHubRelease(publishedVersion, dlerConfig);
1292
+ logger.success("\u2713 GitHub release created");
1293
+ const repo = await getGitHubRepo();
1294
+ logger.info(`GitHub: https://github.com/${repo}/releases/tag/v${publishedVersion}`);
1295
+ } catch (error) {
1296
+ logger.error("\u2717 GitHub release failed");
1297
+ if (options.verbose) {
1298
+ logger.error(error instanceof Error ? error.message : String(error));
1299
+ }
1300
+ }
1301
+ }
1302
+ }
1303
+ }
1234
1304
  return {
1235
1305
  results,
1236
1306
  hasErrors,
@@ -1240,9 +1310,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1240
1310
  };
1241
1311
  } catch (error) {
1242
1312
  if (options.verbose) {
1243
- logger.debug(
1244
- `Publish all failed: ${error instanceof Error ? error.message : String(error)}`
1245
- );
1313
+ logger.debug(`Publish all failed: ${error instanceof Error ? error.message : String(error)}`);
1246
1314
  }
1247
1315
  logger.error(
1248
1316
  re.red("Failed to publish packages:"),
@@ -1257,3 +1325,12 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1257
1325
  };
1258
1326
  }
1259
1327
  }
1328
+ export {
1329
+ buildProject,
1330
+ bumpVersionSimple,
1331
+ checkGitClean,
1332
+ createGitHubRelease,
1333
+ createGitTag,
1334
+ getGitHubRepo,
1335
+ runTests
1336
+ } 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.2",
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.2",
22
+ "@reliverse/bump": "2.3.2",
23
+ "@reliverse/config": "2.3.2",
24
+ "@reliverse/relico": "2.3.2",
25
+ "@reliverse/relinka": "2.3.2",
26
+ "@reliverse/typerso": "2.3.2",
27
+ "c12": "^3.3.3",
28
+ "dotenv": "^17.2.3"
29
+ }
30
30
  }