@bobfrankston/npmglobalize 1.0.141 → 1.0.143
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 +32 -6
- package/lib.d.ts +15 -0
- package/lib.js +76 -12
- 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, recordBuildIssue, extractFirstTscError } 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');
|
|
@@ -388,15 +402,23 @@ export async function main() {
|
|
|
388
402
|
}
|
|
389
403
|
}
|
|
390
404
|
if (pkg.scripts?.build) {
|
|
391
|
-
const {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
405
|
+
const { spawnSync } = await import('child_process');
|
|
406
|
+
console.log(`Building ${cwd}...`);
|
|
407
|
+
const buildResult = spawnSync('npm', ['run', 'build'], {
|
|
408
|
+
cwd, encoding: 'utf-8', stdio: 'pipe', shell: true
|
|
409
|
+
});
|
|
410
|
+
if (buildResult.status === 0) {
|
|
395
411
|
console.log(styleText('green', '✓ Build succeeded'));
|
|
396
412
|
}
|
|
397
|
-
|
|
413
|
+
else {
|
|
414
|
+
const buildOutput = (buildResult.stderr || '') + (buildResult.stdout || '');
|
|
415
|
+
if (buildOutput)
|
|
416
|
+
console.error(buildOutput);
|
|
398
417
|
console.error(styleText('red', `Build failed in ${cwd}`));
|
|
418
|
+
const firstErr = extractFirstTscError(buildOutput);
|
|
419
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', firstErr || 'Build failed');
|
|
399
420
|
if (!cliOptions.force) {
|
|
421
|
+
printBuildSummary();
|
|
400
422
|
process.exit(1);
|
|
401
423
|
}
|
|
402
424
|
console.log(styleText('yellow', 'Continuing with --force...'));
|
|
@@ -444,6 +466,7 @@ export async function main() {
|
|
|
444
466
|
}
|
|
445
467
|
writeConfig(cwd, persistable, cliOptions.explicitKeys);
|
|
446
468
|
}
|
|
469
|
+
clearBuildIssues();
|
|
447
470
|
try {
|
|
448
471
|
// Auto-detect workspace root: private package with workspaces[] field
|
|
449
472
|
if (!options.noWorkspace) {
|
|
@@ -452,12 +475,14 @@ export async function main() {
|
|
|
452
475
|
const rootPkg = readPackageJson(cwd);
|
|
453
476
|
if (rootPkg.private && Array.isArray(rootPkg.workspaces)) {
|
|
454
477
|
const result = await globalizeWorkspace(cwd, options, configOptions);
|
|
478
|
+
printBuildSummary();
|
|
455
479
|
process.exit(result.success ? 0 : 1);
|
|
456
480
|
}
|
|
457
481
|
}
|
|
458
482
|
}
|
|
459
483
|
options._fromCli = true;
|
|
460
484
|
const success = await globalize(cwd, options, configOptions);
|
|
485
|
+
printBuildSummary();
|
|
461
486
|
process.exit(success ? 0 : 1);
|
|
462
487
|
}
|
|
463
488
|
catch (error) {
|
|
@@ -465,6 +490,7 @@ export async function main() {
|
|
|
465
490
|
if (options.verbose) {
|
|
466
491
|
console.error(error.stack);
|
|
467
492
|
}
|
|
493
|
+
printBuildSummary();
|
|
468
494
|
process.exit(1);
|
|
469
495
|
}
|
|
470
496
|
}
|
package/lib.d.ts
CHANGED
|
@@ -10,6 +10,21 @@
|
|
|
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;
|
|
25
|
+
/** Extract the first TypeScript error line from build output for the summary.
|
|
26
|
+
* Returns a short string like "file.ts(42,5): error TS2339: Property 'foo' ..." */
|
|
27
|
+
export declare function extractFirstTscError(output: string): string | null;
|
|
13
28
|
/** Options for the globalize operation */
|
|
14
29
|
export interface GlobalizeOptions {
|
|
15
30
|
/** Bump type: patch (default), minor, major */
|
package/lib.js
CHANGED
|
@@ -33,6 +33,38 @@ 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
|
+
}
|
|
49
|
+
/** Extract the first TypeScript error line from build output for the summary.
|
|
50
|
+
* Returns a short string like "file.ts(42,5): error TS2339: Property 'foo' ..." */
|
|
51
|
+
export function extractFirstTscError(output) {
|
|
52
|
+
if (!output)
|
|
53
|
+
return null;
|
|
54
|
+
// tsc errors: src/file.ts(line,col): error TS1234: message
|
|
55
|
+
const tscMatch = output.match(/^(.+?\(\d+,\d+\): error TS\d+: .+)$/m);
|
|
56
|
+
if (tscMatch) {
|
|
57
|
+
const line = tscMatch[1];
|
|
58
|
+
return line.length > 120 ? line.slice(0, 117) + '...' : line;
|
|
59
|
+
}
|
|
60
|
+
// Generic "error" line
|
|
61
|
+
const errMatch = output.match(/^(error .+)$/m);
|
|
62
|
+
if (errMatch) {
|
|
63
|
+
const line = errMatch[1];
|
|
64
|
+
return line.length > 120 ? line.slice(0, 117) + '...' : line;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
36
68
|
/**
|
|
37
69
|
* Remove 'nul' files from a directory tree (Windows reserved name issue).
|
|
38
70
|
* These files break git and npm on Windows. Uses \\?\ prefix to bypass name validation.
|
|
@@ -2733,16 +2765,24 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
2733
2765
|
if (pkg.scripts?.build && !options._fromCli) {
|
|
2734
2766
|
console.log(`${timestamp()} Running build...`);
|
|
2735
2767
|
if (!dryRun) {
|
|
2736
|
-
|
|
2768
|
+
// Always capture output so we can extract tsc errors for the summary
|
|
2769
|
+
const buildResult = runCommand('npm', ['run', 'build'], { cwd, silent: true });
|
|
2737
2770
|
if (!buildResult.success) {
|
|
2738
|
-
|
|
2739
|
-
|
|
2771
|
+
const buildOutput = buildResult.stderr || buildResult.output;
|
|
2772
|
+
if (buildOutput)
|
|
2773
|
+
console.error(buildOutput);
|
|
2774
|
+
console.error(colors.red('ERROR: Build failed'));
|
|
2775
|
+
diagnoseBuildFailure(buildOutput || '', cwd);
|
|
2776
|
+
const firstErr = extractFirstTscError(buildOutput || '');
|
|
2777
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', firstErr || 'Build failed');
|
|
2740
2778
|
if (!force) {
|
|
2741
2779
|
return false;
|
|
2742
2780
|
}
|
|
2743
2781
|
console.log(colors.yellow('Continuing with --force despite build failure...'));
|
|
2744
2782
|
}
|
|
2745
2783
|
else {
|
|
2784
|
+
if (verbose && buildResult.output)
|
|
2785
|
+
process.stdout.write(buildResult.output);
|
|
2746
2786
|
console.log(`${timestamp()} ${colors.green('✓ Build succeeded')}`);
|
|
2747
2787
|
}
|
|
2748
2788
|
}
|
|
@@ -3143,6 +3183,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3143
3183
|
});
|
|
3144
3184
|
if (!depSuccess) {
|
|
3145
3185
|
console.error(colors.red(`Failed to publish ${name}`));
|
|
3186
|
+
recordBuildIssue(pkg.name || cwd, 'error', `Dependency failed: ${name}`);
|
|
3146
3187
|
if (!force) {
|
|
3147
3188
|
return false;
|
|
3148
3189
|
}
|
|
@@ -3871,6 +3912,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3871
3912
|
}
|
|
3872
3913
|
console.error(colors.yellow('Consumers will get E404 when installing this package.'));
|
|
3873
3914
|
console.error(colors.yellow('Fix: publish those deps as public, or make this package private.'));
|
|
3915
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'warning', `Public pkg depends on private: ${privateDeps.join(', ')}`);
|
|
3874
3916
|
console.log('');
|
|
3875
3917
|
}
|
|
3876
3918
|
}
|
|
@@ -3882,6 +3924,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3882
3924
|
const authStatus = checkNpmAuth();
|
|
3883
3925
|
if (!authStatus.authenticated) {
|
|
3884
3926
|
console.error(colors.red(`Not authenticated to npm (${authStatus.error}) — run: npm login`));
|
|
3927
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', `npm auth: ${authStatus.error}`);
|
|
3885
3928
|
return false;
|
|
3886
3929
|
}
|
|
3887
3930
|
if (verbose) {
|
|
@@ -3893,6 +3936,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3893
3936
|
console.error(colors.red('ERROR: Failed to create package tarball'));
|
|
3894
3937
|
console.error(colors.yellow('Output:'), packResult.output);
|
|
3895
3938
|
console.error(colors.yellow('Error:'), packResult.stderr);
|
|
3939
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', 'npm pack failed');
|
|
3896
3940
|
return false;
|
|
3897
3941
|
}
|
|
3898
3942
|
// Get the tarball filename from npm pack output
|
|
@@ -3925,6 +3969,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3925
3969
|
}
|
|
3926
3970
|
}
|
|
3927
3971
|
console.error(colors.yellow(' Check .npmignore — you may need to exclude large files or directories.'));
|
|
3972
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', `Tarball too large (${sizeMB.toFixed(0)}MB)`);
|
|
3928
3973
|
// Clean up and abort
|
|
3929
3974
|
try {
|
|
3930
3975
|
fs.unlinkSync(tarballPath);
|
|
@@ -3984,21 +4029,17 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3984
4029
|
// Ignore cleanup errors
|
|
3985
4030
|
}
|
|
3986
4031
|
if (!publishResult.success) {
|
|
3987
|
-
|
|
3988
|
-
// Check for specific error types
|
|
4032
|
+
// Check for specific error types before recording
|
|
3989
4033
|
const output = (publishResult.output + '\n' + publishResult.stderr).toLowerCase();
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
}
|
|
3993
|
-
else if (output.includes('e409') || output.includes('409 conflict')) {
|
|
3994
|
-
console.error(colors.yellow('npm still processing previous version — wait and retry'));
|
|
3995
|
-
}
|
|
3996
|
-
else if (output.includes('cannot publish over') || output.includes('previously published')) {
|
|
4034
|
+
// "Already published" is benign — don't record as error
|
|
4035
|
+
if (output.includes('cannot publish over') || output.includes('previously published')) {
|
|
3997
4036
|
const currentPkgVersion = readPackageJson(cwd).version;
|
|
3998
4037
|
if (output.includes(currentPkgVersion)) {
|
|
3999
4038
|
console.log(colors.green('✓ Already published — continuing'));
|
|
4000
4039
|
}
|
|
4001
4040
|
else {
|
|
4041
|
+
console.error(colors.red('\nERROR: npm publish failed\n'));
|
|
4042
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', 'npm publish failed');
|
|
4002
4043
|
console.error(colors.yellow('Version conflict — run npmglobalize again'));
|
|
4003
4044
|
if (transformResult.transformed) {
|
|
4004
4045
|
const failPkg = readPackageJson(cwd);
|
|
@@ -4010,7 +4051,19 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4010
4051
|
return false;
|
|
4011
4052
|
}
|
|
4012
4053
|
}
|
|
4054
|
+
else if (output.includes('err_string_too_long') || output.includes('string longer than')) {
|
|
4055
|
+
console.error(colors.red('\nERROR: npm publish failed\n'));
|
|
4056
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', 'npm publish failed');
|
|
4057
|
+
console.error(colors.red('Tarball too large — check .npmignore. Run: npm pack --dry-run'));
|
|
4058
|
+
}
|
|
4059
|
+
else if (output.includes('e409') || output.includes('409 conflict')) {
|
|
4060
|
+
console.error(colors.red('\nERROR: npm publish failed\n'));
|
|
4061
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', 'npm publish failed');
|
|
4062
|
+
console.error(colors.yellow('npm still processing previous version — wait and retry'));
|
|
4063
|
+
}
|
|
4013
4064
|
else if (output.includes('403') || output.includes('forbidden')) {
|
|
4065
|
+
console.error(colors.red('\nERROR: npm publish failed\n'));
|
|
4066
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', 'npm publish failed');
|
|
4014
4067
|
console.error(colors.yellow('Publish forbidden — check npm login and package access'));
|
|
4015
4068
|
if (transformResult.transformed) {
|
|
4016
4069
|
const failPkg = readPackageJson(cwd);
|
|
@@ -4022,9 +4075,13 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4022
4075
|
return false;
|
|
4023
4076
|
}
|
|
4024
4077
|
else if (output.includes('402') || output.includes('payment required')) {
|
|
4078
|
+
console.error(colors.red('\nERROR: npm publish failed\n'));
|
|
4079
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', 'npm publish failed');
|
|
4025
4080
|
console.error(colors.yellow('Private packages need paid npm account — use --npm public'));
|
|
4026
4081
|
}
|
|
4027
4082
|
else {
|
|
4083
|
+
console.error(colors.red('\nERROR: npm publish failed\n'));
|
|
4084
|
+
recordBuildIssue(pkg.name || path.basename(cwd), 'error', 'npm publish failed');
|
|
4028
4085
|
console.error(colors.yellow('Publish failed — run npm login or check npm whoami'));
|
|
4029
4086
|
}
|
|
4030
4087
|
if (transformResult.transformed) {
|
|
@@ -4086,6 +4143,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4086
4143
|
else {
|
|
4087
4144
|
console.error(colors.red(`✗ Global link install failed`));
|
|
4088
4145
|
console.error(colors.yellow(' Try running manually: npm install -g .'));
|
|
4146
|
+
recordBuildIssue(pkgName, 'warning', 'Global link install failed');
|
|
4089
4147
|
}
|
|
4090
4148
|
}
|
|
4091
4149
|
else {
|
|
@@ -4105,6 +4163,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4105
4163
|
else {
|
|
4106
4164
|
console.error(colors.red(`✗ Global install failed`));
|
|
4107
4165
|
console.error(colors.yellow(' Try running manually: npm install -g .'));
|
|
4166
|
+
recordBuildIssue(pkgName, 'warning', 'Global install failed');
|
|
4108
4167
|
}
|
|
4109
4168
|
}
|
|
4110
4169
|
else {
|
|
@@ -4123,6 +4182,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4123
4182
|
else {
|
|
4124
4183
|
console.error(colors.red(`✗ Global install failed`));
|
|
4125
4184
|
console.error(colors.yellow(` Try running manually: npm install -g ${pkgName}@${pkgVersion}`));
|
|
4185
|
+
recordBuildIssue(pkgName, 'warning', 'Global install failed');
|
|
4126
4186
|
}
|
|
4127
4187
|
}
|
|
4128
4188
|
else {
|
|
@@ -4144,6 +4204,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4144
4204
|
}
|
|
4145
4205
|
else {
|
|
4146
4206
|
console.error(colors.yellow('✗ WSL install failed (is npm installed in WSL?)'));
|
|
4207
|
+
recordBuildIssue(pkgName, 'warning', 'WSL install failed');
|
|
4147
4208
|
}
|
|
4148
4209
|
}
|
|
4149
4210
|
else {
|
|
@@ -4371,6 +4432,7 @@ export async function globalizeWorkspace(rootDir, options = {}, configOptions =
|
|
|
4371
4432
|
success: false,
|
|
4372
4433
|
error: error.message,
|
|
4373
4434
|
});
|
|
4435
|
+
recordBuildIssue(pkgName, 'error', error.message);
|
|
4374
4436
|
console.error(colors.red(`\nError processing ${pkgName}: ${error.message}`));
|
|
4375
4437
|
if (!options.continueOnError) {
|
|
4376
4438
|
console.error(colors.red('Use --continue-on-error to continue with remaining packages.'));
|
|
@@ -4453,6 +4515,7 @@ export async function globalizeWorkspace(rootDir, options = {}, configOptions =
|
|
|
4453
4515
|
else {
|
|
4454
4516
|
console.error(colors.red(`✗ Global install failed`));
|
|
4455
4517
|
console.error(colors.yellow(' Try running manually: npm install -g .'));
|
|
4518
|
+
recordBuildIssue(pkgName, 'warning', 'Global install failed');
|
|
4456
4519
|
}
|
|
4457
4520
|
}
|
|
4458
4521
|
else {
|
|
@@ -4468,6 +4531,7 @@ export async function globalizeWorkspace(rootDir, options = {}, configOptions =
|
|
|
4468
4531
|
}
|
|
4469
4532
|
else {
|
|
4470
4533
|
console.error(colors.yellow('✗ WSL install failed (is npm installed in WSL?)'));
|
|
4534
|
+
recordBuildIssue(pkgName, 'warning', 'WSL install failed');
|
|
4471
4535
|
}
|
|
4472
4536
|
}
|
|
4473
4537
|
else {
|