@@ -237,7 +237,20 @@ const privateConsole = new WeakMap()
237237 */
238238const privateConstructorArgs = new WeakMap ( )
239239
240- const consoleSymbols = Object . getOwnPropertySymbols ( globalConsole )
240+ /**
241+ * Lazily get console symbols on first access.
242+ *
243+ * Deferred to avoid accessing global console during early Node.js bootstrap
244+ * before stdout is ready.
245+ * @private
246+ */
247+ let _consoleSymbols : symbol [ ] | undefined
248+ function getConsoleSymbols ( ) : symbol [ ] {
249+ if ( _consoleSymbols === undefined ) {
250+ _consoleSymbols = Object . getOwnPropertySymbols ( globalConsole )
251+ }
252+ return _consoleSymbols
253+ }
241254
242255/**
243256 * Symbol for incrementing the internal log call counter.
@@ -247,9 +260,19 @@ const consoleSymbols = Object.getOwnPropertySymbols(globalConsole)
247260 */
248261export const incLogCallCountSymbol = Symbol . for ( 'logger.logCallCount++' )
249262
250- const kGroupIndentationWidthSymbol =
251- consoleSymbols . find ( s => ( s as any ) . label === 'kGroupIndentWidth' ) ??
252- Symbol ( 'kGroupIndentWidth' )
263+ /**
264+ * Lazily get kGroupIndentationWidth symbol on first access.
265+ * @private
266+ */
267+ let _kGroupIndentationWidthSymbol : symbol | undefined
268+ function getKGroupIndentationWidthSymbol ( ) : symbol {
269+ if ( _kGroupIndentationWidthSymbol === undefined ) {
270+ _kGroupIndentationWidthSymbol =
271+ getConsoleSymbols ( ) . find ( s => ( s as any ) . label === 'kGroupIndentWidth' ) ??
272+ Symbol ( 'kGroupIndentWidth' )
273+ }
274+ return _kGroupIndentationWidthSymbol
275+ }
253276
254277/**
255278 * Symbol for tracking whether the last logged line was blank.
@@ -392,6 +415,9 @@ export class Logger {
392415 * @private
393416 */
394417 #getConsole( ) : typeof console & Record < string , unknown > {
418+ // Ensure prototype is initialized before creating Console.
419+ ensurePrototypeInitialized ( )
420+
395421 let con = privateConsole . get ( this )
396422 if ( ! con ) {
397423 // Lazy initialization - create Console on first use.
@@ -971,7 +997,7 @@ export class Logger {
971997 if ( length ) {
972998 ReflectApply ( this . log , this , label )
973999 }
974- this . indent ( ( this as any ) [ kGroupIndentationWidthSymbol ] )
1000+ this . indent ( ( this as any ) [ getKGroupIndentationWidthSymbol ( ) ] )
9751001 if ( length ) {
9761002 ; ( this as any ) [ lastWasBlankSymbol ] ( false )
9771003 ; ( this as any ) [ incLogCallCountSymbol ] ( )
@@ -1017,7 +1043,7 @@ export class Logger {
10171043 * ```
10181044 */
10191045 groupEnd ( ) {
1020- this . dedent ( ( this as any ) [ kGroupIndentationWidthSymbol ] )
1046+ this . dedent ( ( this as any ) [ getKGroupIndentationWidthSymbol ( ) ] )
10211047 return this
10221048 }
10231049
@@ -1560,71 +1586,79 @@ export class Logger {
15601586 }
15611587}
15621588
1563- Object . defineProperties (
1564- Logger . prototype ,
1565- Object . fromEntries (
1566- ( ( ) => {
1567- const entries : Array < [ string | symbol , PropertyDescriptor ] > = [
1568- [
1569- kGroupIndentationWidthSymbol ,
1570- {
1571- ...consolePropAttributes ,
1572- value : 2 ,
1573- } ,
1574- ] ,
1575- [
1576- Symbol . toStringTag ,
1577- {
1578- __proto__ : null ,
1579- configurable : true ,
1580- value : 'logger' ,
1581- } as PropertyDescriptor ,
1582- ] ,
1583- ]
1584- for ( const { 0 : key , 1 : value } of Object . entries ( globalConsole ) ) {
1585- if ( ! ( Logger . prototype as any ) [ key ] && typeof value === 'function' ) {
1586- // Dynamically name the log method without using Object.defineProperty.
1587- const { [ key ] : func } = {
1588- [ key ] ( this : Logger , ...args : unknown [ ] ) {
1589- // Access Console via WeakMap directly since private methods can't be
1590- // called from dynamically created functions.
1591- let con = privateConsole . get ( this )
1592- if ( con === undefined ) {
1593- // Lazy initialization - this will only happen if someone calls a
1594- // dynamically added console method before any core logger method.
1595- const ctorArgs = privateConstructorArgs . get ( this ) ?? [ ]
1596- // Clean up constructor args - no longer needed after Console creation.
1597- privateConstructorArgs . delete ( this )
1598- if ( ctorArgs . length ) {
1599- con = constructConsole ( ...ctorArgs )
1600- } else {
1601- con = constructConsole ( {
1602- stdout : process . stdout ,
1603- stderr : process . stderr ,
1604- } ) as typeof console & Record < string , unknown >
1605- for ( const { 0 : k , 1 : method } of boundConsoleEntries ) {
1606- con [ k ] = method
1607- }
1608- }
1609- privateConsole . set ( this , con )
1589+ /**
1590+ * Lazily add dynamic console methods to Logger prototype.
1591+ *
1592+ * This is deferred until first access to avoid calling Object.entries(globalConsole)
1593+ * during early Node.js bootstrap before stdout is ready.
1594+ * @private
1595+ */
1596+ let _prototypeInitialized = false
1597+ function ensurePrototypeInitialized ( ) {
1598+ if ( _prototypeInitialized ) {
1599+ return
1600+ }
1601+ _prototypeInitialized = true
1602+
1603+ const entries : Array < [ string | symbol , PropertyDescriptor ] > = [
1604+ [
1605+ getKGroupIndentationWidthSymbol ( ) ,
1606+ {
1607+ ...consolePropAttributes ,
1608+ value : 2 ,
1609+ } ,
1610+ ] ,
1611+ [
1612+ Symbol . toStringTag ,
1613+ {
1614+ __proto__ : null ,
1615+ configurable : true ,
1616+ value : 'logger' ,
1617+ } as PropertyDescriptor ,
1618+ ] ,
1619+ ]
1620+ for ( const { 0 : key , 1 : value } of Object . entries ( globalConsole ) ) {
1621+ if ( ! ( Logger . prototype as any ) [ key ] && typeof value === 'function' ) {
1622+ // Dynamically name the log method without using Object.defineProperty.
1623+ const { [ key ] : func } = {
1624+ [ key ] ( this : Logger , ...args : unknown [ ] ) {
1625+ // Access Console via WeakMap directly since private methods can't be
1626+ // called from dynamically created functions.
1627+ let con = privateConsole . get ( this )
1628+ if ( con === undefined ) {
1629+ // Lazy initialization - this will only happen if someone calls a
1630+ // dynamically added console method before any core logger method.
1631+ const ctorArgs = privateConstructorArgs . get ( this ) ?? [ ]
1632+ // Clean up constructor args - no longer needed after Console creation.
1633+ privateConstructorArgs . delete ( this )
1634+ if ( ctorArgs . length ) {
1635+ con = constructConsole ( ...ctorArgs )
1636+ } else {
1637+ con = constructConsole ( {
1638+ stdout : process . stdout ,
1639+ stderr : process . stderr ,
1640+ } ) as typeof console & Record < string , unknown >
1641+ for ( const { 0 : k , 1 : method } of boundConsoleEntries ) {
1642+ con [ k ] = method
16101643 }
1611- const result = ( con as any ) [ key ] ( ...args )
1612- return result === undefined || result === con ? this : result
1613- } ,
1644+ }
1645+ privateConsole . set ( this , con )
16141646 }
1615- entries . push ( [
1616- key ,
1617- {
1618- ...consolePropAttributes ,
1619- value : func ,
1620- } ,
1621- ] )
1622- }
1647+ const result = ( con as any ) [ key ] ( ...args )
1648+ return result === undefined || result === con ? this : result
1649+ } ,
16231650 }
1624- return entries
1625- } ) ( ) ,
1626- ) ,
1627- )
1651+ entries . push ( [
1652+ key ,
1653+ {
1654+ ...consolePropAttributes ,
1655+ value : func ,
1656+ } ,
1657+ ] )
1658+ }
1659+ }
1660+ Object . defineProperties ( Logger . prototype , Object . fromEntries ( entries ) )
1661+ }
16281662
16291663/**
16301664 * Default logger instance for the application.
0 commit comments