diff options
author | Joyee Cheung <joyeec9h3@gmail.com> | 2018-11-29 06:14:30 +0800 |
---|---|---|
committer | Joyee Cheung <joyeec9h3@gmail.com> | 2018-12-02 04:51:17 +0800 |
commit | ce8b0e196eca6816d77dda43f28e7da6b3784e6b (patch) | |
tree | 3f342b8aa462c07fd98bc3032e7e61a7266040d7 /lib/console.js | |
parent | adbf9477eb244b0f83a705b06269705967194f03 (diff) | |
download | node-new-ce8b0e196eca6816d77dda43f28e7da6b3784e6b.tar.gz |
lib: move lib/console.js to lib/internal/console/constructor.js
This is a broken commit: it's here so that git interpret this
as a file move and preserve most of the history of the Console
constructor.
PR-URL: https://github.com/nodejs/node/pull/24709
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'lib/console.js')
-rw-r--r-- | lib/console.js | 571 |
1 files changed, 0 insertions, 571 deletions
diff --git a/lib/console.js b/lib/console.js deleted file mode 100644 index ebd098e9af..0000000000 --- a/lib/console.js +++ /dev/null @@ -1,571 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; - -const { trace } = internalBinding('trace_events'); -const { - isStackOverflowError, - codes: { - ERR_CONSOLE_WRITABLE_STREAM, - ERR_INVALID_ARG_TYPE, - ERR_INVALID_ARG_VALUE, - }, -} = require('internal/errors'); -const { previewEntries } = internalBinding('util'); -const { Buffer: { isBuffer } } = require('buffer'); -const util = require('util'); -const { - isTypedArray, isSet, isMap, isSetIterator, isMapIterator, -} = util.types; -const kCounts = Symbol('counts'); - -const kTraceConsoleCategory = 'node,node.console'; -const kTraceCount = 'C'.charCodeAt(0); -const kTraceBegin = 'b'.charCodeAt(0); -const kTraceEnd = 'e'.charCodeAt(0); -const kTraceInstant = 'n'.charCodeAt(0); - -const { - keys: ObjectKeys, - values: ObjectValues, -} = Object; -const hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty); - -const { - isArray: ArrayIsArray, - from: ArrayFrom, -} = Array; - -// Lazy loaded for startup performance. -let cliTable; - -// Track amount of indentation required via `console.group()`. -const kGroupIndent = Symbol('kGroupIndent'); -const kFormatForStderr = Symbol('kFormatForStderr'); -const kFormatForStdout = Symbol('kFormatForStdout'); -const kGetInspectOptions = Symbol('kGetInspectOptions'); -const kColorMode = Symbol('kColorMode'); -const kIsConsole = Symbol('kIsConsole'); -const kWriteToConsole = Symbol('kWriteToConsole'); -const kBindProperties = Symbol('kBindProperties'); -const kBindStreamsEager = Symbol('kBindStreamsEager'); -const kBindStreamsLazy = Symbol('kBindStreamsLazy'); -const kUseStdout = Symbol('kUseStdout'); -const kUseStderr = Symbol('kUseStderr'); - -// This constructor is not used to construct the global console. -// It's exported for backwards compatibility. -function Console(options /* or: stdout, stderr, ignoreErrors = true */) { - // We have to test new.target here to see if this function is called - // with new, because we need to define a custom instanceof to accommodate - // the global console. - if (!new.target) { - return new Console(...arguments); - } - - if (!options || typeof options.write === 'function') { - options = { - stdout: options, - stderr: arguments[1], - ignoreErrors: arguments[2] - }; - } - - const { - stdout, - stderr = stdout, - ignoreErrors = true, - colorMode = 'auto' - } = options; - - if (!stdout || typeof stdout.write !== 'function') { - throw new ERR_CONSOLE_WRITABLE_STREAM('stdout'); - } - if (!stderr || typeof stderr.write !== 'function') { - throw new ERR_CONSOLE_WRITABLE_STREAM('stderr'); - } - - if (typeof colorMode !== 'boolean' && colorMode !== 'auto') - throw new ERR_INVALID_ARG_VALUE('colorMode', colorMode); - - // bind the prototype functions to this Console instance - var keys = Object.keys(Console.prototype); - for (var v = 0; v < keys.length; v++) { - var k = keys[v]; - // We have to bind the methods grabbed from the instance instead of from - // the prototype so that users extending the Console can override them - // from the prototype chain of the subclass. - this[k] = this[k].bind(this); - } - - this[kBindStreamsEager](stdout, stderr); - this[kBindProperties](ignoreErrors, colorMode); -} - -const consolePropAttributes = { - writable: true, - enumerable: false, - configurable: true -}; - -// Fixup global.console instanceof global.console.Console -Object.defineProperty(Console, Symbol.hasInstance, { - value(instance) { - return instance[kIsConsole]; - } -}); - -// Eager version for the Console constructor -Console.prototype[kBindStreamsEager] = function(stdout, stderr) { - Object.defineProperties(this, { - '_stdout': { ...consolePropAttributes, value: stdout }, - '_stderr': { ...consolePropAttributes, value: stderr } - }); -}; - -// Lazily load the stdout and stderr from an object so we don't -// create the stdio streams when they are not even accessed -Console.prototype[kBindStreamsLazy] = function(object) { - let stdout; - let stderr; - Object.defineProperties(this, { - '_stdout': { - enumerable: false, - configurable: true, - get() { - if (!stdout) stdout = object.stdout; - return stdout; - }, - set(value) { stdout = value; } - }, - '_stderr': { - enumerable: false, - configurable: true, - get() { - if (!stderr) { stderr = object.stderr; } - return stderr; - }, - set(value) { stderr = value; } - } - }); -}; - -Console.prototype[kBindProperties] = function(ignoreErrors, colorMode) { - Object.defineProperties(this, { - '_stdoutErrorHandler': { - ...consolePropAttributes, - value: createWriteErrorHandler(this, kUseStdout) - }, - '_stderrErrorHandler': { - ...consolePropAttributes, - value: createWriteErrorHandler(this, kUseStderr) - }, - '_ignoreErrors': { - ...consolePropAttributes, - value: Boolean(ignoreErrors) - }, - '_times': { ...consolePropAttributes, value: new Map() } - }); - - // TODO(joyeecheung): use consolePropAttributes for these - // Corresponds to https://console.spec.whatwg.org/#count-map - this[kCounts] = new Map(); - this[kColorMode] = colorMode; - this[kIsConsole] = true; - this[kGroupIndent] = ''; -}; - -// Make a function that can serve as the callback passed to `stream.write()`. -function createWriteErrorHandler(instance, streamSymbol) { - return (err) => { - // This conditional evaluates to true if and only if there was an error - // that was not already emitted (which happens when the _write callback - // is invoked asynchronously). - const stream = streamSymbol === kUseStdout ? - instance._stdout : instance._stderr; - if (err !== null && !stream._writableState.errorEmitted) { - // If there was an error, it will be emitted on `stream` as - // an `error` event. Adding a `once` listener will keep that error - // from becoming an uncaught exception, but since the handler is - // removed after the event, non-console.* writes won't be affected. - // we are only adding noop if there is no one else listening for 'error' - if (stream.listenerCount('error') === 0) { - stream.on('error', noop); - } - } - }; -} - -Console.prototype[kWriteToConsole] = function(streamSymbol, string) { - const ignoreErrors = this._ignoreErrors; - const groupIndent = this[kGroupIndent]; - - const useStdout = streamSymbol === kUseStdout; - const stream = useStdout ? this._stdout : this._stderr; - const errorHandler = useStdout ? - this._stdoutErrorHandler : this._stderrErrorHandler; - - if (groupIndent.length !== 0) { - if (string.indexOf('\n') !== -1) { - string = string.replace(/\n/g, `\n${groupIndent}`); - } - string = groupIndent + string; - } - string += '\n'; - - if (ignoreErrors === false) return stream.write(string); - - // There may be an error occurring synchronously (e.g. for files or TTYs - // on POSIX systems) or asynchronously (e.g. pipes on POSIX systems), so - // handle both situations. - try { - // Add and later remove a noop error handler to catch synchronous errors. - stream.once('error', noop); - - stream.write(string, errorHandler); - } catch (e) { - // console is a debugging utility, so it swallowing errors is not desirable - // even in edge cases such as low stack space. - if (isStackOverflowError(e)) - throw e; - // Sorry, there's no proper way to pass along the error here. - } finally { - stream.removeListener('error', noop); - } -}; - -const kColorInspectOptions = { colors: true }; -const kNoColorInspectOptions = {}; -Console.prototype[kGetInspectOptions] = function(stream) { - let color = this[kColorMode]; - if (color === 'auto') { - color = stream.isTTY && ( - typeof stream.getColorDepth === 'function' ? - stream.getColorDepth() > 2 : true); - } - - return color ? kColorInspectOptions : kNoColorInspectOptions; -}; - -Console.prototype[kFormatForStdout] = function(args) { - const opts = this[kGetInspectOptions](this._stdout); - return util.formatWithOptions(opts, ...args); -}; - -Console.prototype[kFormatForStderr] = function(args) { - const opts = this[kGetInspectOptions](this._stderr); - return util.formatWithOptions(opts, ...args); -}; - -Console.prototype.log = function log(...args) { - this[kWriteToConsole](kUseStdout, this[kFormatForStdout](args)); -}; - -Console.prototype.debug = Console.prototype.log; -Console.prototype.info = Console.prototype.log; -Console.prototype.dirxml = Console.prototype.log; - -Console.prototype.warn = function warn(...args) { - this[kWriteToConsole](kUseStderr, this[kFormatForStderr](args)); -}; - -Console.prototype.error = Console.prototype.warn; - -Console.prototype.dir = function dir(object, options) { - options = Object.assign({ - customInspect: false - }, this[kGetInspectOptions](this._stdout), options); - this[kWriteToConsole](kUseStdout, util.inspect(object, options)); -}; - -Console.prototype.time = function time(label = 'default') { - // Coerces everything other than Symbol to a string - label = `${label}`; - if (this._times.has(label)) { - process.emitWarning(`Label '${label}' already exists for console.time()`); - return; - } - trace(kTraceBegin, kTraceConsoleCategory, `time::${label}`, 0); - this._times.set(label, process.hrtime()); -}; - -Console.prototype.timeEnd = function timeEnd(label = 'default') { - // Coerces everything other than Symbol to a string - label = `${label}`; - const hasWarned = timeLogImpl(this, 'timeEnd', label); - trace(kTraceEnd, kTraceConsoleCategory, `time::${label}`, 0); - if (!hasWarned) { - this._times.delete(label); - } -}; - -Console.prototype.timeLog = function timeLog(label = 'default', ...data) { - // Coerces everything other than Symbol to a string - label = `${label}`; - timeLogImpl(this, 'timeLog', label, data); - trace(kTraceInstant, kTraceConsoleCategory, `time::${label}`, 0); -}; - -// Returns true if label was not found -function timeLogImpl(self, name, label, data) { - const time = self._times.get(label); - if (!time) { - process.emitWarning(`No such label '${label}' for console.${name}()`); - return true; - } - const duration = process.hrtime(time); - const ms = duration[0] * 1000 + duration[1] / 1e6; - if (data === undefined) { - self.log('%s: %sms', label, ms.toFixed(3)); - } else { - self.log('%s: %sms', label, ms.toFixed(3), ...data); - } - return false; -} - -Console.prototype.trace = function trace(...args) { - const err = { - name: 'Trace', - message: this[kFormatForStderr](args) - }; - Error.captureStackTrace(err, trace); - this.error(err.stack); -}; - -Console.prototype.assert = function assert(expression, ...args) { - if (!expression) { - args[0] = `Assertion failed${args.length === 0 ? '' : `: ${args[0]}`}`; - this.warn(...args); // the arguments will be formatted in warn() again - } -}; - -// Defined by: https://console.spec.whatwg.org/#clear -Console.prototype.clear = function clear() { - // It only makes sense to clear if _stdout is a TTY. - // Otherwise, do nothing. - if (this._stdout.isTTY) { - // The require is here intentionally to avoid readline being - // required too early when console is first loaded. - const { cursorTo, clearScreenDown } = require('readline'); - cursorTo(this._stdout, 0, 0); - clearScreenDown(this._stdout); - } -}; - -// Defined by: https://console.spec.whatwg.org/#count -Console.prototype.count = function count(label = 'default') { - // Ensures that label is a string, and only things that can be - // coerced to strings. e.g. Symbol is not allowed - label = `${label}`; - const counts = this[kCounts]; - let count = counts.get(label); - if (count === undefined) - count = 1; - else - count++; - counts.set(label, count); - trace(kTraceCount, kTraceConsoleCategory, `count::${label}`, 0, count); - this.log(`${label}: ${count}`); -}; - -// Defined by: https://console.spec.whatwg.org/#countreset -Console.prototype.countReset = function countReset(label = 'default') { - const counts = this[kCounts]; - if (!counts.has(label)) { - process.emitWarning(`Count for '${label}' does not exist`); - return; - } - trace(kTraceCount, kTraceConsoleCategory, `count::${label}`, 0, 0); - counts.delete(`${label}`); -}; - -Console.prototype.group = function group(...data) { - if (data.length > 0) { - this.log(...data); - } - this[kGroupIndent] += ' '; -}; -Console.prototype.groupCollapsed = Console.prototype.group; - -Console.prototype.groupEnd = function groupEnd() { - this[kGroupIndent] = - this[kGroupIndent].slice(0, this[kGroupIndent].length - 2); -}; - -const keyKey = 'Key'; -const valuesKey = 'Values'; -const indexKey = '(index)'; -const iterKey = '(iteration index)'; - -const isArray = (v) => ArrayIsArray(v) || isTypedArray(v) || isBuffer(v); - -// https://console.spec.whatwg.org/#table -Console.prototype.table = function(tabularData, properties) { - if (properties !== undefined && !ArrayIsArray(properties)) - throw new ERR_INVALID_ARG_TYPE('properties', 'Array', properties); - - if (tabularData === null || typeof tabularData !== 'object') - return this.log(tabularData); - - if (cliTable === undefined) cliTable = require('internal/cli_table'); - const final = (k, v) => this.log(cliTable(k, v)); - - const inspect = (v) => { - const opt = { depth: 0, maxArrayLength: 3 }; - if (v !== null && typeof v === 'object' && - !isArray(v) && ObjectKeys(v).length > 2) - opt.depth = -1; - Object.assign(opt, this[kGetInspectOptions](this._stdout)); - return util.inspect(v, opt); - }; - const getIndexArray = (length) => ArrayFrom({ length }, (_, i) => inspect(i)); - - const mapIter = isMapIterator(tabularData); - let isKeyValue = false; - let i = 0; - if (mapIter) { - const res = previewEntries(tabularData, true); - tabularData = res[0]; - isKeyValue = res[1]; - } - - if (isKeyValue || isMap(tabularData)) { - const keys = []; - const values = []; - let length = 0; - if (mapIter) { - for (; i < tabularData.length / 2; ++i) { - keys.push(inspect(tabularData[i * 2])); - values.push(inspect(tabularData[i * 2 + 1])); - length++; - } - } else { - for (const [k, v] of tabularData) { - keys.push(inspect(k)); - values.push(inspect(v)); - length++; - } - } - return final([ - iterKey, keyKey, valuesKey - ], [ - getIndexArray(length), - keys, - values, - ]); - } - - const setIter = isSetIterator(tabularData); - if (setIter) - tabularData = previewEntries(tabularData); - - const setlike = setIter || (mapIter && !isKeyValue) || isSet(tabularData); - if (setlike) { - const values = []; - let length = 0; - for (const v of tabularData) { - values.push(inspect(v)); - length++; - } - return final([setlike ? iterKey : indexKey, valuesKey], [ - getIndexArray(length), - values, - ]); - } - - const map = {}; - let hasPrimitives = false; - const valuesKeyArray = []; - const indexKeyArray = ObjectKeys(tabularData); - - for (; i < indexKeyArray.length; i++) { - const item = tabularData[indexKeyArray[i]]; - const primitive = item === null || - (typeof item !== 'function' && typeof item !== 'object'); - if (properties === undefined && primitive) { - hasPrimitives = true; - valuesKeyArray[i] = inspect(item); - } else { - const keys = properties || ObjectKeys(item); - for (const key of keys) { - if (map[key] === undefined) - map[key] = []; - if ((primitive && properties) || !hasOwnProperty(item, key)) - map[key][i] = ''; - else - map[key][i] = item == null ? item : inspect(item[key]); - } - } - } - - const keys = ObjectKeys(map); - const values = ObjectValues(map); - if (hasPrimitives) { - keys.push(valuesKey); - values.push(valuesKeyArray); - } - keys.unshift(indexKey); - values.unshift(indexKeyArray); - - return final(keys, values); -}; - -function noop() {} - -// See https://console.spec.whatwg.org/#console-namespace -// > For historical web-compatibility reasons, the namespace object -// > for console must have as its [[Prototype]] an empty object, -// > created as if by ObjectCreate(%ObjectPrototype%), -// > instead of %ObjectPrototype%. - -// Since in Node.js, the Console constructor has been exposed through -// require('console'), we need to keep the Console constructor but -// we cannot actually use `new Console` to construct the global console. -// Therefore, the console.Console.prototype is not -// in the global console prototype chain anymore. - -// TODO(joyeecheung): -// - Move the Console constructor into internal/console.js -// - Move the global console creation code along with the inspector console -// wrapping code in internal/bootstrap/node.js into a separate file. -// - Make this file a simple re-export of those two files. -const globalConsole = Object.create({}); - -// Since Console is not on the prototype chain of the global console, -// the symbol properties on Console.prototype have to be looked up from -// the global console itself. In addition, we need to make the global -// console a namespace by binding the console methods directly onto -// the global console with the receiver fixed. -for (const prop of Reflect.ownKeys(Console.prototype)) { - if (prop === 'constructor') { continue; } - const desc = Reflect.getOwnPropertyDescriptor(Console.prototype, prop); - if (typeof desc.value === 'function') { // fix the receiver - desc.value = desc.value.bind(globalConsole); - } - Reflect.defineProperty(globalConsole, prop, desc); -} - -globalConsole[kBindStreamsLazy](process); -globalConsole[kBindProperties](true, 'auto'); - -module.exports = globalConsole; -module.exports.Console = Console; |