@bobfrankston/npmglobalize 1.0.142 → 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.
Files changed (4) hide show
  1. package/cli.js +14 -6
  2. package/lib.d.ts +3 -0
  3. package/lib.js +51 -14
  4. 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, getBuildIssues, clearBuildIssues } 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';
@@ -402,15 +402,23 @@ export async function main() {
402
402
  }
403
403
  }
404
404
  if (pkg.scripts?.build) {
405
- const { execSync } = await import('child_process');
406
- try {
407
- console.log(`Building ${cwd}...`);
408
- execSync('npm run build', { cwd, stdio: 'inherit' });
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) {
409
411
  console.log(styleText('green', '✓ Build succeeded'));
410
412
  }
411
- catch (error) {
413
+ else {
414
+ const buildOutput = (buildResult.stderr || '') + (buildResult.stdout || '');
415
+ if (buildOutput)
416
+ console.error(buildOutput);
412
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');
413
420
  if (!cliOptions.force) {
421
+ printBuildSummary();
414
422
  process.exit(1);
415
423
  }
416
424
  console.log(styleText('yellow', 'Continuing with --force...'));
package/lib.d.ts CHANGED
@@ -22,6 +22,9 @@ export declare function recordBuildIssue(module: string, severity: 'error' | 'wa
22
22
  export declare function getBuildIssues(): readonly BuildIssue[];
23
23
  /** Clear accumulated issues (call at start of run) */
24
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;
25
28
  /** Options for the globalize operation */
26
29
  export interface GlobalizeOptions {
27
30
  /** Bump type: patch (default), minor, major */
package/lib.js CHANGED
@@ -46,6 +46,25 @@ export function getBuildIssues() {
46
46
  export function clearBuildIssues() {
47
47
  _buildIssues.length = 0;
48
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
+ }
49
68
  /**
50
69
  * Remove 'nul' files from a directory tree (Windows reserved name issue).
51
70
  * These files break git and npm on Windows. Uses \\?\ prefix to bypass name validation.
@@ -2746,17 +2765,24 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
2746
2765
  if (pkg.scripts?.build && !options._fromCli) {
2747
2766
  console.log(`${timestamp()} Running build...`);
2748
2767
  if (!dryRun) {
2749
- const buildResult = runCommand('npm', ['run', 'build'], { cwd, silent: !verbose });
2768
+ // Always capture output so we can extract tsc errors for the summary
2769
+ const buildResult = runCommand('npm', ['run', 'build'], { cwd, silent: true });
2750
2770
  if (!buildResult.success) {
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');
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');
2754
2778
  if (!force) {
2755
2779
  return false;
2756
2780
  }
2757
2781
  console.log(colors.yellow('Continuing with --force despite build failure...'));
2758
2782
  }
2759
2783
  else {
2784
+ if (verbose && buildResult.output)
2785
+ process.stdout.write(buildResult.output);
2760
2786
  console.log(`${timestamp()} ${colors.green('✓ Build succeeded')}`);
2761
2787
  }
2762
2788
  }
@@ -4003,22 +4029,17 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
4003
4029
  // Ignore cleanup errors
4004
4030
  }
4005
4031
  if (!publishResult.success) {
4006
- console.error(colors.red('\nERROR: npm publish failed\n'));
4007
- recordBuildIssue(pkg.name || path.basename(cwd), 'error', 'npm publish failed');
4008
- // Check for specific error types
4032
+ // Check for specific error types before recording
4009
4033
  const output = (publishResult.output + '\n' + publishResult.stderr).toLowerCase();
4010
- if (output.includes('err_string_too_long') || output.includes('string longer than')) {
4011
- console.error(colors.red('Tarball too large check .npmignore. Run: npm pack --dry-run'));
4012
- }
4013
- else if (output.includes('e409') || output.includes('409 conflict')) {
4014
- console.error(colors.yellow('npm still processing previous version — wait and retry'));
4015
- }
4016
- 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')) {
4017
4036
  const currentPkgVersion = readPackageJson(cwd).version;
4018
4037
  if (output.includes(currentPkgVersion)) {
4019
4038
  console.log(colors.green('✓ Already published — continuing'));
4020
4039
  }
4021
4040
  else {
4041
+ console.error(colors.red('\nERROR: npm publish failed\n'));
4042
+ recordBuildIssue(pkg.name || path.basename(cwd), 'error', 'npm publish failed');
4022
4043
  console.error(colors.yellow('Version conflict — run npmglobalize again'));
4023
4044
  if (transformResult.transformed) {
4024
4045
  const failPkg = readPackageJson(cwd);
@@ -4030,7 +4051,19 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
4030
4051
  return false;
4031
4052
  }
4032
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
+ }
4033
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');
4034
4067
  console.error(colors.yellow('Publish forbidden — check npm login and package access'));
4035
4068
  if (transformResult.transformed) {
4036
4069
  const failPkg = readPackageJson(cwd);
@@ -4042,9 +4075,13 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
4042
4075
  return false;
4043
4076
  }
4044
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');
4045
4080
  console.error(colors.yellow('Private packages need paid npm account — use --npm public'));
4046
4081
  }
4047
4082
  else {
4083
+ console.error(colors.red('\nERROR: npm publish failed\n'));
4084
+ recordBuildIssue(pkg.name || path.basename(cwd), 'error', 'npm publish failed');
4048
4085
  console.error(colors.yellow('Publish failed — run npm login or check npm whoami'));
4049
4086
  }
4050
4087
  if (transformResult.transformed) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/npmglobalize",
3
- "version": "1.0.142",
3
+ "version": "1.0.143",
4
4
  "description": "Transform file: dependencies to npm versions for publishing",
5
5
  "main": "index.js",
6
6
  "type": "module",