@bobfrankston/npmglobalize 1.0.138 → 1.0.140

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/git.js CHANGED
@@ -673,6 +673,19 @@ export function pushWithProtection(cwd, verbose) {
673
673
  console.log(colors.green(' ✓ Already up-to-date'));
674
674
  return true;
675
675
  }
676
+ // No upstream branch — auto-set and retry
677
+ if (combined.includes('no upstream branch') || combined.includes('has no upstream')) {
678
+ if (verbose)
679
+ console.log(colors.yellow(' No upstream branch — setting upstream...'));
680
+ const branchResult = runCommand('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, silent: true });
681
+ const branch = branchResult.output.trim() || 'master';
682
+ pushResult = runCommand('git', ['push', '--set-upstream', 'origin', branch], { cwd, silent: true });
683
+ if (pushResult.success) {
684
+ if (verbose)
685
+ console.log(colors.green(' ✓ Pushed to remote (set upstream)'));
686
+ return true;
687
+ }
688
+ }
676
689
  // Transient network errors (HTTP 408, RPC failed, unexpected disconnect) — retry once
677
690
  if (/rpc failed|curl \d+|unexpected disconnect|hung up unexpectedly/i.test(pushResult.stderr)) {
678
691
  console.log(colors.yellow('Git push failed with transient error — retrying...'));
package/lib/types.d.ts CHANGED
@@ -6,16 +6,8 @@ import { type SpawnSyncOptions, type SpawnSyncReturns } from 'child_process';
6
6
  /** Wrapper for spawnSync that avoids DEP0190 (args + shell: true).
7
7
  * When shell is true, joins cmd+args into a single command string. */
8
8
  export declare function spawnSafe(cmd: string, args: string[], options?: SpawnSyncOptions): SpawnSyncReturns<string>;
9
- /** Color/style helpers using Node.js styleText (Node 24+) */
10
- export declare const colors: {
11
- red: (text: string) => string;
12
- yellow: (text: string) => string;
13
- green: (text: string) => string;
14
- italic: (text: string) => string;
15
- blue: (text: string) => string;
16
- cyan: (text: string) => string;
17
- dim: (text: string) => string;
18
- };
9
+ /** Semantic color functions adapts to terminal light/dark theme */
10
+ export declare const colors: import("@bobfrankston/themecolors").SemanticColors;
19
11
  /** Get npm command for current platform (npm.cmd on Windows, npm elsewhere) */
20
12
  export declare function getNpmCommand(): string;
21
13
  /** Options for the globalize operation */
package/lib/types.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * Leaf module — no internal dependencies.
4
4
  */
5
5
  import { spawnSync } from 'child_process';
6
- import { styleText } from 'util';
6
+ import { themeColors } from '@bobfrankston/themecolors';
7
7
  import readline from 'readline';
8
8
  /** Wrapper for spawnSync that avoids DEP0190 (args + shell: true).
9
9
  * When shell is true, joins cmd+args into a single command string. */
@@ -16,16 +16,8 @@ export function spawnSafe(cmd, args, options = {}) {
16
16
  }
17
17
  return spawnSync(cmd, args, opts);
18
18
  }
19
- /** Color/style helpers using Node.js styleText (Node 24+) */
20
- export const colors = {
21
- red: (text) => styleText('red', text),
22
- yellow: (text) => styleText('yellow', text),
23
- green: (text) => styleText('green', text),
24
- italic: (text) => styleText('italic', text),
25
- blue: (text) => styleText('blue', text),
26
- cyan: (text) => styleText('cyan', text),
27
- dim: (text) => styleText('dim', text),
28
- };
19
+ /** Semantic color functions adapts to terminal light/dark theme */
20
+ export const colors = themeColors();
29
21
  /** Get npm command for current platform (npm.cmd on Windows, npm elsewhere) */
30
22
  export function getNpmCommand() {
31
23
  return process.platform === 'win32' ? 'npm.cmd' : 'npm';
package/lib.js CHANGED
@@ -137,6 +137,10 @@ function getNpmCommand() {
137
137
  return process.platform === 'win32' ? 'npm.cmd' : 'npm';
138
138
  }
139
139
  const DEP_KEYS = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
140
+ /** Format current time as HH:MM:SS for progress timestamps */
141
+ function timestamp() {
142
+ return colors.dim(new Date().toLocaleTimeString('en-US', { hour12: false }));
143
+ }
140
144
  /** Read and parse package.json from a directory */
141
145
  export function readPackageJson(dir) {
142
146
  const pkgPath = path.join(dir, 'package.json');
@@ -1003,7 +1007,7 @@ export function fixVersionTagMismatch(cwd, pkg, verbose = false) {
1003
1007
  function waitForNpmVersion(pkgName, version, maxWaitMs = 90000) {
1004
1008
  const interval = 3000;
1005
1009
  const maxAttempts = Math.ceil(maxWaitMs / interval);
1006
- process.stdout.write(`Waiting for ${pkgName}@${version} on npm registry`);
1010
+ process.stdout.write(`${timestamp()} Waiting for ${pkgName}@${version} on npm registry`);
1007
1011
  for (let i = 0; i < maxAttempts; i++) {
1008
1012
  const result = spawnSafe('npm', ['view', `${pkgName}@${version}`, 'version'], {
1009
1013
  shell: process.platform === 'win32',
@@ -1647,6 +1651,20 @@ function pushWithProtection(cwd, verbose) {
1647
1651
  console.log(colors.green(' ✓ Already up-to-date'));
1648
1652
  return true;
1649
1653
  }
1654
+ // No upstream branch — auto-set and retry
1655
+ if (combined.includes('no upstream branch') || combined.includes('has no upstream')) {
1656
+ if (verbose)
1657
+ console.log(colors.yellow(' No upstream branch — setting upstream...'));
1658
+ // Detect current branch name
1659
+ const branchResult = runCommand('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, silent: true });
1660
+ const branch = branchResult.output.trim() || 'master';
1661
+ pushResult = runCommand('git', ['push', '--set-upstream', 'origin', branch], { cwd, silent: true });
1662
+ if (pushResult.success) {
1663
+ if (verbose)
1664
+ console.log(colors.green(' ✓ Pushed to remote (set upstream)'));
1665
+ return true;
1666
+ }
1667
+ }
1650
1668
  // Transient network errors (HTTP 408, RPC failed, unexpected disconnect) — retry once
1651
1669
  if (/rpc failed|curl \d+|unexpected disconnect|hung up unexpectedly/i.test(pushResult.stderr)) {
1652
1670
  console.log(colors.yellow('Git push failed with transient error — retrying...'));
@@ -2623,7 +2641,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
2623
2641
  const pkg = readPackageJson(cwd);
2624
2642
  // Run build step if package.json has a build script (skip if CLI already built)
2625
2643
  if (pkg.scripts?.build && !options._fromCli) {
2626
- console.log('Running build...');
2644
+ console.log(`${timestamp()} Running build...`);
2627
2645
  if (!dryRun) {
2628
2646
  const buildResult = runCommand('npm', ['run', 'build'], { cwd, silent: !verbose });
2629
2647
  if (!buildResult.success) {
@@ -2634,7 +2652,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
2634
2652
  console.log(colors.yellow('Continuing with --force despite build failure...'));
2635
2653
  }
2636
2654
  else {
2637
- console.log(colors.green('✓ Build succeeded'));
2655
+ console.log(`${timestamp()} ${colors.green('✓ Build succeeded')}`);
2638
2656
  }
2639
2657
  }
2640
2658
  else {
@@ -3378,7 +3396,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3378
3396
  // Git operations
3379
3397
  if (currentGitStatus.hasUncommitted) {
3380
3398
  const commitMsg = message || 'Pre-release commit';
3381
- console.log(`Committing changes: ${commitMsg}`);
3399
+ console.log(`${timestamp()} Committing changes: ${commitMsg}`);
3382
3400
  if (!dryRun) {
3383
3401
  // Remove 'nul' files that break git on Windows
3384
3402
  const nulCount = removeNulFiles(cwd);
@@ -3476,7 +3494,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3476
3494
  console.log(`Skipping version bump — ${pkg.version} already set locally.`);
3477
3495
  }
3478
3496
  else {
3479
- console.log(`Bumping version (${bump})...`);
3497
+ console.log(`${timestamp()} Bumping version (${bump})...`);
3480
3498
  }
3481
3499
  if (!dryRun && !skipVersionBump) {
3482
3500
  // Temporarily disable postversion hook to prevent double-publishing or push
@@ -3720,8 +3738,51 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3720
3738
  else if (!skipVersionBump) {
3721
3739
  console.log(` [dry-run] Would run: npm version ${bump}`);
3722
3740
  }
3741
+ // Check for public package depending on private/unpublished scoped dependencies
3742
+ // Only scoped deps can be private — unscoped are always public on npm.
3743
+ // Use a single fast `npm view` per dep instead of the full checkNpmAccess.
3744
+ const publishAccess = effectiveNpmVisibility || currentAccess || (isScoped ? 'restricted' : 'public');
3745
+ if (publishAccess === 'public') {
3746
+ const scopedDeps = [];
3747
+ for (const depKey of ['dependencies', 'peerDependencies']) {
3748
+ const deps = pkg[depKey];
3749
+ if (!deps)
3750
+ continue;
3751
+ for (const depName of Object.keys(deps)) {
3752
+ if (depName.startsWith('@'))
3753
+ scopedDeps.push(depName);
3754
+ }
3755
+ }
3756
+ if (scopedDeps.length > 0) {
3757
+ const privateDeps = [];
3758
+ for (const depName of scopedDeps) {
3759
+ const viewResult = spawnSafe('npm', ['view', depName, 'name'], {
3760
+ encoding: 'utf-8', stdio: 'pipe', shell: true
3761
+ });
3762
+ if (viewResult.status !== 0) {
3763
+ const stderr = viewResult.stderr || '';
3764
+ if (stderr.includes('404') || stderr.includes('not found')) {
3765
+ privateDeps.push(`${depName} (not on npm)`);
3766
+ }
3767
+ else {
3768
+ privateDeps.push(`${depName} (restricted)`);
3769
+ }
3770
+ }
3771
+ }
3772
+ if (privateDeps.length > 0) {
3773
+ console.log('');
3774
+ console.error(colors.red('WARNING: Public package depends on PRIVATE/missing dependencies:'));
3775
+ for (const d of privateDeps) {
3776
+ console.error(colors.red(` ${d}`));
3777
+ }
3778
+ console.error(colors.yellow('Consumers will get E404 when installing this package.'));
3779
+ console.error(colors.yellow('Fix: publish those deps as public, or make this package private.'));
3780
+ console.log('');
3781
+ }
3782
+ }
3783
+ }
3723
3784
  // Publish
3724
- console.log('Publishing to npm...');
3785
+ console.log(`${timestamp()} Publishing to npm...`);
3725
3786
  if (!dryRun) {
3726
3787
  // Check npm authentication right before publishing
3727
3788
  const authStatus = checkNpmAuth();
@@ -3885,7 +3946,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3885
3946
  // Determine what was published
3886
3947
  const finalAccess = effectiveNpmVisibility || currentAccess || (isScoped ? 'restricted' : 'public');
3887
3948
  const accessLabel = (finalAccess === 'restricted' || finalAccess === 'private') ? 'PRIVATE' : 'PUBLIC';
3888
- console.log(colors.green(`✓ Published to npm as ${accessLabel}`));
3949
+ console.log(`${timestamp()} ${colors.green(`✓ Published to npm as ${accessLabel}`)}`);
3889
3950
  }
3890
3951
  else {
3891
3952
  console.log(` [dry-run] Would run: npm publish ${quiet ? '--quiet' : ''}`);
@@ -3893,7 +3954,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3893
3954
  // Push to git (with push-protection detection and auto-bypass)
3894
3955
  if (currentGitStatus.hasRemote) {
3895
3956
  if (verbose) {
3896
- console.log('Pushing to git...');
3957
+ console.log(`${timestamp()} Pushing to git...`);
3897
3958
  }
3898
3959
  if (!dryRun) {
3899
3960
  if (pushWithProtection(cwd, verbose)) {
@@ -3957,7 +4018,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
3957
4018
  }
3958
4019
  }
3959
4020
  else {
3960
- console.log(`Installing globally from registry: ${pkgName}@${pkgVersion}...`);
4021
+ console.log(`${timestamp()} Installing globally from registry: ${pkgName}@${pkgVersion}...`);
3961
4022
  if (!dryRun) {
3962
4023
  waitForNpmVersion(pkgName, pkgVersion);
3963
4024
  const installResult = installGlobalWithRetry(`${pkgName}@${pkgVersion}`, cwd);
@@ -4028,7 +4089,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
4028
4089
  else if (!files) {
4029
4090
  console.log('Keeping npm versions (--nofiles mode).');
4030
4091
  }
4031
- console.log('Done!');
4092
+ console.log(`${timestamp()} Done!`);
4032
4093
  // Run final audit report if not already run
4033
4094
  const auditAlreadyRun = (fix || updateDeps) && (transformResult.transformed || alreadyTransformed || updateDeps);
4034
4095
  if (!auditAlreadyRun && (fix || updateDeps || transformResult.transformed) && !dryRun) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/npmglobalize",
3
- "version": "1.0.138",
3
+ "version": "1.0.140",
4
4
  "description": "Transform file: dependencies to npm versions for publishing",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -33,8 +33,8 @@
33
33
  "dependencies": {
34
34
  "@bobfrankston/freezepak": "^0.1.6",
35
35
  "@bobfrankston/importgen": "^0.1.32",
36
- "@bobfrankston/themecolors": "^0.1.0",
37
- "@bobfrankston/userconfig": "^1.0.5",
36
+ "@bobfrankston/themecolors": "^0.1.4",
37
+ "@bobfrankston/userconfig": "^1.0.6",
38
38
  "@npmcli/package-json": "^7.0.4",
39
39
  "json5": "^2.2.3",
40
40
  "libnpmversion": "^8.0.3",