@reliverse/publish 2.2.8 → 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
@@ -14,6 +14,16 @@ import {
14
14
  import { re } from "@reliverse/relico";
15
15
  import { logger } from "@reliverse/relinka";
16
16
  import { readPackageJSON, writePackageJSON } from "@reliverse/typerso";
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";
17
27
  const THROW_2FA_ERROR = false;
18
28
  let hasShown2FATip = false;
19
29
  async function runBunPublishCommand(packagePath, args, verbose, withNpmLogs) {
@@ -22,9 +32,7 @@ async function runBunPublishCommand(packagePath, args, verbose, withNpmLogs) {
22
32
  logger.debug(`Spawning bun publish command: bun ${args.join(" ")}`);
23
33
  logger.debug(`Working directory: ${packagePath}`);
24
34
  if (withNpmLogs) {
25
- logger.debug(
26
- "With npm logs enabled - output will be displayed directly"
27
- );
35
+ logger.debug("With npm logs enabled - output will be displayed directly");
28
36
  }
29
37
  }
30
38
  const proc = Bun.spawn(args, {
@@ -106,7 +114,7 @@ Error: Publishing timed out after 5 minutes. This may indicate an authentication
106
114
  );
107
115
  }
108
116
  throw new Error(
109
- "2FA authentication required. The publish command cannot read OTP input when output is captured. Please either:\n 1. Set NPM_CONFIG_TOKEN environment variable with your npm token (note: .env files are not automatically loaded - export it in your shell or use a tool like dotenv-cli), or\n 2. Use --with-npm-logs flag to allow interactive OTP input."
117
+ "2FA authentication required. The publish command cannot read OTP input when output is captured. Please either:\n 1. Set NPM_CONFIG_TOKEN environment variable with your npm token (.env files are automatically loaded), or\n 2. Use --with-npm-logs flag to allow interactive OTP input."
110
118
  );
111
119
  }
112
120
  return { stdout, stderr, exitCode };
@@ -147,15 +155,11 @@ function validateKindRegistryCombination(kind, registry, verbose) {
147
155
  };
148
156
  const allowedRegistries = allowedCombinations[kind];
149
157
  if (verbose) {
150
- logger.debug(
151
- `Allowed registries for kind "${kind}": ${allowedRegistries.join(", ")}`
152
- );
158
+ logger.debug(`Allowed registries for kind "${kind}": ${allowedRegistries.join(", ")}`);
153
159
  }
154
160
  if (!allowedRegistries.includes(registry)) {
155
161
  if (verbose) {
156
- logger.debug(
157
- `Validation failed: registry "${registry}" not allowed for kind "${kind}"`
158
- );
162
+ logger.debug(`Validation failed: registry "${registry}" not allowed for kind "${kind}"`);
159
163
  }
160
164
  return {
161
165
  valid: false,
@@ -176,9 +180,7 @@ async function validateDistFolder(packagePath, verbose) {
176
180
  const stat = await Bun.file(distPath).stat();
177
181
  const isValid = stat.isDirectory();
178
182
  if (verbose) {
179
- logger.debug(
180
- `Dist folder ${isValid ? "exists and is a directory" : "is not a directory"}`
181
- );
183
+ logger.debug(`Dist folder ${isValid ? "exists and is a directory" : "is not a directory"}`);
182
184
  }
183
185
  return isValid;
184
186
  } catch (error) {
@@ -209,7 +211,7 @@ function validatePackageJsonFields(pkg, _packageName, kind, verbose) {
209
211
  "Package has 'private: true' - cannot publish. Run 'dler build' to prepare the package."
210
212
  );
211
213
  }
212
- if (!pkg.files || !Array.isArray(pkg.files) || pkg.files.length === 0) {
214
+ if (!(pkg.files && Array.isArray(pkg.files)) || pkg.files.length === 0) {
213
215
  errors.push("Missing or empty 'files' field - Run 'dler build' to add it.");
214
216
  } else {
215
217
  if (verbose) {
@@ -225,23 +227,19 @@ function validatePackageJsonFields(pkg, _packageName, kind, verbose) {
225
227
  logger.debug(`Has bin field: ${hasBinField}`);
226
228
  logger.debug(`Has exports field: ${!!pkg.exports}`);
227
229
  }
228
- if (!pkg.exports && !hasBinField) {
230
+ if (!(pkg.exports || hasBinField)) {
229
231
  errors.push("Missing 'exports' field - Run 'dler build' to add it.");
230
232
  }
231
233
  if (!pkg.publishConfig) {
232
234
  errors.push("Missing 'publishConfig' field - Run 'dler build' to add it.");
233
235
  } else if (!pkg.publishConfig.access) {
234
- errors.push(
235
- "Missing 'publishConfig.access' field - Run 'dler build' to add it."
236
- );
236
+ errors.push("Missing 'publishConfig.access' field - Run 'dler build' to add it.");
237
237
  } else if (verbose) {
238
238
  logger.debug(`PublishConfig access: ${pkg.publishConfig.access}`);
239
239
  }
240
240
  if (kind === "cli") {
241
241
  if (!pkg.bin) {
242
- errors.push(
243
- "CLI package missing 'bin' field - Run 'dler build' to add it."
244
- );
242
+ errors.push("CLI package missing 'bin' field - Run 'dler build' to add it.");
245
243
  } else if (typeof pkg.bin === "object") {
246
244
  const binEntries = Object.keys(pkg.bin);
247
245
  if (verbose) {
@@ -279,25 +277,19 @@ async function restoreOriginalDependencies(packagePath, originalDependencies, or
279
277
  if (pkg) {
280
278
  if (originalDependencies !== void 0) {
281
279
  if (verbose) {
282
- logger.debug(
283
- `Restoring ${Object.keys(originalDependencies).length} dependencies`
284
- );
280
+ logger.debug(`Restoring ${Object.keys(originalDependencies).length} dependencies`);
285
281
  }
286
282
  pkg.dependencies = originalDependencies;
287
283
  }
288
284
  if (originalDevDependencies !== void 0) {
289
285
  if (verbose) {
290
- logger.debug(
291
- `Restoring ${Object.keys(originalDevDependencies).length} devDependencies`
292
- );
286
+ logger.debug(`Restoring ${Object.keys(originalDevDependencies).length} devDependencies`);
293
287
  }
294
288
  pkg.devDependencies = originalDevDependencies;
295
289
  }
296
290
  if (originalScripts !== void 0) {
297
291
  if (verbose) {
298
- logger.debug(
299
- `Restoring ${Object.keys(originalScripts).length} scripts`
300
- );
292
+ logger.debug(`Restoring ${Object.keys(originalScripts).length} scripts`);
301
293
  }
302
294
  pkg.scripts = originalScripts;
303
295
  }
@@ -354,9 +346,7 @@ async function resolveWorkspaceVersion(packagePath, depName, workspacePackages,
354
346
  const workspacePkg = workspacePackages.find((pkg) => pkg.name === depName);
355
347
  if (workspacePkg?.pkg?.version) {
356
348
  if (verbose) {
357
- logger.debug(
358
- `Resolved workspace version for ${depName}: ${workspacePkg.pkg.version}`
359
- );
349
+ logger.debug(`Resolved workspace version for ${depName}: ${workspacePkg.pkg.version}`);
360
350
  }
361
351
  return workspacePkg.pkg.version;
362
352
  }
@@ -400,12 +390,11 @@ async function resolveCatalogVersion(packagePath, depName, verbose) {
400
390
  const catalogVersion = catalog[depName];
401
391
  if (catalogVersion) {
402
392
  if (verbose) {
403
- logger.debug(
404
- `Resolved catalog version for ${depName}: ${catalogVersion}`
405
- );
393
+ logger.debug(`Resolved catalog version for ${depName}: ${catalogVersion}`);
406
394
  }
407
395
  return catalogVersion;
408
- } else if (verbose) {
396
+ }
397
+ if (verbose) {
409
398
  logger.debug(`Catalog found but ${depName} not in catalog`);
410
399
  }
411
400
  }
@@ -452,32 +441,30 @@ async function preparePackageForPublishing(packagePath, options, workspacePackag
452
441
  if (options.verbose) {
453
442
  logger.debug("Created backup of original package.json");
454
443
  }
455
- if (shouldBumpVersion && options.bump && !options.bumpDisable && !options.dryRun && pkg.version) {
456
- if (options.verbose) {
457
- logger.debug(`Bumping version: ${pkg.version} -> ${options.bump}`);
458
- }
459
- const bumpResult = bumpVersion(pkg.version, options.bump);
460
- if (!bumpResult) {
444
+ if (shouldBumpVersion && !options.dryRun && bumpedVersions && pkg.name) {
445
+ const bumpedVersion = bumpedVersions.get(pkg.name);
446
+ if (bumpedVersion) {
461
447
  if (options.verbose) {
462
- 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;
463
466
  }
464
- return {
465
- success: false,
466
- error: `Invalid version bump: ${options.bump}`
467
- };
468
- }
469
- if (options.verbose) {
470
- logger.log(
471
- re.blue(
472
- ` Bumping version from ${re.bold(pkg.version)} to ${re.bold(re.green(bumpResult.bumped))} (${options.bump})`
473
- )
474
- );
475
467
  }
476
- pkg.version = bumpResult.bumped;
477
- } else if (options.verbose) {
478
- logger.debug(
479
- `Skipping version bump: shouldBumpVersion=${shouldBumpVersion}, bump=${options.bump}, bumpDisable=${options.bumpDisable}, dryRun=${options.dryRun}`
480
- );
481
468
  }
482
469
  if (!pkg.publishConfig) {
483
470
  pkg.publishConfig = {};
@@ -497,8 +484,8 @@ async function preparePackageForPublishing(packagePath, options, workspacePackag
497
484
  `Stored originals: ${originalDependencies ? Object.keys(originalDependencies).length : 0} deps, ${originalDevDependencies ? Object.keys(originalDevDependencies).length : 0} devDeps, ${originalScripts ? Object.keys(originalScripts).length : 0} scripts`
498
485
  );
499
486
  }
500
- delete pkg.devDependencies;
501
- delete pkg.scripts;
487
+ pkg.devDependencies = void 0;
488
+ pkg.scripts = void 0;
502
489
  if (options.verbose) {
503
490
  logger.debug("Removed devDependencies and scripts for publishing");
504
491
  }
@@ -625,9 +612,7 @@ export async function publishPackage(packagePath, options = {}, bumpedVersions)
625
612
  workspacePackages = await getWorkspacePackages(monorepoRoot);
626
613
  if (options.verbose) {
627
614
  logger.log(
628
- re.blue(
629
- ` Found ${re.bold(workspacePackages.length)} workspace packages`
630
- )
615
+ re.blue(` Found ${re.bold(String(workspacePackages.length))} workspace packages`)
631
616
  );
632
617
  }
633
618
  } else if (options.verbose) {
@@ -702,17 +687,11 @@ export async function publishPackage(packagePath, options = {}, bumpedVersions)
702
687
  await Bun.write(targetPath, content);
703
688
  copiedFiles.push(targetPath);
704
689
  if (options.verbose) {
705
- logger.log(
706
- re.blue(` Copied ${re.bold(fileName)} from monorepo root`)
707
- );
708
- logger.debug(
709
- `Copied ${fileName} from ${sourcePath} to ${targetPath}`
710
- );
690
+ logger.log(re.blue(` Copied ${re.bold(fileName)} from monorepo root`));
691
+ logger.debug(`Copied ${fileName} from ${sourcePath} to ${targetPath}`);
711
692
  }
712
693
  } else if (options.verbose) {
713
- logger.debug(
714
- `${fileName} already exists in package, skipping copy`
715
- );
694
+ logger.debug(`${fileName} already exists in package, skipping copy`);
716
695
  }
717
696
  } else if (options.verbose) {
718
697
  logger.debug(`${fileName} not found in monorepo root`);
@@ -737,9 +716,7 @@ export async function publishPackage(packagePath, options = {}, bumpedVersions)
737
716
  );
738
717
  if (!pkgValidation.valid) {
739
718
  if (options.verbose) {
740
- logger.debug(
741
- `Package validation failed with ${pkgValidation.errors.length} error(s)`
742
- );
719
+ logger.debug(`Package validation failed with ${pkgValidation.errors.length} error(s)`);
743
720
  }
744
721
  return {
745
722
  success: false,
@@ -785,9 +762,7 @@ export async function publishPackage(packagePath, options = {}, bumpedVersions)
785
762
  };
786
763
  }
787
764
  if (options.verbose) {
788
- logger.debug(
789
- `Package prepared successfully, version: ${prepResult.version}`
790
- );
765
+ logger.debug(`Package prepared successfully, version: ${prepResult.version}`);
791
766
  }
792
767
  originalDependencies = prepResult.originalDependencies;
793
768
  originalDevDependencies = prepResult.originalDevDependencies;
@@ -847,16 +822,14 @@ export async function publishPackage(packagePath, options = {}, bumpedVersions)
847
822
  hasShown2FATip = true;
848
823
  logger.log(
849
824
  `
850
- ${re.cyan.bold("\u{1F4A1} 2FA Authentication Tip")}
825
+ ${re.bold(re.cyan("\u{1F4A1} 2FA Authentication Tip"))}
851
826
  If you have 2FA enabled on npm, you may be prompted for a one-time password during publishing (on each publish attempt).
852
827
  ${re.bold("Quick fix:")}
853
828
  When prompted, check the box: "Do not challenge npm publish operations from your IP address for the next 5 minutes"
854
829
  ${re.bold("Permanent solution:")}
855
830
  https://npmjs.com \u2192 Avatar \u2192 Account \u2192 Modify 2FA \u2192 Uncheck "Require two-factor authentication for write actions"
856
831
 
857
- ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
858
-
859
- `)}`
832
+ ${re.bold("Use --skip-tip-2fa to skip this information and the 3s wait.\n\n")}`
860
833
  );
861
834
  await new Promise((resolve2) => setTimeout(resolve2, 3e3));
862
835
  }
@@ -870,9 +843,7 @@ ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
870
843
  if (result.exitCode !== 0) {
871
844
  const errorOutput = result.stderr || result.stdout;
872
845
  if (options.verbose) {
873
- logger.debug(
874
- `Publish command failed with exit code ${result.exitCode}`
875
- );
846
+ logger.debug(`Publish command failed with exit code ${result.exitCode}`);
876
847
  logger.debug(`Error output: ${errorOutput}`);
877
848
  }
878
849
  if (THROW_2FA_ERROR && errorOutput.includes("This operation requires a one-time password.")) {
@@ -888,7 +859,7 @@ ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
888
859
  );
889
860
  }
890
861
  throw new Error(
891
- "2FA authentication required. The publish command cannot read OTP input when output is captured. Please either:\n 1. Set NPM_CONFIG_TOKEN environment variable with your npm token (note: .env files are not automatically loaded - export it in your shell or use a tool like dotenv-cli), or\n 2. Use --with-npm-logs flag to allow interactive OTP input."
862
+ "2FA authentication required. The publish command cannot read OTP input when output is captured. Please either:\n 1. Set NPM_CONFIG_TOKEN environment variable with your npm token (.env files are automatically loaded), or\n 2. Use --with-npm-logs flag to allow interactive OTP input."
892
863
  );
893
864
  }
894
865
  throw new Error(
@@ -897,7 +868,7 @@ ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
897
868
  }
898
869
  const output = result.stdout || result.stderr;
899
870
  if (options.verbose) {
900
- logger.debug(`Publish command succeeded`);
871
+ logger.debug("Publish command succeeded");
901
872
  logger.debug(`Output length: ${output.length} bytes`);
902
873
  }
903
874
  const versionMatch = output.match(/published\s+([^\s]+)/i) || output.match(/@([0-9]+\.[0-9]+\.[0-9]+)/);
@@ -907,22 +878,16 @@ ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
907
878
  }
908
879
  if (options.verbose) {
909
880
  logger.log(
910
- re.green(
911
- `\u2713 Published ${re.bold(packageName)}@${re.bold(version || "unknown")}`
912
- )
881
+ re.green(`\u2713 Published ${re.bold(packageName)}@${re.bold(version || "unknown")}`)
913
882
  );
914
883
  } else {
915
884
  logger.log(
916
- re.green(
917
- `\u2713 Published ${re.bold(packageName)}${version ? `@${re.bold(version)}` : ""}`
918
- )
885
+ re.green(`\u2713 Published ${re.bold(packageName)}${version ? `@${re.bold(version)}` : ""}`)
919
886
  );
920
887
  }
921
888
  } catch (error) {
922
889
  if (options.verbose) {
923
- logger.debug(
924
- `Publish error: ${error instanceof Error ? error.message : String(error)}`
925
- );
890
+ logger.debug(`Publish error: ${error instanceof Error ? error.message : String(error)}`);
926
891
  }
927
892
  throw new Error(
928
893
  `Failed to publish ${packageName}: ${error instanceof Error ? error.message : String(error)}`
@@ -1004,9 +969,7 @@ ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
1004
969
  await Bun.file(copiedFile).unlink();
1005
970
  if (options.verbose) {
1006
971
  const fileName = copiedFile.split(/[/\\]/).pop();
1007
- logger.log(
1008
- re.blue(` Removed copied file: ${re.bold(fileName || "")}`)
1009
- );
972
+ logger.log(re.blue(` Removed copied file: ${re.bold(fileName || "")}`));
1010
973
  logger.debug(`Removed copied file: ${copiedFile}`);
1011
974
  }
1012
975
  } catch (error) {
@@ -1021,6 +984,13 @@ ${re.bold(`Use --skip-tip-2fa to skip this information and the 3s wait.
1021
984
  }
1022
985
  export async function publishAllPackages(cwd, ignore, options = {}) {
1023
986
  try {
987
+ const monorepoRoot = cwd ? resolve(cwd) : await findMonorepoRoot(process.cwd());
988
+ if (monorepoRoot) {
989
+ config({ path: resolve(monorepoRoot, ".env") });
990
+ config({ path: resolve(monorepoRoot, ".env.local") });
991
+ }
992
+ config({ path: resolve(process.cwd(), ".env") });
993
+ config({ path: resolve(process.cwd(), ".env.local") });
1024
994
  if (options.verbose) {
1025
995
  logger.debug(`Starting publishAllPackages, cwd: ${cwd ?? "current"}`);
1026
996
  if (options.filter) {
@@ -1037,6 +1007,33 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1037
1007
  logger.debug("Loading dler.ts configuration...");
1038
1008
  }
1039
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
+ }
1040
1037
  if (options.verbose) {
1041
1038
  logger.debug("Discovering workspace packages...");
1042
1039
  }
@@ -1054,7 +1051,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1054
1051
  const filterPatterns = Array.isArray(options.filter) ? options.filter : [options.filter];
1055
1052
  logger.info(
1056
1053
  re.blue(
1057
- ` 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(", "))}`
1058
1055
  )
1059
1056
  );
1060
1057
  }
@@ -1069,11 +1066,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1069
1066
  warningCount: 0
1070
1067
  };
1071
1068
  }
1072
- logger.info(
1073
- re.blue(
1074
- `Found ${re.bold(filteredPackages.length)} package(s) to publish`
1075
- )
1076
- );
1069
+ logger.info(re.blue(`Found ${re.bold(String(filteredPackages.length))} package(s) to publish`));
1077
1070
  const results = [];
1078
1071
  const concurrency = options.concurrency || 3;
1079
1072
  if (options.verbose) {
@@ -1083,21 +1076,15 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1083
1076
  const packageConfig = getPackagePublishConfig(pkg.name, dlerConfig);
1084
1077
  if (packageConfig?.enable === false) {
1085
1078
  if (options.verbose) {
1086
- logger.info(
1087
- re.yellow(`Skipping ${re.bold(pkg.name)} (disabled in config)`)
1088
- );
1089
- logger.debug(
1090
- `Package config for ${pkg.name}: ${JSON.stringify(packageConfig)}`
1091
- );
1079
+ logger.info(re.yellow(`Skipping ${re.bold(pkg.name)} (disabled in config)`));
1080
+ logger.debug(`Package config for ${pkg.name}: ${JSON.stringify(packageConfig)}`);
1092
1081
  }
1093
1082
  return false;
1094
1083
  }
1095
1084
  return true;
1096
1085
  });
1097
1086
  if (options.verbose) {
1098
- logger.debug(
1099
- `After enable filter: ${packagesToPublish.length} package(s) enabled`
1100
- );
1087
+ logger.debug(`After enable filter: ${packagesToPublish.length} package(s) enabled`);
1101
1088
  }
1102
1089
  if (packagesToPublish.length === 0) {
1103
1090
  logger.warn("No packages enabled for publishing");
@@ -1110,9 +1097,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1110
1097
  };
1111
1098
  }
1112
1099
  logger.info(
1113
- re.blue(
1114
- `Publishing ${re.bold(packagesToPublish.length)} enabled package(s)`
1115
- )
1100
+ re.blue(`Publishing ${re.bold(String(packagesToPublish.length))} enabled package(s)`)
1116
1101
  );
1117
1102
  const bumpedVersions = /* @__PURE__ */ new Map();
1118
1103
  if (options.verbose) {
@@ -1120,41 +1105,63 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1120
1105
  }
1121
1106
  for (const pkg of packagesToPublish) {
1122
1107
  const mergedOptions = mergePublishOptions(options, pkg.name, dlerConfig);
1123
- const bumpType = mergedOptions.bump || (mergedOptions.bumpDisable ? void 0 : "patch");
1124
- if (options.verbose) {
1125
- logger.debug(
1126
- `Pre-bumping ${pkg.name}: current=${pkg.pkg.version}, bumpType=${bumpType ?? "none"}, bumpDisable=${mergedOptions.bumpDisable}`
1127
- );
1128
- }
1129
- if (bumpType && !mergedOptions.bumpDisable && pkg.pkg.version) {
1130
- try {
1131
- const nextVersion = getNextVersion(pkg.pkg.version, bumpType);
1132
- if (nextVersion) {
1133
- bumpedVersions.set(pkg.name, nextVersion);
1134
- if (options.verbose) {
1135
- logger.log(
1136
- re.blue(
1137
- ` ${re.bold(pkg.name)}: ${pkg.pkg.version} -> ${re.green.bold(nextVersion)}`
1138
- )
1139
- );
1140
- }
1141
- } else if (options.verbose) {
1142
- logger.debug(`Failed to calculate next version for ${pkg.name}`);
1143
- }
1144
- } catch (error) {
1145
- if (options.verbose) {
1146
- logger.debug(
1147
- `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"
1148
1117
  );
1118
+ } else {
1119
+ releaseVersion = "1.0.0";
1149
1120
  }
1150
1121
  }
1151
- } else if (pkg.pkg.version) {
1152
- 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");
1153
1132
  if (options.verbose) {
1154
1133
  logger.debug(
1155
- `Using current version for ${pkg.name}: ${pkg.pkg.version}`
1134
+ `Pre-bumping ${pkg.name}: current=${pkg.pkg.version}, bumpType=${bumpType ?? "none"}, bumpDisable=${mergedOptions.bumpDisable}`
1156
1135
  );
1157
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
+ }
1158
1165
  }
1159
1166
  }
1160
1167
  if (options.verbose) {
@@ -1162,9 +1169,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1162
1169
  }
1163
1170
  const totalBatches = Math.ceil(packagesToPublish.length / concurrency);
1164
1171
  if (options.verbose) {
1165
- logger.debug(
1166
- `Processing ${totalBatches} batch(es) with concurrency ${concurrency}`
1167
- );
1172
+ logger.debug(`Processing ${totalBatches} batch(es) with concurrency ${concurrency}`);
1168
1173
  }
1169
1174
  for (let i = 0; i < packagesToPublish.length; i += concurrency) {
1170
1175
  const batch = packagesToPublish.slice(i, i + concurrency);
@@ -1175,18 +1180,12 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1175
1180
  );
1176
1181
  }
1177
1182
  const batchPromises = batch.map(async (pkg) => {
1178
- const mergedOptions = mergePublishOptions(
1179
- options,
1180
- pkg.name,
1181
- dlerConfig
1182
- );
1183
- if (!mergedOptions.bump && !mergedOptions.bumpDisable) {
1183
+ const mergedOptions = mergePublishOptions(options, pkg.name, dlerConfig);
1184
+ if (!(mergedOptions.bump || mergedOptions.bumpDisable)) {
1184
1185
  mergedOptions.bump = "patch";
1185
1186
  }
1186
1187
  if (options.verbose) {
1187
- logger.debug(
1188
- `Merged options for ${pkg.name}: ${JSON.stringify(mergedOptions)}`
1189
- );
1188
+ logger.debug(`Merged options for ${pkg.name}: ${JSON.stringify(mergedOptions)}`);
1190
1189
  }
1191
1190
  if (mergedOptions.verbose) {
1192
1191
  logger.info(re.blue(`Publishing ${re.bold(pkg.name)}...`));
@@ -1205,9 +1204,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1205
1204
  const hasBatchErrors = batchResults.some((r) => !r.success);
1206
1205
  if (hasBatchErrors) {
1207
1206
  if (options.verbose) {
1208
- logger.debug(
1209
- `Batch ${batchNumber} had errors, stopping processing of remaining batches`
1210
- );
1207
+ logger.debug(`Batch ${batchNumber} had errors, stopping processing of remaining batches`);
1211
1208
  }
1212
1209
  break;
1213
1210
  }
@@ -1223,6 +1220,45 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1223
1220
  `Publish all completed: ${successCount} success, ${errorCount} error(s), ${warningCount} warning(s)`
1224
1221
  );
1225
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
+ }
1226
1262
  return {
1227
1263
  results,
1228
1264
  hasErrors,
@@ -1232,9 +1268,7 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1232
1268
  };
1233
1269
  } catch (error) {
1234
1270
  if (options.verbose) {
1235
- logger.debug(
1236
- `Publish all failed: ${error instanceof Error ? error.message : String(error)}`
1237
- );
1271
+ logger.debug(`Publish all failed: ${error instanceof Error ? error.message : String(error)}`);
1238
1272
  }
1239
1273
  logger.error(
1240
1274
  re.red("Failed to publish packages:"),
@@ -1249,3 +1283,12 @@ export async function publishAllPackages(cwd, ignore, options = {}) {
1249
1283
  };
1250
1284
  }
1251
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.8",
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,21 +14,17 @@
9
14
  "default": "./dist/mod.js"
10
15
  }
11
16
  },
12
- "dependencies": {
13
- "c12": "^3.3.3",
14
- "@reliverse/typerso": "2.2.8",
15
- "@reliverse/config": "2.2.8",
16
- "@reliverse/relinka": "2.2.8",
17
- "@reliverse/bump": "2.2.9",
18
- "@reliverse/relico": "2.2.8",
19
- "@reliverse/build": "2.2.8"
20
- },
21
17
  "publishConfig": {
22
18
  "access": "public"
23
19
  },
24
- "files": [
25
- "dist",
26
- "package.json"
27
- ],
28
- "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
+ }
29
30
  }