@bobfrankston/npmglobalize 1.0.19 → 1.0.21

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.
Files changed (4) hide show
  1. package/cli.js +64 -10
  2. package/lib.d.ts +39 -2
  3. package/lib.js +348 -16
  4. package/package.json +1 -1
package/cli.js CHANGED
@@ -3,6 +3,9 @@
3
3
  * npmglobalize CLI - Transform file: dependencies to npm versions for publishing
4
4
  */
5
5
  import { globalize, readConfig } from './lib.js';
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+ import { fileURLToPath } from 'url';
6
9
  function printHelp() {
7
10
  console.log(`
8
11
  npmglobalize - Transform file: dependencies to npm versions for publishing
@@ -13,9 +16,16 @@ Release Options:
13
16
  --patch Bump patch version (default)
14
17
  --minor Bump minor version
15
18
  --major Bump major version
16
- --apply Just transform, don't publish
19
+ --nopublish, -np Just transform, don't publish
17
20
  --cleanup Restore from .dependencies
18
21
 
22
+ Dependency Options:
23
+ --update-deps Update package.json to latest (minor/patch only, safe)
24
+ --update-major Allow major version updates (breaking changes)
25
+ --no-publish-deps, -npd Don't auto-publish file: dependencies (use with caution)
26
+ --force-publish Republish dependencies even if version exists
27
+ --fix Run npm audit fix after transformation
28
+
19
29
  Install Options:
20
30
  --install Global install after publish (Windows)
21
31
  --wsl Also install globally in WSL
@@ -39,20 +49,27 @@ Other Options:
39
49
  --conform Update .gitignore/.npmignore to best practices
40
50
  --asis Skip ignore file checks (or set "asis": true in .globalize.json5)
41
51
  --help, -h Show this help
52
+ --version, -v Show version number
42
53
 
43
54
  Examples:
44
- npmglobalize Transform + release (patch)
45
- npmglobalize --minor Release with minor version bump
46
- npmglobalize --install --wsl Release + install on Windows and WSL
47
- npmglobalize --apply Just transform, no publish
48
- npmglobalize --cleanup Restore original dependencies
49
- npmglobalize --init Initialize new git repo + release
50
- npmglobalize --dry-run Preview what would happen
55
+ npmglobalize Transform + publish (auto-publishes file: deps)
56
+ npmglobalize --minor Release with minor version bump
57
+ npmglobalize --update-deps Safe updates (minor/patch only)
58
+ npmglobalize --update-major Allow breaking changes (major updates)
59
+ npmglobalize -npd Skip auto-publishing file: deps (use with caution)
60
+ npmglobalize --force-publish Force republish all file: dependencies
61
+ npmglobalize --fix Fix security vulnerabilities
62
+ npmglobalize --install --wsl Release + install on Windows and WSL
63
+ npmglobalize -np Just transform, no publish
64
+ npmglobalize --cleanup Restore original dependencies
65
+ npmglobalize --init Initialize new git repo + release
66
+ npmglobalize --dry-run Preview what would happen
51
67
  `);
52
68
  }
