@@ -65,7 +65,15 @@ const kFormatForStdout = Symbol('kFormatForStdout');
65
65
const kGetInspectOptions = Symbol ( 'kGetInspectOptions' ) ;
66
66
const kColorMode = Symbol ( 'kColorMode' ) ;
67
67
const kIsConsole = Symbol ( 'kIsConsole' ) ;
68
-
68
+ const kWriteToConsole = Symbol ( 'kWriteToConsole' ) ;
69
+ const kBindProperties = Symbol ( 'kBindProperties' ) ;
70
+ const kBindStreamsEager = Symbol ( 'kBindStreamsEager' ) ;
71
+ const kBindStreamsLazy = Symbol ( 'kBindStreamsLazy' ) ;
72
+ const kUseStdout = Symbol ( 'kUseStdout' ) ;
73
+ const kUseStderr = Symbol ( 'kUseStderr' ) ;
74
+
75
+ // This constructor is not used to construct the global console.
76
+ // It's exported for backwards compatibility.
69
77
function Console ( options /* or: stdout, stderr, ignoreErrors = true */ ) {
70
78
// We have to test new.target here to see if this function is called
71
79
// with new, because we need to define a custom instanceof to accommodate
@@ -74,7 +82,6 @@ function Console(options /* or: stdout, stderr, ignoreErrors = true */) {
74
82
return new Console ( ...arguments ) ;
75
83
}
76
84
77
- this [ kIsConsole ] = true ;
78
85
if ( ! options || typeof options . write === 'function' ) {
79
86
options = {
80
87
stdout : options ,
@@ -97,37 +104,9 @@ function Console(options /* or: stdout, stderr, ignoreErrors = true */) {
97
104
throw new ERR_CONSOLE_WRITABLE_STREAM ( 'stderr' ) ;
98
105
}
99
106
100
- const prop = {
101
- writable : true ,
102
- enumerable : false ,
103
- configurable : true
104
- } ;
105
- Object . defineProperty ( this , '_stdout' , { ...prop , value : stdout } ) ;
106
- Object . defineProperty ( this , '_stderr' , { ...prop , value : stderr } ) ;
107
- Object . defineProperty ( this , '_ignoreErrors' , {
108
- ...prop ,
109
- value : Boolean ( ignoreErrors ) ,
110
- } ) ;
111
- Object . defineProperty ( this , '_times' , { ...prop , value : new Map ( ) } ) ;
112
- Object . defineProperty ( this , '_stdoutErrorHandler' , {
113
- ...prop ,
114
- value : createWriteErrorHandler ( stdout ) ,
115
- } ) ;
116
- Object . defineProperty ( this , '_stderrErrorHandler' , {
117
- ...prop ,
118
- value : createWriteErrorHandler ( stderr ) ,
119
- } ) ;
120
-
121
107
if ( typeof colorMode !== 'boolean' && colorMode !== 'auto' )
122
108
throw new ERR_INVALID_ARG_VALUE ( 'colorMode' , colorMode ) ;
123
109
124
- // Corresponds to https://console.spec.whatwg.org/#count-map
125
- this [ kCounts ] = new Map ( ) ;
126
- this [ kColorMode ] = colorMode ;
127
-
128
- Object . defineProperty ( this , kGroupIndent , { writable : true } ) ;
129
- this [ kGroupIndent ] = '' ;
130
-
131
110
// Bind the prototype functions to this Console instance
132
111
var keys = Object . keys ( Console . prototype ) ;
133
112
for ( var v = 0 ; v < keys . length ; v ++ ) {
@@ -137,14 +116,92 @@ function Console(options /* or: stdout, stderr, ignoreErrors = true */) {
137
116
// from the prototype chain of the subclass.
138
117
this [ k ] = this [ k ] . bind ( this ) ;
139
118
}
119
+
120
+ this [ kBindStreamsEager ] ( stdout , stderr ) ;
121
+ this [ kBindProperties ] ( ignoreErrors , colorMode ) ;
140
122
}
141
123
124
+ const consolePropAttributes = {
125
+ writable : true ,
126
+ enumerable : false ,
127
+ configurable : true
128
+ } ;
129
+
130
+ // Fixup global.console instanceof global.console.Console
131
+ Object . defineProperty ( Console , Symbol . hasInstance , {
132
+ value ( instance ) {
133
+ return instance [ kIsConsole ] ;
134
+ }
135
+ } ) ;
136
+
137
+ // Eager version for the Console constructor
138
+ Console . prototype [ kBindStreamsEager ] = function ( stdout , stderr ) {
139
+ Object . defineProperties ( this , {
140
+ '_stdout' : { ...consolePropAttributes , value : stdout } ,
141
+ '_stderr' : { ...consolePropAttributes , value : stderr }
142
+ } ) ;
143
+ } ;
144
+
145
+ // Lazily load the stdout and stderr from an object so we don't
146
+ // create the stdio streams when they are not even accessed
147
+ Console . prototype [ kBindStreamsLazy ] = function ( object ) {
148
+ let stdout ;
149
+ let stderr ;
150
+ Object . defineProperties ( this , {
151
+ '_stdout' : {
152
+ enumerable : false ,
153
+ configurable : true ,
154
+ get ( ) {
155
+ if ( ! stdout ) stdout = object . stdout ;
156
+ return stdout ;
157
+ } ,
158
+ set ( value ) { stdout = value ; }
159
+ } ,
160
+ '_stderr' : {
161
+ enumerable : false ,
162
+ configurable : true ,
163
+ get ( ) {
164
+ if ( ! stderr ) { stderr = object . stderr ; }
165
+ return stderr ;
166
+ } ,
167
+ set ( value ) { stderr = value ; }
168
+ }
169
+ } ) ;
170
+ } ;
171
+
172
+ Console . prototype [ kBindProperties ] = function ( ignoreErrors , colorMode ) {
173
+ Object . defineProperties ( this , {
174
+ '_stdoutErrorHandler' : {
175
+ ...consolePropAttributes ,
176
+ value : createWriteErrorHandler ( this , kUseStdout )
177
+ } ,
178
+ '_stderrErrorHandler' : {
179
+ ...consolePropAttributes ,
180
+ value : createWriteErrorHandler ( this , kUseStderr )
181
+ } ,
182
+ '_ignoreErrors' : {
183
+ ...consolePropAttributes ,
184
+ value : Boolean ( ignoreErrors )
185
+ } ,
186
+ '_times' : { ...consolePropAttributes , value : new Map ( ) }
187
+ } ) ;
188
+
189
+ // TODO(joyeecheung): use consolePropAttributes for these
190
+ // Corresponds to https://console.spec.whatwg.org/#count-map
191
+ this [ kCounts ] = new Map ( ) ;
192
+ this [ kColorMode ] = colorMode ;
193
+ this [ kIsConsole ] = true ;
194
+ this [ kGroupIndent ] = '' ;
195
+ } ;
196
+
142
197
// Make a function that can serve as the callback passed to `stream.write()`.
143
- function createWriteErrorHandler ( stream ) {
198
+ function createWriteErrorHandler ( instance , streamSymbol ) {
144
199
return ( err ) => {
145
200
// This conditional evaluates to true if and only if there was an error
146
201
// that was not already emitted (which happens when the _write callback
147
202
// is invoked asynchronously).
203
+ const stream = streamSymbol === kUseStdout ?
204
+ instance . _stdout : instance . _stderr ;
148
205
if ( err !== null && ! stream . _writableState . errorEmitted ) {
149
206
// If there was an error, it will be emitted on `stream` as
150
207
// an `error` event. Adding a `once` listener will keep that error
@@ -158,7 +215,15 @@ function createWriteErrorHandler(stream) {
158
215
} ;
159
216
}
160
217
161
- function write ( ignoreErrors , stream , string , errorhandler , groupIndent ) {
218
+ Console . prototype [ kWriteToConsole ] = function ( streamSymbol , string ) {
219
+ const ignoreErrors = this . _ignoreErrors ;
220
+ const groupIndent = this [ kGroupIndent ] ;
221
+
222
+ const useStdout = streamSymbol === kUseStdout ;
223
+ const stream = useStdout ? this . _stdout : this . _stderr ;
224
+ const errorHandler = useStdout ?
225
+ this . _stdoutErrorHandler : this . _stderrErrorHandler ;
226
+
162
227
if ( groupIndent . length !== 0 ) {
163
228
if ( string . indexOf ( '\n' ) !== - 1 ) {
164
229
string = string . replace ( / \n / g, `\n${ groupIndent } ` ) ;
@@ -176,7 +241,7 @@ function write(ignoreErrors, stream, string, errorhandler, groupIndent) {
176
241
// Add and later remove a noop error handler to catch synchronous errors.
177
242
stream . once ( 'error' , noop ) ;
178
243
179
- stream . write ( string , errorhandler ) ;
244
+ stream . write ( string , errorHandler ) ;
180
245
} catch ( e ) {
181
246
// Console is a debugging utility, so it swallowing errors is not desirable
182
247
// even in edge cases such as low stack space.
@@ -186,7 +251,7 @@ function write(ignoreErrors, stream, string, errorhandler, groupIndent) {
186
251
} finally {
187
252
stream . removeListener ( 'error' , noop ) ;
188
253
}
189
- }
254
+ } ;
190
255
191
256
const kColorInspectOptions = { colors : true } ;
192
257
const kNoColorInspectOptions = { } ;
@@ -212,23 +277,17 @@ Console.prototype[kFormatForStderr] = function(args) {
212
277
} ;
213
278
214
279
Console . prototype . log = function log ( ...args ) {
215
- write ( this . _ignoreErrors ,
216
- this . _stdout ,
217
- this [ kFormatForStdout ] ( args ) ,
218
- this . _stdoutErrorHandler ,
219
- this [ kGroupIndent ] ) ;
280
+ this [ kWriteToConsole ] ( kUseStdout , this [ kFormatForStdout ] ( args ) ) ;
220
281
} ;
282
+
221
283
Console . prototype . debug = Console . prototype . log ;
222
284
Console . prototype . info = Console . prototype . log ;
223
285
Console . prototype . dirxml = Console . prototype . log ;
224
286
225
287
Console . prototype . warn = function warn ( ...args ) {
226
- write ( this . _ignoreErrors ,
227
- this . _stderr ,
228
- this [ kFormatForStderr ] ( args ) ,
229
- this . _stderrErrorHandler ,
230
- this [ kGroupIndent ] ) ;
288
+ this [ kWriteToConsole ] ( kUseStderr , this [ kFormatForStderr ] ( args ) ) ;
231
289
} ;
290
+
232
291
Console . prototype . error = Console . prototype . warn ;
233
292
234
293
Console . prototype . dir = function dir ( object , options ) {
@@ -237,11 +296,7 @@ Console.prototype.dir = function dir(object, options) {
237
296
...this [ kGetInspectOptions ] ( this . _stdout ) ,
238
297
...options
239
298
} ;
240
- write ( this . _ignoreErrors ,
241
- this . _stdout ,
242
- util . inspect ( object , options ) ,
243
- this . _stdoutErrorHandler ,
244
- this [ kGroupIndent ] ) ;
299
+ this [ kWriteToConsole ] ( kUseStdout , util . inspect ( object , options ) ) ;
245
300
} ;
246
301
247
302
Console . prototype . time = function time ( label = 'default' ) {
@@ -301,7 +356,7 @@ Console.prototype.trace = function trace(...args) {
301
356
Console . prototype . assert = function assert ( expression , ...args ) {
302
357
if ( ! expression ) {
303
358
args [ 0 ] = `Assertion failed${ args . length === 0 ? '' : `: ${ args [ 0 ] } ` } ` ;
304
- this . warn ( this [ kFormatForStderr ] ( args ) ) ;
359
+ this . warn ( ... args ) ; // the arguments will be formatted in warn() again
305
360
}
306
361
} ;
307
362
@@ -363,7 +418,6 @@ const valuesKey = 'Values';
363
418
const indexKey = '(index)' ;
364
419
const iterKey = '(iteration index)' ;
365
420
366
-
367
421
const isArray = ( v ) => ArrayIsArray ( v ) || isTypedArray ( v ) || isBuffer ( v ) ;
368
422
369
423
// https://console.spec.whatwg.org/#table
@@ -490,38 +544,31 @@ function noop() {}
490
544
// we cannot actually use `new Console` to construct the global console.
491
545
// Therefore, the console.Console.prototype is not
492
546
// in the global console prototype chain anymore.
547
+
548
+ // TODO(joyeecheung):
549
+ // - Move the Console constructor into internal/console.js
550
+ // - Move the global console creation code along with the inspector console
551
+ // wrapping code in internal/bootstrap/node.js into a separate file.
552
+ // - Make this file a simple re-export of those two files.
493
553
// This is only here for v11.x conflict resolution.
494
554
const globalConsole = Object . create ( Console . prototype ) ;
495
- const tempConsole = new Console ( {
496
- stdout : process . stdout ,
497
- stderr : process . stderr
498
- } ) ;
499
555
500
556
// Since Console is not on the prototype chain of the global console,
501
557
// the symbol properties on Console.prototype have to be looked up from
502
- // the global console itself.
503
- for ( const prop of Object . getOwnPropertySymbols ( Console . prototype ) ) {
504
- globalConsole [ prop ] = Console . prototype [ prop ] ;
505
- }
506
-
507
- // Reflect.ownKeys() is used here for retrieving Symbols
508
- for ( const prop of Reflect . ownKeys ( tempConsole ) ) {
509
- const desc = { ...( Reflect . getOwnPropertyDescriptor ( tempConsole , prop ) ) } ;
510
- // Since Console would bind method calls onto the instance,
511
- // make sure the methods are called on globalConsole instead of
512
- // tempConsole.
513
- if ( typeof Console . prototype [ prop ] === 'function' ) {
514
- desc . value = Console . prototype [ prop ] . bind ( globalConsole ) ;
558
+ // the global console itself. In addition, we need to make the global
559
+ // console a namespace by binding the console methods directly onto
560
+ // the global console with the receiver fixed.
561
+ for ( const prop of Reflect . ownKeys ( Console . prototype ) ) {
562
+ if ( prop === 'constructor' ) { continue ; }
563
+ const desc = Reflect . getOwnPropertyDescriptor ( Console . prototype , prop ) ;
564
+ if ( typeof desc . value === 'function' ) { // fix the receiver
565
+ desc . value = desc . value . bind ( globalConsole ) ;
515
566
}
516
567
Reflect . defineProperty ( globalConsole , prop , desc ) ;
517
568
}
518
569
519
- globalConsole . Console = Console ;
520
-
521
- Object . defineProperty ( Console , Symbol . hasInstance , {
522
- value ( instance ) {
523
- return instance [ kIsConsole ] ;
524
- }
525
- } ) ;
570
+ globalConsole [ kBindStreamsLazy ] ( process ) ;
571
+ globalConsole [ kBindProperties ] ( true , 'auto' ) ;
526
572
527
573
module . exports = globalConsole ;
574
+ module . exports . Console = Console ;
0 commit comments