@@ -8,6 +8,8 @@ import { createWriteStream } from 'node:fs';
88import { readFile } from 'node:fs/promises' ;
99import process from 'node:process' ;
1010
11+ import { unwrapSuppressedError } from '@d-zero/cli-core' ;
12+
1113import { formatResultLine } from './format-output.js' ;
1214import { parseArgs } from './parse-args.js' ;
1315import { parseUrlList } from './parse-url-list.js' ;
@@ -134,9 +136,12 @@ export async function runCli(options: {
134136 return 1 ;
135137 }
136138
137- const outStream : NodeJS . WritableStream = args . out
138- ? createWriteStream ( args . out )
139- : options . stdout ;
139+ // `await using` により、runBatch() が想定外の例外を投げてスコープを抜けても
140+ // --out で開いたファイル記述子が確実に閉じられる(stdout の場合は outFile が
141+ // undefined のままなので dispose は no-op — process.stdout を誤って
142+ // close してしまうことはない)。
143+ await using outFile = args . out ? createWriteStream ( args . out ) : undefined ;
144+ const outStream : NodeJS . WritableStream = outFile ?? options . stdout ;
140145
141146 let hadError = false ;
142147 await runBatch ( urls , {
@@ -158,24 +163,26 @@ export async function runCli(options: {
158163 } ,
159164 onError : ( url , error ) => {
160165 hadError = true ;
161- options . stderr . write (
162- `anatomist: failed to analyze ${ url } : ${ ( error as Error ) . message } \n` ,
163- ) ;
166+ // SuppressedError(using スコープ内で本体と dispose の両方が例外を投げた
167+ // 場合)を分解し、定型メッセージの裏に隠れる根本原因を両方とも出力する
168+ for ( const cause of unwrapSuppressedError ( error ) ) {
169+ options . stderr . write (
170+ `anatomist: failed to analyze ${ url } : ${ cause instanceof Error ? cause . message : String ( cause ) } \n` ,
171+ ) ;
172+ }
164173 } ,
165174 } ) ;
166175
167- if ( args . out ) {
176+ if ( outFile ) {
168177 try {
169178 await new Promise < void > ( ( resolve , reject ) => {
170- ( outStream as ReturnType < typeof createWriteStream > ) . end (
171- ( error ?: Error | null ) => {
172- if ( error ) {
173- reject ( error ) ;
174- } else {
175- resolve ( ) ;
176- }
177- } ,
178- ) ;
179+ outFile . end ( ( error ?: Error | null ) => {
180+ if ( error ) {
181+ reject ( error ) ;
182+ } else {
183+ resolve ( ) ;
184+ }
185+ } ) ;
179186 } ) ;
180187 } catch ( error ) {
181188 // Consistent with every other failure path here: report to stderr
0 commit comments