53
69
  function parseArgs(args) {
54
70
  const options = {
55
71
  help: false,
72
+ version: false,
56
73
  error: ''
57
74
  };
58
75
  const unrecognized = [];
@@ -63,6 +80,10 @@ function parseArgs(args) {
63
80
  case '-h':
64
81
  options.help = true;
65
82
  break;
83
+ case '--version':
84
+ case '-v':
85
+ options.version = true;
86
+ break;
66
87
  case '--patch':
67
88
  options.bump = 'patch';
68
89
  break;
@@ -72,8 +93,10 @@ function parseArgs(args) {
72
93
  case '--major':
73
94
  options.bump = 'major';
74
95
  break;
75
- case '--apply':
76
- options.applyOnly = true;
96
+ case '--nopublish':
97
+ case '-np':
98
+ case '--apply': // Keep for backward compatibility
99
+ options.noPublish = true;
77
100
  break;
78
101
  case '--cleanup':
79
102
  options.cleanup = true;
@@ -148,6 +171,29 @@ function parseArgs(args) {
148
171
  case '--asis':
149
172
  options.asis = true;
150
173
  break;
174
+ case '--update-deps':
175
+ options.updateDeps = true;
176
+ break;
177
+ case '--update-major':
178
+ options.updateMajor = true;
179
+ options.updateDeps = true; // Implies --update-deps
180
+ break;
181
+ case '--publish-deps':
182
+ options.publishDeps = true; // Explicitly enable (though it's default)
183
+ break;
184
+ case '--no-publish-deps':
185
+ case '-npd':
186
+ options.publishDeps = false; // Disable auto-publishing
187
+ break;
188
+ case '--force-publish':
189
+ options.forcePublish = true;
190
+ break;
191
+ case '--fix':
192
+ options.fix = true;
193
+ break;
194
+ case '--no-fix':
195
+ options.fix = false;
196
+ break;
151
197
  default:
152
198
  if (arg.startsWith('-')) {
153
199
  unrecognized.push(arg);
@@ -167,6 +213,14 @@ export async function main() {
167
213
  printHelp();
168
214
  process.exit(0);
169
215
  }
216
+ if (cliOptions.version) {
217
+ const __filename = fileURLToPath(import.meta.url);
218
+ const __dirname = path.dirname(__filename);
219
+ const pkgPath = path.join(__dirname, 'package.json');
220
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
221
+ console.log(pkg.version);
222
+ process.exit(0);
223
+ }
170
224
  if (cliOptions.error) {
171
225
  console.error(`Error: ${cliOptions.error}`);
172
226
  console.error('Run with --help for usage.');
package/lib.d.ts CHANGED
@@ -6,7 +6,7 @@ export interface GlobalizeOptions {
6
6
  /** Bump type: patch (default), minor, major */
7
7
  bump?: 'patch' | 'minor' | 'major';
8
8
  /** Just transform, don't publish */
9
- applyOnly?: boolean;
9
+ noPublish?: boolean;
10
10
  /** Restore from .dependencies */
11
11
  cleanup?: boolean;
12
12
  /** Global install after publish */
@@ -35,6 +35,16 @@ export interface GlobalizeOptions {
35
35
  conform?: boolean;
36
36
  /** Keep ignore files as-is without checking */
37
37
  asis?: boolean;
38
+ /** Check and update existing npm dependencies to latest versions */
39
+ updateDeps?: boolean;
40
+ /** Allow major version updates (breaking changes) */
41
+ updateMajor?: boolean;
42
+ /** Publish file: dependencies before converting them */
43
+ publishDeps?: boolean;
44
+ /** Force republish dependencies even if version exists on npm */
45
+ forcePublish?: boolean;
46
+ /** Run npm audit and fix vulnerabilities */
47
+ fix?: boolean;
38
48
  }
39
49
  /** Read and parse package.json from a directory */
40
50
  export declare function readPackageJson(dir: string): any;
@@ -48,6 +58,20 @@ export declare function writePackageJson(dir: string, pkg: any): void;
48
58
  export declare function resolveFilePath(fileRef: string, baseDir: string): string;
49
59
  /** Check if a dependency value is a file: reference */
50
60
  export declare function isFileRef(value: string): boolean;
61
+ /** Get the latest version of a package from npm */
62
+ export declare function getLatestVersion(packageName: string): string | null;
63
+ /** Check if a specific version of a package exists on npm */
64
+ export declare function checkVersionExists(packageName: string, version: string): boolean;
65
+ /** Update existing npm dependencies to latest versions */
66
+ export declare function updateNpmDeps(pkg: any, verbose?: boolean, allowMajor?: boolean): {
67
+ updated: boolean;
68
+ changes: string[];
69
+ majorAvailable: Array<{
70
+ name: string;
71
+ current: string;
72
+ latest: string;
73
+ }>;
74
+ };
51
75
  /** Get all file: dependencies from package.json */
52
76
  export declare function getFileRefs(pkg: any): Map<string, {
53
77
  key: string;
@@ -55,7 +79,14 @@ export declare function getFileRefs(pkg: any): Map<string, {
55
79
  value: string;
56
80
  }>;
57
81
  /** Transform file: dependencies to npm versions */
58
- export declare function transformDeps(pkg: any, baseDir: string, verbose?: boolean): boolean;
82
+ export declare function transformDeps(pkg: any, baseDir: string, verbose?: boolean, forcePublish?: boolean): {
83
+ transformed: boolean;
84
+ unpublished: Array<{
85
+ name: string;
86
+ version: string;
87
+ path: string;
88
+ }>;
89
+ };
59
90
  /** Restore file: dependencies from .dependencies */
60
91
  export declare function restoreDeps(pkg: any, verbose?: boolean): boolean;
61
92
  /** Check if .dependencies exist (already transformed) */
@@ -94,6 +125,12 @@ export declare function confirm(message: string, defaultYes?: boolean): Promise<
94
125
  /** Initialize git repository */
95
126
  export declare function initGit(cwd: string, visibility: 'private' | 'public', dryRun: boolean): Promise<boolean>;
96
127
  /** Main globalize function */
128
+ /** Run npm audit and optionally fix vulnerabilities */
129
+ export declare function runNpmAudit(cwd: string, fix?: boolean, verbose?: boolean): {
130
+ success: boolean;
131
+ report: string;
132
+ hasVulnerabilities: boolean;
133
+ };
97
134
  export declare function globalize(cwd: string, options?: GlobalizeOptions): Promise<boolean>;
98
135
  declare const _default: {
99
136
  globalize: typeof globalize;
package/lib.js CHANGED
@@ -57,7 +57,7 @@ export function writeConfig(dir, config, explicitKeys) {
57
57
  const existing = readConfig(dir);
58
58
  // Filter out temporary flags and default values (unless explicitly set)
59
59
  const filtered = {};
60
- const omitKeys = new Set(['applyOnly', 'cleanup', 'init', 'dryRun', 'message', 'conform', 'asis', 'help', 'error']);
60
+ const omitKeys = new Set(['noPublish', 'cleanup', 'init', 'dryRun', 'message', 'conform', 'asis', 'help', 'error', 'updateDeps', 'updateMajor', 'publishDeps', 'forcePublish']);
61
61
  for (const [key, value] of Object.entries(config)) {
62
62
  if (omitKeys.has(key))
63
63
  continue;
@@ -102,6 +102,8 @@ export function writeConfig(dir, config, explicitKeys) {
102
102
  comment = ' // private or public (default)';
103
103
  else if (key === 'bump')
104
104
  comment = ' // patch (default), minor, or major';
105
+ else if (key === 'fix')
106
+ comment = ' // Auto-run npm audit fix';
105
107
  lines.push(` "${key}": ${jsonValue}${comma}${comment}`);
106
108
  });
107
109
  lines.push('');
@@ -117,6 +119,7 @@ export function writeConfig(dir, config, explicitKeys) {
117
119
  lines.push(' // "verbose": false // Show detailed output');
118
120
  lines.push(' // "gitVisibility": "private" // Git repo: private or public');
119
121
  lines.push(' // "npmVisibility": "public" // npm package: private or public');
122
+ lines.push(' // "fix": false // Auto-run npm audit fix');
120
123
  lines.push('}');
121
124
  fs.writeFileSync(configPath, lines.join('\n') + '\n');
122
125
  }
@@ -134,6 +137,119 @@ export function resolveFilePath(fileRef, baseDir) {
134
137
  export function isFileRef(value) {
135
138
  return typeof value === 'string' && value.startsWith('file:');
136
139
  }
140
+ /** Get the latest version of a package from npm */
141
+ export function getLatestVersion(packageName) {
142
+ try {
143
+ const result = spawnSync('npm', ['view', packageName, 'version'], {
144
+ encoding: 'utf-8',
145
+ stdio: 'pipe',
146
+ shell: true
147
+ });
148
+ if (result.status === 0 && result.stdout) {
149
+ return result.stdout.trim();
150
+ }
151
+ return null;
152
+ }
153
+ catch (error) {
154
+ return null;
155
+ }
156
+ }
157
+ /** Check if a specific version of a package exists on npm */
158
+ export function checkVersionExists(packageName, version) {
159
+ try {
160
+ const result = spawnSync('npm', ['view', `${packageName}@${version}`, 'version'], {
161
+ encoding: 'utf-8',
162
+ stdio: 'pipe',
163
+ shell: true
164
+ });
165
+ return result.status === 0 && result.stdout.trim() === version;
166
+ }
167
+ catch (error) {
168
+ return false;
169
+ }
170
+ }
171
+ /** Parse semver version string to major.minor.patch */
172
+ function parseSemver(version) {
173
+ const clean = version.replace(/^[^\d]*/, ''); // Remove ^, ~, etc.
174
+ const match = clean.match(/^(\d+)\.(\d+)\.(\d+)/);
175
+ if (!match)
176
+ return null;
177
+ return {
178
+ major: parseInt(match[1], 10),
179
+ minor: parseInt(match[2], 10),
180
+ patch: parseInt(match[3], 10)
181
+ };
182
+ }
183
+ /** Check if update is within semver range (^1.0.0 allows minor/patch, not major) */
184
+ function isCompatibleUpdate(currentSpec, latestVersion) {
185
+ const current = parseSemver(currentSpec);
186
+ const latest = parseSemver(latestVersion);
187
+ if (!current || !latest) {
188
+ return { compatible: true, isMajor: false }; // Can't parse, allow update
189
+ }
190
+ const isMajor = latest.major > current.major;
191
+ const prefix = currentSpec.match(/^[^\d]*/) ? currentSpec.match(/^[^\d]*/)[0] : '^';
192
+ // ^ allows minor and patch updates within same major version
193
+ // ~ allows only patch updates
194
+ if (prefix === '^') {
195
+ return { compatible: !isMajor, isMajor };
196
+ }
197
+ else if (prefix === '~') {
198
+ return { compatible: latest.major === current.major && latest.minor === current.minor, isMajor };
199
+ }
200
+ // For exact versions or other formats, allow all updates
201
+ return { compatible: true, isMajor };
202
+ }
203
+ /** Update existing npm dependencies to latest versions */
204
+ export function updateNpmDeps(pkg, verbose = false, allowMajor = false) {
205
+ const changes = [];
206
+ const majorAvailable = [];
207
+ let updated = false;
208
+ for (const key of DEP_KEYS) {
209
+ if (!pkg[key])
210
+ continue;
211
+ for (const [name, value] of Object.entries(pkg[key])) {
212
+ // Skip file: references - those are handled separately
213
+ if (isFileRef(value))
214
+ continue;
215
+ // Get current and latest versions
216
+ const currentSpec = value;
217
+ const latest = getLatestVersion(name);
218
+ if (latest) {
219
+ const { compatible, isMajor } = isCompatibleUpdate(currentSpec, latest);
220
+ const newSpec = '^' + latest;
221
+ if (currentSpec !== newSpec) {
222
+ if (isMajor && !allowMajor) {
223
+ // Major update available but not allowed
224
+ majorAvailable.push({ name, current: currentSpec, latest: newSpec });
225
+ if (verbose) {
226
+ console.log(colors.yellow(` ${name}: ${currentSpec} (major update ${newSpec} available, use --update-major)`));
227
+ }
228
+ }
229
+ else if (compatible || allowMajor) {
230
+ // Safe update or major updates allowed
231
+ pkg[key][name] = newSpec;
232
+ changes.push(`${name}: ${currentSpec} → ${newSpec}`);
233
+ if (isMajor) {
234
+ console.log(colors.red(` ${name}: ${currentSpec} → ${newSpec} (MAJOR)`));
235
+ }
236
+ else {
237
+ console.log(colors.yellow(` ${name}: ${currentSpec} → ${newSpec}`));
238
+ }
239
+ updated = true;
240
+ }
241
+ }
242
+ else if (verbose) {
243
+ console.log(colors.green(` ${name}: ${currentSpec} is up to date`));
244
+ }
245
+ }
246
+ else if (verbose) {
247
+ console.log(colors.italic(` ${name}: couldn't check npm registry`));
248
+ }
249
+ }
250
+ }
251
+ return { updated, changes, majorAvailable };
252
+ }
137
253
  /** Get all file: dependencies from package.json */
138
254
  export function getFileRefs(pkg) {
139
255
  const refs = new Map();
@@ -149,8 +265,9 @@ export function getFileRefs(pkg) {
149
265
  return refs;
150
266
  }
151
267
  /** Transform file: dependencies to npm versions */
152
- export function transformDeps(pkg, baseDir, verbose = false) {
268
+ export function transformDeps(pkg, baseDir, verbose = false, forcePublish = false) {
153
269
  let transformed = false;
270
+ const unpublished = [];
154
271
  for (const key of DEP_KEYS) {
155
272
  if (!pkg[key])
156
273
  continue;
@@ -176,7 +293,22 @@ export function transformDeps(pkg, baseDir, verbose = false) {
176
293
  const targetPath = resolveFilePath(value, baseDir);
177
294
  try {
178
295
  const targetPkg = readPackageJson(targetPath);
179
- const npmVersion = '^' + targetPkg.version;
296
+ const targetVersion = targetPkg.version;
297
+ const npmVersion = '^' + targetVersion;
298
+ // Check if this version exists on npm (or if force publish)
299
+ const versionExists = forcePublish ? false : checkVersionExists(name, targetVersion);
300
+ if (!versionExists) {
301
+ unpublished.push({ name, version: targetVersion, path: targetPath });
302
+ if (forcePublish) {
303
+ console.log(colors.yellow(` ⟳ ${name}@${targetVersion} will be republished (--force-publish)`));
304
+ }
305
+ else {
306
+ console.log(colors.red(` ⚠ ${name}@${targetVersion} not found on npm (local: ${value})`));
307
+ }
308
+ }
309
+ else if (verbose) {
310
+ console.log(colors.green(` ✓ ${name}@${targetVersion} exists on npm`));
311
+ }
180
312
  pkg[key][name] = npmVersion;
181
313
  if (verbose) {
182
314
  console.log(` ${name}: ${value} -> ${npmVersion}`);
@@ -189,7 +321,7 @@ export function transformDeps(pkg, baseDir, verbose = false) {
189
321
  }
190
322
  }
191
323
  }
192
- return transformed;
324
+ return { transformed, unpublished };
193
325
  }
194
326
  /** Restore file: dependencies from .dependencies */
195
327
  export function restoreDeps(pkg, verbose = false) {
@@ -523,6 +655,20 @@ function checkIgnoreFiles(cwd, options) {
523
655
  }
524
656
  /** Update ignore files to conform to best practices */
525
657
  function conformIgnoreFiles(cwd) {
658
+ // Update .gitattributes for LF line endings
659
+ const gitattributesPath = path.join(cwd, '.gitattributes');
660
+ const gitattributesContent = '* text=auto eol=lf\n';
661
+ if (!fs.existsSync(gitattributesPath)) {
662
+ fs.writeFileSync(gitattributesPath, gitattributesContent);
663
+ console.log(colors.green(' ✓ Created .gitattributes (LF line endings)'));
664
+ }
665
+ else {
666
+ const content = fs.readFileSync(gitattributesPath, 'utf-8');
667
+ if (!content.includes('eol=lf')) {
668
+ fs.writeFileSync(gitattributesPath, gitattributesContent);
669
+ console.log(colors.green(' ✓ Updated .gitattributes (LF line endings)'));
670
+ }
671
+ }
526
672
  // Update .gitignore
527
673
  const gitignorePath = path.join(cwd, '.gitignore');
528
674
  if (fs.existsSync(gitignorePath)) {
@@ -680,8 +826,100 @@ export async function initGit(cwd, visibility, dryRun) {
680
826
  return true;
681
827
  }
682
828
  /** Main globalize function */
829
+ /** Run npm audit and optionally fix vulnerabilities */
830
+ export function runNpmAudit(cwd, fix = false, verbose = false) {
831
+ console.log('');
832
+ console.log(colors.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
833
+ console.log(colors.yellow('🔒 npm audit'));
834
+ console.log(colors.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
835
+ if (fix) {
836
+ console.log('Running npm audit fix...');
837
+ const fixResult = runCommand('npm', ['audit', 'fix'], { cwd, silent: false, shell: true });
838
+ if (!fixResult.success) {
839
+ console.log(colors.yellow('⚠ Some vulnerabilities could not be automatically fixed'));
840
+ }
841
+ else {
842
+ console.log(colors.green('✓ Audit fixes applied'));
843
+ }
844
+ }
845
+ // Always run audit to report status
846
+ const auditResult = spawnSync('npm', ['audit', '--json'], {
847
+ cwd,
848
+ encoding: 'utf-8',
849
+ stdio: 'pipe',
850
+ shell: true
851
+ });
852
+ let hasVulnerabilities = false;
853
+ let report = '';
854
+ if (auditResult.status !== 0 || auditResult.stdout) {
855
+ try {
856
+ const auditData = JSON.parse(auditResult.stdout || '{}');
857
+ const { vulnerabilities = {} } = auditData;
858
+ const critical = auditData.metadata?.vulnerabilities?.critical || 0;
859
+ const high = auditData.metadata?.vulnerabilities?.high || 0;
860
+ const moderate = auditData.metadata?.vulnerabilities?.moderate || 0;
861
+ const low = auditData.metadata?.vulnerabilities?.low || 0;
862
+ const info = auditData.metadata?.vulnerabilities?.info || 0;
863
+ const total = critical + high + moderate + low + info;
864
+ if (total > 0) {
865
+ hasVulnerabilities = true;
866
+ report = `Found ${total} vulnerabilities`;
867
+ const parts = [];
868
+ if (critical > 0)
869
+ parts.push(colors.red(`${critical} critical`));
870
+ if (high > 0)
871
+ parts.push(colors.red(`${high} high`));
872
+ if (moderate > 0)
873
+ parts.push(colors.yellow(`${moderate} moderate`));
874
+ if (low > 0)
875
+ parts.push(`${low} low`);
876
+ if (info > 0)
877
+ parts.push(`${info} info`);
878
+ console.log('');
879
+ console.log(colors.yellow(`Vulnerabilities: ${parts.join(', ')}`));
880
+ if (verbose && Object.keys(vulnerabilities).length > 0) {
881
+ console.log('');
882
+ console.log('Details:');
883
+ for (const [pkg, data] of Object.entries(vulnerabilities)) {
884
+ const vulnData = data;
885
+ const severity = vulnData.severity || 'unknown';
886
+ const severityColor = severity === 'critical' || severity === 'high' ? colors.red :
887
+ severity === 'moderate' ? colors.yellow : (s) => s;
888
+ console.log(` ${severityColor(severity)}: ${pkg}`);
889
+ }
890
+ }
891
+ if (!fix) {
892
+ console.log('');
893
+ console.log(colors.yellow('Run with --fix to automatically fix vulnerabilities'));
894
+ }
895
+ }
896
+ else {
897
+ console.log(colors.green('✓ No vulnerabilities found'));
898
+ report = 'No vulnerabilities';
899
+ }
900
+ }
901
+ catch (e) {
902
+ // Fallback to text output if JSON parsing fails
903
+ console.log('Running text audit...');
904
+ const textResult = runCommand('npm', ['audit'], { cwd, silent: false, shell: true });
905
+ report = 'Audit completed (see output above)';
906
+ }
907
+ }
908
+ else {
909
+ console.log(colors.green('✓ No vulnerabilities found'));
910
+ report = 'No vulnerabilities';
911
+ }
912
+ console.log(colors.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
913
+ console.log('');
914
+ return {
915
+ success: true,
916
+ report,
917
+ hasVulnerabilities
918
+ };
919
+ }
683
920
  export async function globalize(cwd, options = {}) {
684
- const { bump = 'patch', applyOnly = false, cleanup = false, install = false, wsl = false, force = false, files = true, dryRun = false, quiet = true, verbose = false, init = false, gitVisibility = 'private', npmVisibility = 'public', message, conform = false, asis = false } = options;
921
+ const { bump = 'patch', noPublish = false, cleanup = false, install = false, wsl = false, force = false, files = true, dryRun = false, quiet = true, verbose = false, init = false, gitVisibility = 'private', npmVisibility = 'public', message, conform = false, asis = false, updateDeps = false, updateMajor = false, publishDeps = true, // Default to publishing deps for safety
922
+ forcePublish = false, fix = false } = options;
685
923
  // Check ignore files first (unless cleanup mode)
686
924
  if (!cleanup && !asis) {
687
925
  const checkResult = checkIgnoreFiles(cwd, { conform, asis, verbose });
@@ -727,8 +965,8 @@ export async function globalize(cwd, options = {}) {
727
965
  }
728
966
  return true;
729
967
  }
730
- // Check npm authentication early (unless dry-run or apply-only)
731
- if (!dryRun && !applyOnly) {
968
+ // Check npm authentication early (unless dry-run or no-publish)
969
+ if (!dryRun && !noPublish) {
732
970
  const authStatus = checkNpmAuth();
733
971
  if (!authStatus.authenticated) {
734
972
  console.error(colors.red('\n✗ npm authentication required'));
@@ -854,15 +1092,90 @@ export async function globalize(cwd, options = {}) {
854
1092
  // Default is public - just inform
855
1093
  console.log(`Will publish '${pkg.name}' to PUBLIC npm registry (default).`);
856
1094
  }
1095
+ // Update existing npm dependencies if requested
1096
+ if (updateDeps) {
1097
+ console.log('Checking for dependency updates...');
1098
+ const updateResult = updateNpmDeps(pkg, verbose, updateMajor);
1099
+ if (updateResult.updated) {
1100
+ console.log(colors.green(`Updated ${updateResult.changes.length} dependencies`));
1101
+ if (!dryRun) {
1102
+ writePackageJson(cwd, pkg);
1103
+ }
1104
+ }
1105
+ else {
1106
+ console.log('All npm dependencies are up to date.');
1107
+ }
1108
+ // Show summary of available major updates
1109
+ if (updateResult.majorAvailable.length > 0 && !updateMajor) {
1110
+ console.log('');
1111
+ console.log(colors.yellow(`${updateResult.majorAvailable.length} major update(s) available (use --update-major):`));
1112
+ for (const { name, current, latest } of updateResult.majorAvailable) {
1113
+ console.log(colors.yellow(` ${name}: ${current} → ${latest}`));
1114
+ }
1115
+ }
1116
+ }
857
1117
  // Transform dependencies
858
1118
  console.log('Transforming file: dependencies...');
859
1119
  const alreadyTransformed = hasBackup(pkg);
860
1120
  if (alreadyTransformed) {
861
1121
  console.log(' Already transformed (found .dependencies)');
862
1122
  }
863
- const transformed = transformDeps(pkg, cwd, verbose);
864
- if (transformed) {
1123
+ const transformResult = transformDeps(pkg, cwd, verbose, forcePublish);
1124
+ if (transformResult.transformed) {
865
1125
  console.log(' Dependencies transformed.');
1126
+ // Check if target packages need to be published
1127
+ if (transformResult.unpublished.length > 0) {
1128
+ console.log('');
1129
+ console.log(colors.red('⚠ WARNING: Some file: dependencies are not published on npm:'));
1130
+ for (const { name, version, path } of transformResult.unpublished) {
1131
+ console.log(colors.yellow(` ${name}@${version} (${path})`));
1132
+ }
1133
+ console.log('');
1134
+ if (publishDeps) {
1135
+ const action = forcePublish ? 'Publishing/updating' : 'Publishing';
1136
+ console.log(`${action} file: dependencies first (--publish-deps)...`);
1137
+ for (const { name, version, path } of transformResult.unpublished) {
1138
+ console.log('');
1139
+ console.log(colors.yellow(`━━━ Publishing ${name}@${version} ━━━`));
1140
+ if (!dryRun) {
1141
+ // Recursively call globalize on the dependency
1142
+ const depSuccess = await globalize(path, {
1143
+ bump: 'patch', // Use existing version, don't bump
1144
+ verbose,
1145
+ quiet,
1146
+ force,
1147
+ files,
1148
+ gitVisibility,
1149
+ npmVisibility
1150
+ });
1151
+ if (!depSuccess) {
1152
+ console.error(colors.red(`Failed to publish ${name}`));
1153
+ if (!force) {
1154
+ return false;
1155
+ }
1156
+ }
1157
+ }
1158
+ else {
1159
+ console.log(` [dry-run] Would publish ${name}`);
1160
+ }
1161
+ }
1162
+ console.log('');
1163
+ console.log(colors.green('✓ All dependencies published'));
1164
+ }
1165
+ else {
1166
+ console.log(colors.yellow('Options:'));
1167
+ console.log(colors.yellow(' 1. Publish them manually first'));
1168
+ console.log(colors.yellow(' 2. Use --publish-deps to publish them automatically'));
1169
+ console.log(colors.yellow(' 3. Use --force to continue anyway (NOT RECOMMENDED)'));
1170
+ console.log('');
1171
+ if (!force) {
1172
+ const shouldContinue = await confirm('Continue with unpublished dependencies?', false);
1173
+ if (!shouldContinue) {
1174
+ return false;
1175
+ }
1176
+ }
1177
+ }
1178
+ }
866
1179
  if (!dryRun) {
867
1180
  writePackageJson(cwd, pkg);
868
1181
  }
@@ -870,8 +1183,21 @@ export async function globalize(cwd, options = {}) {
870
1183
  else if (!alreadyTransformed) {
871
1184
  console.log(' No file: dependencies found.');
872
1185
  }
873
- if (applyOnly) {
874
- console.log('Transform complete (--apply mode).');
1186
+ // Run npm audit if requested or if dependencies were transformed
1187
+ if ((fix || updateDeps) && (transformResult.transformed || alreadyTransformed || updateDeps)) {
1188
+ if (!dryRun) {
1189
+ runNpmAudit(cwd, fix, verbose);
1190
+ }
1191
+ else {
1192
+ console.log(' [dry-run] Would run npm audit');
1193
+ }
1194
+ }
1195
+ else if (fix && !dryRun) {
1196
+ // Run fix even if no deps changed
1197
+ runNpmAudit(cwd, fix, verbose);
1198
+ }
1199
+ if (noPublish) {
1200
+ console.log('Transform complete (--nopublish mode).');
875
1201
  return true;
876
1202
  }
877
1203
  // Skip if private
@@ -1073,7 +1399,7 @@ export async function globalize(cwd, options = {}) {
1073
1399
  console.error(colors.yellow(' 3. Authentication token expired'));
1074
1400
  console.error('');
1075
1401
  }
1076
- if (transformed) {
1402
+ if (transformResult.transformed) {
1077
1403
  console.log('Run --cleanup to restore file: dependencies');
1078
1404
  }
1079
1405
  return false;
@@ -1095,8 +1421,9 @@ export async function globalize(cwd, options = {}) {
1095
1421
  }
1096
1422
  }
1097
1423
  // Global install
1098
- const pkgName = pkg.name;
1099
- const pkgVersion = pkg.version;
1424
+ const updatedPkg = readPackageJson(cwd); // Re-read to get updated version
1425
+ const pkgName = updatedPkg.name;
1426
+ const pkgVersion = updatedPkg.version;
1100
1427
  if (install) {
1101
1428
  console.log(`Installing globally: ${pkgName}@${pkgVersion}...`);
1102
1429
  if (!dryRun) {
@@ -1145,7 +1472,7 @@ export async function globalize(cwd, options = {}) {
1145
1472
  }
1146
1473
  }
1147
1474
  // Finalize - restore file: paths if --files mode (default)
1148
- if (files && transformed) {
1475
+ if (files && transformResult.transformed) {
1149
1476
  console.log('Restoring file: dependencies (--files mode)...');
1150
1477
  const finalPkg = readPackageJson(cwd);
1151
1478
  restoreDeps(finalPkg, verbose);
@@ -1163,6 +1490,11 @@ export async function globalize(cwd, options = {}) {
1163
1490
  console.log('Keeping npm versions (--nofiles mode).');
1164
1491
  }
1165
1492
  console.log('Done!');
1493
+ // Run final audit report if not already run
1494
+ const auditAlreadyRun = (fix || updateDeps) && (transformResult.transformed || alreadyTransformed || updateDeps);
1495
+ if (!auditAlreadyRun && (fix || updateDeps || transformResult.transformed) && !dryRun) {
1496
+ runNpmAudit(cwd, false, verbose); // Just report, don't fix again
1497
+ }
1166
1498
  // Print summary
1167
1499
  console.log('');
1168
1500
  console.log(colors.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
@@ -1179,7 +1511,7 @@ export async function globalize(cwd, options = {}) {
1179
1511
  if (wsl) {
1180
1512
  console.log(` Installed in WSL: ${colors.green('✓')}`);
1181
1513
  }
1182
- if (files && transformed) {
1514
+ if (files && transformResult.transformed) {
1183
1515
  console.log(` Restored file: deps: ${colors.green('✓')}`);
1184
1516
  }
1185
1517
  console.log(colors.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/npmglobalize",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "Transform file: dependencies to npm versions for publishing",
5
5
  "main": "index.js",
6
6
  "type": "module",