| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 453bd18 commit 73c3a3d
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -60,17 +60,21 @@ let cliTable; | |||
| 60 | 60 | ||
| 61 | 61 | // Track amount of indentation required via `console.group()`. | |
| 62 | 62 | const kGroupIndent = Symbol('kGroupIndent'); | |
| 63 | - | ||
| 64 | 63 | const kFormatForStderr = Symbol('kFormatForStderr'); | |
| 65 | 64 | const kFormatForStdout = Symbol('kFormatForStdout'); | |
| 66 | 65 | const kGetInspectOptions = Symbol('kGetInspectOptions'); | |
| 67 | 66 | const kColorMode = Symbol('kColorMode'); | |
| 67 | + const kIsConsole = Symbol('kIsConsole'); | ||
| 68 | 68 | ||
| 69 | 69 | function Console(options /* or: stdout, stderr, ignoreErrors = true */) { | |
| 70 | - if (!(this instanceof Console)) { | ||
| 70 | + // We have to test new.target here to see if this function is called | ||
| 71 | + // with new, because we need to define a custom instanceof to accommodate | ||
| 72 | + // the global console. | ||
| 73 | + if (!new.target) { | ||
| 71 | 74 | return new Console(...arguments); | |
| 72 | 75 | } | |
| 73 | 76 | ||
| 77 | + this[kIsConsole] = true; | ||
| 74 | 78 | if (!options || typeof options.write === 'function') { | |
| 75 | 79 | options = { | |
| 76 | 80 | stdout: options, | |
@@ -128,7 +132,7 @@ function Console(options /* or: stdout, stderr, ignoreErrors = true */) { | |||
| 128 | 132 | var keys = Object.keys(Console.prototype); | |
| 129 | 133 | for (var v = 0; v < keys.length; v++) { | |
| 130 | 134 | var k = keys[v]; | |
| 131 | - this[k] = this[k].bind(this); | ||
| 135 | + this[k] = Console.prototype[k].bind(this); | ||
| 132 | 136 | } | |
| 133 | 137 | } | |
| 134 | 138 | ||
@@ -470,10 +474,50 @@ Console.prototype.table = function(tabularData, properties) { | |||
| 470 | 474 | return final(keys, values); | |
| 471 | 475 | }; | |
| 472 | 476 | ||
| 473 | - module.exports = new Console({ | ||
| 477 | + function noop() {} | ||
| 478 | + | ||
| 479 | + // See https://console.spec.whatwg.org/#console-namespace | ||
| 480 | + // > For historical web-compatibility reasons, the namespace object | ||
| 481 | + // > for console must have as its [[Prototype]] an empty object, | ||
| 482 | + // > created as if by ObjectCreate(%ObjectPrototype%), | ||
| 483 | + // > instead of %ObjectPrototype%. | ||
| 484 | + | ||
| 485 | + // Since in Node.js, the Console constructor has been exposed through | ||
| 486 | + // require('console'), we need to keep the Console constructor but | ||
| 487 | + // we cannot actually use `new Console` to construct the global console. | ||
| 488 | + // Therefore, the console.Console.prototype is not | ||
| 489 | + // in the global console prototype chain anymore. | ||
| 490 | + const globalConsole = Object.create({}); | ||
| 491 | + const tempConsole = new Console({ | ||
| 474 | 492 | stdout: process.stdout, | |
| 475 | 493 | stderr: process.stderr | |
| 476 | 494 | }); | |
| 477 | - module.exports.Console = Console; | ||
| 478 | 495 | ||
| 479 | - function noop() {} | ||
| 496 | + // Since Console is not on the prototype chain of the global console, | ||
| 497 | + // the symbol properties on Console.prototype have to be looked up from | ||
| 498 | + // the global console itself. | ||
| 499 | + for (const prop of Object.getOwnPropertySymbols(Console.prototype)) { | ||
| 500 | + globalConsole[prop] = Console.prototype[prop]; | ||
| 501 | + } | ||
| 502 | + | ||
| 503 | + // Reflect.ownKeys() is used here for retrieving Symbols | ||
| 504 | + for (const prop of Reflect.ownKeys(tempConsole)) { | ||
| 505 | + const desc = { ...(Reflect.getOwnPropertyDescriptor(tempConsole, prop)) }; | ||
| 506 | + // Since Console would bind method calls onto the instance, | ||
| 507 | + // make sure the methods are called on globalConsole instead of | ||
| 508 | + // tempConsole. | ||
| 509 | + if (typeof Console.prototype[prop] === 'function') { | ||
| 510 | + desc.value = Console.prototype[prop].bind(globalConsole); | ||
| 511 | + } | ||
| 512 | + Reflect.defineProperty(globalConsole, prop, desc); | ||
| 513 | + } | ||
| 514 | + | ||
| 515 | + globalConsole.Console = Console; | ||
| 516 | + | ||
| 517 | + Object.defineProperty(Console, Symbol.hasInstance, { | ||
| 518 | + value(instance) { | ||
| 519 | + return instance[kIsConsole]; | ||
| 520 | + } | ||
| 521 | + }); | ||
| 522 | + | ||
| 523 | + module.exports = globalConsole; | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,7 +23,8 @@ | |||
| 23 | 23 | const common = require('../common'); | |
| 24 | 24 | const assert = require('assert'); | |
| 25 | 25 | const Stream = require('stream'); | |
| 26 | - const Console = require('console').Console; | ||
| 26 | + const requiredConsole = require('console'); | ||
| 27 | + const Console = requiredConsole.Console; | ||
| 27 | 28 | ||
| 28 | 29 | const out = new Stream(); | |
| 29 | 30 | const err = new Stream(); | |
@@ -35,6 +36,11 @@ process.stdout.write = process.stderr.write = common.mustNotCall(); | |||
| 35 | 36 | // Make sure that the "Console" function exists. | |
| 36 | 37 | assert.strictEqual(typeof Console, 'function'); | |
| 37 | 38 | ||
| 39 | + assert.strictEqual(requiredConsole, global.console); | ||
| 40 | + // Make sure the custom instanceof of Console works | ||
| 41 | + assert.ok(global.console instanceof Console); | ||
| 42 | + assert.ok(!({} instanceof Console)); | ||
| 43 | + | ||
| 38 | 44 | // Make sure that the Console constructor throws | |
| 39 | 45 | // when not given a writable stream instance. | |
| 40 | 46 | common.expectsError( | |
@@ -62,46 +68,57 @@ common.expectsError( | |||
| 62 | 68 | ||
| 63 | 69 | out.write = err.write = (d) => {}; | |
| 64 | 70 | ||
| 65 | - const c = new Console(out, err); | ||
| 71 | + { | ||
| 72 | + const c = new Console(out, err); | ||
| 73 | + assert.ok(c instanceof Console); | ||
| 66 | 74 | ||
| 67 | - out.write = err.write = common.mustCall((d) => { | ||
| 68 | - assert.strictEqual(d, 'test\n'); | ||
| 69 | - }, 2); | ||
| 75 | + out.write = err.write = common.mustCall((d) => { | ||
| 76 | + assert.strictEqual(d, 'test\n'); | ||
| 77 | + }, 2); | ||
| 70 | 78 | ||
| 71 | - c.log('test'); | ||
| 72 | - c.error('test'); | ||
| 79 | + c.log('test'); | ||
| 80 | + c.error('test'); | ||
| 73 | 81 | ||
| 74 | - out.write = common.mustCall((d) => { | ||
| 75 | - assert.strictEqual(d, '{ foo: 1 }\n'); | ||
| 76 | - }); | ||
| 82 | + out.write = common.mustCall((d) => { | ||
| 83 | + assert.strictEqual(d, '{ foo: 1 }\n'); | ||
| 84 | + }); | ||
| 77 | 85 | ||
| 78 | - c.dir({ foo: 1 }); | ||
| 86 | + c.dir({ foo: 1 }); | ||
| 79 | 87 | ||
| 80 | - // Ensure that the console functions are bound to the console instance. | ||
| 81 | - let called = 0; | ||
| 82 | - out.write = common.mustCall((d) => { | ||
| 83 | - called++; | ||
| 84 | - assert.strictEqual(d, `${called} ${called - 1} [ 1, 2, 3 ]\n`); | ||
| 85 | - }, 3); | ||
| 88 | + // Ensure that the console functions are bound to the console instance. | ||
| 89 | + let called = 0; | ||
| 90 | + out.write = common.mustCall((d) => { | ||
| 91 | + called++; | ||
| 92 | + assert.strictEqual(d, `${called} ${called - 1} [ 1, 2, 3 ]\n`); | ||
| 93 | + }, 3); | ||
| 86 | 94 | ||
| 87 | - [1, 2, 3].forEach(c.log); | ||
| 95 | + [1, 2, 3].forEach(c.log); | ||
| 96 | + } | ||
| 88 | 97 | ||
| 89 | - // Console() detects if it is called without `new` keyword. | ||
| 90 | - Console(out, err); | ||
| 98 | + // Test calling Console without the `new` keyword. | ||
| 99 | + { | ||
| 100 | + const withoutNew = Console(out, err); | ||
| 101 | + assert.ok(withoutNew instanceof Console); | ||
| 102 | + } | ||
| 91 | 103 | ||
| 92 | - // Extending Console works. | ||
| 93 | - class MyConsole extends Console { | ||
| 94 | - hello() {} | ||
| 104 | + // Test extending Console | ||
| 105 | + { | ||
| 106 | + class MyConsole extends Console { | ||
| 107 | + hello() {} | ||
| 108 | + } | ||
| 109 | + const myConsole = new MyConsole(process.stdout); | ||
| 110 | + assert.strictEqual(typeof myConsole.hello, 'function'); | ||
| 111 | + assert.ok(myConsole instanceof Console); | ||
| 95 | 112 | } | |
| 96 | - const myConsole = new MyConsole(process.stdout); | ||
| 97 | - assert.strictEqual(typeof myConsole.hello, 'function'); | ||
| 98 | 113 | ||
| 99 | 114 | // Instance that does not ignore the stream errors. | |
| 100 | - const c2 = new Console(out, err, false); | ||
| 115 | + { | ||
| 116 | + const c2 = new Console(out, err, false); | ||
| 101 | 117 | ||
| 102 | - out.write = () => { throw new Error('out'); }; | ||
| 103 | - err.write = () => { throw new Error('err'); }; | ||
| 118 | + out.write = () => { throw new Error('out'); }; | ||
| 119 | + err.write = () => { throw new Error('err'); }; | ||
| 104 | 120 | ||
| 105 | - assert.throws(() => c2.log('foo'), /^Error: out$/); | ||
| 106 | - assert.throws(() => c2.warn('foo'), /^Error: err$/); | ||
| 107 | - assert.throws(() => c2.dir('foo'), /^Error: out$/); | ||
| 121 | + assert.throws(() => c2.log('foo'), /^Error: out$/); | ||
| 122 | + assert.throws(() => c2.warn('foo'), /^Error: err$/); | ||
| 123 | + assert.throws(() => c2.dir('foo'), /^Error: out$/); | ||
| 124 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments