@bobfrankston/npmglobalize 1.0.140 → 1.0.142
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/cli.js +19 -1
- package/lib.d.ts +12 -0
- package/lib.js +121 -0
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* npmglobalize CLI - Transform file: dependencies to npm versions for publishing
|
|
4
4
|
*/
|
|
5
|
-
import { globalize, globalizeWorkspace, readConfig, readPackageJson, readUserNpmConfig, writeConfig, writePackageJson, confirm } from './lib.js';
|
|
5
|
+
import { globalize, globalizeWorkspace, readConfig, readPackageJson, readUserNpmConfig, writeConfig, writePackageJson, confirm, getBuildIssues, clearBuildIssues } from './lib.js';
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import { styleText } from 'util';
|
|
@@ -328,6 +328,20 @@ function parseArgs(args) {
|
|
|
328
328
|
}
|
|
329
329
|
return options;
|
|
330
330
|
}
|
|
331
|
+
/** Print accumulated build issues in warning color */
|
|
332
|
+
function printBuildSummary() {
|
|
333
|
+
const issues = getBuildIssues();
|
|
334
|
+
if (issues.length === 0)
|
|
335
|
+
return;
|
|
336
|
+
console.log('');
|
|
337
|
+
console.log(styleText('yellow', '━━━ Issues Summary ━━━━━━━━━━━━━━━━━━━━'));
|
|
338
|
+
for (const issue of issues) {
|
|
339
|
+
const icon = issue.severity === 'error' ? '✗' : '⚠';
|
|
340
|
+
console.log(styleText('yellow', ` ${icon} ${issue.module}: ${issue.message}`));
|
|
341
|
+
}
|
|
342
|
+
console.log(styleText('yellow', '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
343
|
+
console.log('');
|
|
344
|
+
}
|
|
331
345
|
export async function main() {
|
|
332
346
|
// Show version at the very start
|
|
333
347
|
const ownPkgPath = path.join(__dirname, 'package.json');
|
|
@@ -444,6 +458,7 @@ export async function main() {
|
|
|
444
458
|
}
|
|
445
459
|
writeConfig(cwd, persistable, cliOptions.explicitKeys);
|
|
446
460
|
}
|
|
461
|
+
clearBuildIssues();
|
|
447
462
|
try {
|
|
448
463
|
// Auto-detect workspace root: private package with workspaces[] field
|
|
449
464
|
if (!options.noWorkspace) {
|
|
@@ -452,12 +467,14 @@ export async function main() {
|
|
|
452
467
|
const rootPkg = readPackageJson(cwd);
|
|
453
468
|
if (rootPkg.private && Array.isArray(rootPkg.workspaces)) {
|
|
454
469
|
const result = await globalizeWorkspace(cwd, options, configOptions);
|
|
470
|
+
printBuildSummary();
|
|
455
471
|
process.exit(result.success ? 0 : 1);
|
|
456
472
|
}
|
|
457
473
|
}
|
|
458
474
|
}
|
|
459
475
|
options._fromCli = true;
|
|
460
476
|
const success = await globalize(cwd, options, configOptions);
|
|
477
|
+
printBuildSummary();
|
|
461
478
|
process.exit(success ? 0 : 1);
|
|
462
479
|
}
|
|
463
480
|
catch (error) {
|
|
@@ -465,6 +482,7 @@ export async function main() {
|
|
|
465
482
|
if (options.verbose) {
|
|
466
483
|
console.error(error.stack);
|
|
467
484
|
}
|
|
485
|
+
printBuildSummary();
|
|
468
486
|
process.exit(1);
|
|
469
487
|
}
|
|
470
488
|
}
|
package/lib.d.ts
CHANGED
|
@@ -10,6 +10,18 @@
|
|
|
10
10
|
* Consider library-based approach if async operations or cross-platform issues arise.
|
|
11
11
|
*/
|
|
12
12
|
import { type NpmCommonConfig } from '@bobfrankston/userconfig';
|
|
13
|
+
/** Issue recorded during a build/publish run for end-of-run summary */
|
|
14
|
+
export interface BuildIssue {
|
|
15
|
+
module: string;
|
|
16
|
+
severity: 'error' | 'warning';
|
|
17
|
+
message: string;
|
|
18
|
+
}
|
|
19
|
+
/** Record an issue for the end-of-run summary */
|
|
20
|
+
export declare function recordBuildIssue(module: string, severity: 'error' | 'warning', message: string): void;
|
|
21
|
+
/** Get all accumulated build issues */
|
|
22
|
+
export declare function getBuildIssues(): readonly BuildIssue[];
|
|
23
|
+
/** Clear accumulated issues (call at start of run) */
|
|
24
|
+
export declare function clearBuildIssues(): void;
|
|
13
25
|
/** Options for the globalize operation */
|
|
14
26
|
export interface GlobalizeOptions {
|
|
15
27
|
/** Bump type: patch (default), minor, major */
|
package/lib.js
CHANGED
|
@@ -33,6 +33,19 @@ import { fileURLToPath } from 'url';
|
|
|
33
33
|
import { themeColors } from '@bobfrankston/themecolors';
|
|
34
34
|
/** Semantic color functions — adapts to terminal light/dark theme */
|
|
35
35
|
const colors = themeColors();
|
|
36
|
+
const _buildIssues = [];
|
|
37
|
+
/** Record an issue for the end-of-run summary */
|
|
38
|
+
export function recordBuildIssue(module, severity, message) {
|
|
39
|
+
_buildIssues.push({ module, severity, message });
|
|
40
|
+
}
|
|
41
|
+
/** Get all accumulated build issues */
|
|
42
|
+
export function getBuildIssues() {
|
|
43
|
+
return _buildIssues;
|
|
44
|
+
}
|
|
45
|
+
/** Clear accumulated issues (call at start of run) */
|
|
46
|
+
export function clearBuildIssues() {
|
|
47
|
+
_buildIssues.length = 0;
|
|
48
|
+
}
|
|
36
49
|
/**
|
|
37
50
|
* Remove 'nul' files from a directory tree (Windows reserved name issue).
|
|
38
51
|
* These files break git and npm on Windows. Uses \\?\ prefix to bypass name validation.
|
|
@@ -1072,6 +1085,96 @@ export function runCommand(cmd, args, options = {}) {
|
|
|
1072
1085
|
return { success: false, output: '', stderr: error.message };
|
|
1073
1086
|
}
|
|
1074
1087
|
}
|
|
1088
|
+
/** Diagnose common build/version failure patterns and print actionable hints.
|
|
1089
|
+
* Returns true if a diagnosis was printed. */
|
|
1090
|
+
function diagnoseBuildFailure(errorText, cwd) {
|
|
1091
|
+
let diagnosed = false;
|
|
1092
|
+
// Pattern: missing module / package not found
|
|
1093
|
+
const missingPkg = errorText.match(/Cannot find (?:package|module) ['"](@?[^'"\/]+(?:\/[^'"\/]+)?)/);
|
|
1094
|
+
if (missingPkg) {
|
|
1095
|
+
const pkgName = missingPkg[1];
|
|
1096
|
+
console.error(colors.yellow(`\n Hint: missing package "${pkgName}"`));
|
|
1097
|
+
// Check for broken symlinks in node_modules tree
|
|
1098
|
+
const searchDirs = [cwd];
|
|
1099
|
+
try {
|
|
1100
|
+
for (const entry of fs.readdirSync(cwd, { withFileTypes: true })) {
|
|
1101
|
+
if (entry.isDirectory() && entry.name !== 'node_modules' && entry.name !== '.git' && entry.name !== 'prev') {
|
|
1102
|
+
searchDirs.push(path.join(cwd, entry.name));
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
catch { /* ignore */ }
|
|
1107
|
+
for (const dir of searchDirs) {
|
|
1108
|
+
const nmPath = path.join(dir, 'node_modules', ...pkgName.split('/'));
|
|
1109
|
+
try {
|
|
1110
|
+
const stat = fs.lstatSync(nmPath);
|
|
1111
|
+
if (stat.isSymbolicLink()) {
|
|
1112
|
+
const target = fs.readlinkSync(nmPath);
|
|
1113
|
+
const resolved = path.resolve(path.dirname(nmPath), target);
|
|
1114
|
+
if (!fs.existsSync(resolved)) {
|
|
1115
|
+
console.error(colors.yellow(` Broken symlink: ${nmPath}`));
|
|
1116
|
+
console.error(colors.yellow(` → ${resolved} (target missing)`));
|
|
1117
|
+
console.error(colors.yellow(` Fix: cd "${dir}" && npm install`));
|
|
1118
|
+
diagnosed = true;
|
|
1119
|
+
}
|
|
1120
|
+
else {
|
|
1121
|
+
console.error(colors.yellow(` Symlink exists at ${nmPath} but module still failed to load.`));
|
|
1122
|
+
console.error(colors.yellow(` Check that the target package has been built (has .js files).`));
|
|
1123
|
+
diagnosed = true;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
catch {
|
|
1128
|
+
// No symlink found at this location — check if it should exist
|
|
1129
|
+
const subPkgPath = path.join(dir, 'package.json');
|
|
1130
|
+
if (fs.existsSync(subPkgPath)) {
|
|
1131
|
+
try {
|
|
1132
|
+
const subPkg = JSON.parse(fs.readFileSync(subPkgPath, 'utf-8'));
|
|
1133
|
+
const allDeps = { ...subPkg.dependencies, ...subPkg.devDependencies };
|
|
1134
|
+
if (allDeps[pkgName]) {
|
|
1135
|
+
const depValue = allDeps[pkgName];
|
|
1136
|
+
if (typeof depValue === 'string' && depValue.startsWith('file:')) {
|
|
1137
|
+
const target = path.resolve(dir, depValue.replace('file:', ''));
|
|
1138
|
+
console.error(colors.yellow(` "${pkgName}" is a file: dep in ${path.relative(cwd, subPkgPath) || 'package.json'}`));
|
|
1139
|
+
console.error(colors.yellow(` → ${target}`));
|
|
1140
|
+
if (!fs.existsSync(target)) {
|
|
1141
|
+
console.error(colors.yellow(` Target directory does not exist!`));
|
|
1142
|
+
}
|
|
1143
|
+
else {
|
|
1144
|
+
console.error(colors.yellow(` Fix: cd "${dir}" && npm install`));
|
|
1145
|
+
}
|
|
1146
|
+
diagnosed = true;
|
|
1147
|
+
}
|
|
1148
|
+
else {
|
|
1149
|
+
console.error(colors.yellow(` "${pkgName}" is listed in ${path.relative(cwd, subPkgPath) || 'package.json'} but not installed.`));
|
|
1150
|
+
console.error(colors.yellow(` Fix: cd "${dir}" && npm install`));
|
|
1151
|
+
diagnosed = true;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
catch { /* ignore parse errors */ }
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
if (!diagnosed) {
|
|
1160
|
+
console.error(colors.yellow(` "${pkgName}" is not installed and not listed as a dependency.`));
|
|
1161
|
+
console.error(colors.yellow(` If needed, add it: npm install ${pkgName}`));
|
|
1162
|
+
diagnosed = true;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
// Pattern: permission denied
|
|
1166
|
+
if (!diagnosed && /permission denied|EPERM|EACCES/.test(errorText)) {
|
|
1167
|
+
console.error(colors.yellow('\n Hint: permission error during build.'));
|
|
1168
|
+
console.error(colors.yellow(' Check file ownership and that no other process has files locked.'));
|
|
1169
|
+
diagnosed = true;
|
|
1170
|
+
}
|
|
1171
|
+
// Pattern: TypeScript compilation error
|
|
1172
|
+
if (!diagnosed && /error TS\d+/.test(errorText)) {
|
|
1173
|
+
console.error(colors.yellow('\n Hint: TypeScript compilation errors. Fix the type errors above before publishing.'));
|
|
1174
|
+
diagnosed = true;
|
|
1175
|
+
}
|
|
1176
|
+
return diagnosed;
|
|
1177
|
+
}
|
|
1075
1178
|
/** Run git commit silently and print a compact summary (no per-file create/delete mode lines). */
|
|
1076
1179
|
function gitCommit(msg, cwd) {
|
|
1077
1180
|
const result = runCommand('git', ['commit', '-m', msg], { cwd, silent: true });
|
|
@@ -2646,6 +2749,8 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
2646
2749
|
const buildResult = runCommand('npm', ['run', 'build'], { cwd, silent: !verbose });
|
|
2647
2750
|
if (!buildResult.success) {
|
|
2648
2751
|
console.error(colors.red('ERROR: Build failed:'), buildResult.stderr || buildResult.output);
|
|
2752
|
+
diagnoseBuildFailure(buildResult.stderr || buildResult.output || '', cwd);
|
|
2753
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', 'Build failed');
|
|
2649
2754
|
if (!force) {
|
|
2650
2755
|
return false;
|
|
2651
2756
|
}
|
|
@@ -3052,6 +3157,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3052
3157
|
});
|
|
3053
3158
|
if (!depSuccess) {
|
|
3054
3159
|
console.error(colors.red(`Failed to publish ${name}`));
|
|
3160
|
+
recordBuildIssue(pkg.name || cwd, 'error', `Dependency failed: ${name}`);
|
|
3055
3161
|
if (!force) {
|
|
3056
3162
|
return false;
|
|
3057
3163
|
}
|
|
@@ -3551,6 +3657,9 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3551
3657
|
if (error.code)
|
|
3552
3658
|
console.error(' exit code:', error.code);
|
|
3553
3659
|
}
|
|
3660
|
+
// Diagnose common failures (missing modules, permissions, TS errors)
|
|
3661
|
+
const versionErrorText = [error.message, error.stderr, error.stdout].filter(Boolean).join('\n');
|
|
3662
|
+
diagnoseBuildFailure(versionErrorText, cwd);
|
|
3554
3663
|
// Show the full error stack in verbose mode
|
|
3555
3664
|
if (verbose && error.stack) {
|
|
3556
3665
|
console.error(' Stack trace:', error.stack);
|
|
@@ -3777,6 +3886,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3777
3886
|
}
|
|
3778
3887
|
console.error(colors.yellow('Consumers will get E404 when installing this package.'));
|
|
3779
3888
|
console.error(colors.yellow('Fix: publish those deps as public, or make this package private.'));
|
|
3889
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'warning', `Public pkg depends on private: ${privateDeps.join(', ')}`);
|
|
3780
3890
|
console.log('');
|
|
3781
3891
|
}
|
|
3782
3892
|
}
|
|
@@ -3788,6 +3898,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3788
3898
|
const authStatus = checkNpmAuth();
|
|
3789
3899
|
if (!authStatus.authenticated) {
|
|
3790
3900
|
console.error(colors.red(`Not authenticated to npm (${authStatus.error}) — run: npm login`));
|
|
3901
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', `npm auth: ${authStatus.error}`);
|
|
3791
3902
|
return false;
|
|
3792
3903
|
}
|
|
3793
3904
|
if (verbose) {
|
|
@@ -3799,6 +3910,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3799
3910
|
console.error(colors.red('ERROR: Failed to create package tarball'));
|
|
3800
3911
|
console.error(colors.yellow('Output:'), packResult.output);
|
|
3801
3912
|
console.error(colors.yellow('Error:'), packResult.stderr);
|
|
3913
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', 'npm pack failed');
|
|
3802
3914
|
return false;
|
|
3803
3915
|
}
|
|
3804
3916
|
// Get the tarball filename from npm pack output
|
|
@@ -3831,6 +3943,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3831
3943
|
}
|
|
3832
3944
|
}
|
|
3833
3945
|
console.error(colors.yellow(' Check .npmignore — you may need to exclude large files or directories.'));
|
|
3946
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', `Tarball too large (${sizeMB.toFixed(0)}MB)`);
|
|
3834
3947
|
// Clean up and abort
|
|
3835
3948
|
try {
|
|
3836
3949
|
fs.unlinkSync(tarballPath);
|
|
@@ -3891,6 +4004,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3891
4004
|
}
|
|
3892
4005
|
if (!publishResult.success) {
|
|
3893
4006
|
console.error(colors.red('\nERROR: npm publish failed\n'));
|
|
4007
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', 'npm publish failed');
|
|
3894
4008
|
// Check for specific error types
|
|
3895
4009
|
const output = (publishResult.output + '\n' + publishResult.stderr).toLowerCase();
|
|
3896
4010
|
if (output.includes('err_string_too_long') || output.includes('string longer than')) {
|
|
@@ -3992,6 +4106,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3992
4106
|
else {
|
|
3993
4107
|
console.error(colors.red(`✗ Global link install failed`));
|
|
3994
4108
|
console.error(colors.yellow(' Try running manually: npm install -g .'));
|
|
4109
|
+
recordBuildIssue(pkgName, 'warning', 'Global link install failed');
|
|
3995
4110
|
}
|
|
3996
4111
|
}
|
|
3997
4112
|
else {
|
|
@@ -4011,6 +4126,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4011
4126
|
else {
|
|
4012
4127
|
console.error(colors.red(`✗ Global install failed`));
|
|
4013
4128
|
console.error(colors.yellow(' Try running manually: npm install -g .'));
|
|
4129
|
+
recordBuildIssue(pkgName, 'warning', 'Global install failed');
|
|
4014
4130
|
}
|
|
4015
4131
|
}
|
|
4016
4132
|
else {
|
|
@@ -4029,6 +4145,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4029
4145
|
else {
|
|
4030
4146
|
console.error(colors.red(`✗ Global install failed`));
|
|
4031
4147
|
console.error(colors.yellow(` Try running manually: npm install -g ${pkgName}@${pkgVersion}`));
|
|
4148
|
+
recordBuildIssue(pkgName, 'warning', 'Global install failed');
|
|
4032
4149
|
}
|
|
4033
4150
|
}
|
|
4034
4151
|
else {
|
|
@@ -4050,6 +4167,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4050
4167
|
}
|
|
4051
4168
|
else {
|
|
4052
4169
|
console.error(colors.yellow('✗ WSL install failed (is npm installed in WSL?)'));
|
|
4170
|
+
recordBuildIssue(pkgName, 'warning', 'WSL install failed');
|
|
4053
4171
|
}
|
|
4054
4172
|
}
|
|
4055
4173
|
else {
|
|
@@ -4277,6 +4395,7 @@ export async function globalizeWorkspace(rootDir, options = {}, configOptions =
|
|
|
4277
4395
|
success: false,
|
|
4278
4396
|
error: error.message,
|
|
4279
4397
|
});
|
|
4398
|
+
recordBuildIssue(pkgName, 'error', error.message);
|
|
4280
4399
|
console.error(colors.red(`\nError processing ${pkgName}: ${error.message}`));
|
|
4281
4400
|
if (!options.continueOnError) {
|
|
4282
4401
|
console.error(colors.red('Use --continue-on-error to continue with remaining packages.'));
|
|
@@ -4359,6 +4478,7 @@ export async function globalizeWorkspace(rootDir, options = {}, configOptions =
|
|
|
4359
4478
|
else {
|
|
4360
4479
|
console.error(colors.red(`✗ Global install failed`));
|
|
4361
4480
|
console.error(colors.yellow(' Try running manually: npm install -g .'));
|
|
4481
|
+
recordBuildIssue(pkgName, 'warning', 'Global install failed');
|
|
4362
4482
|
}
|
|
4363
4483
|
}
|
|
4364
4484
|
else {
|
|
@@ -4374,6 +4494,7 @@ export async function globalizeWorkspace(rootDir, options = {}, configOptions =
|
|
|
4374
4494
|
}
|
|
4375
4495
|
else {
|
|
4376
4496
|
console.error(colors.yellow('✗ WSL install failed (is npm installed in WSL?)'));
|
|
4497
|
+
recordBuildIssue(pkgName, 'warning', 'WSL install failed');
|
|
4377
4498
|
}
|
|
4378
4499
|
}
|
|
4379
4500
|
else {
|