UNPKG

rc-js-util

Version:

A collection of TS and C++ utilities to help writing performant and correct applications, achieved through strict typing and (removable) invariant checking.

261 lines 8.98 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports._Debug = void 0; const get_global_js_1 = require("../runtime/get-global.js"); const array_add_to_set_js_1 = require("../array/impl/array-add-to-set.js"); const set_values_to_array_js_1 = require("../set/impl/set-values-to-array.js"); const string_concat_2_js_1 = require("../string/impl/string-concat-2.js"); /** * @public * Utilities for debug builds. */ class _Debug { static get label() { logDebugMisuse(); return _Debug._label; } static set label(label) { logDebugMisuse(); _Debug._label = label; } static applyLabel(label, callback) { _Debug.label = label; const ret = callback(); _Debug.label = undefined; return ret; } static labelBlock(label) { return (callback) => { if (_Debug.label != null && label != null) { label = (0, string_concat_2_js_1.stringConcat2)(_Debug.label, label, "\n"); } return _Debug.applyLabel(label, callback); }; } /** * Most debuggers will ignore `debugger` statements in node_modules. * Skirt around this by letting the consumer set their own callback for this. * * @param onBreakpoint - called on debug assert etc, you should provide a function with a `debugger` statement. * * @example * ```typescript * // the braces are not optional * _Debug.configureBreakpoint(() => { debugger; }) * ``` */ static configureBreakpoint(onBreakpoint) { _Debug.onBreakpoint = onBreakpoint; } /** * Convenience method to run multiple asserts. * * @returns A boolean value to make linting happy... * * @example * ```typescript * _BUILD.DEBUG && _Debug.runBlock(() => { * _Debug.assert(someCondition, "someCondition was wrong"); * // ... * }); * ``` */ static runBlock(cb) { logDebugMisuse(); cb(); return true; } /** * Convenience method to run multiple asserts if flag set. * * @returns A boolean value to make linting happy... * * @remarks * Must still be hidden behind _BUILD.DEBUG check for dead code removal. * * @example * ```typescript * _BUILD.DEBUG && _Debug.conditionalBlock("SOME_FLAG", () => { * _Debug.assert(someCondition, "someCondition was wrong"); * // ... * }); * ``` */ static conditionalBlock(flag, cb) { logDebugMisuse(); if (this.isFlagSet(flag)) { cb(); } return true; } /** * Throws an `Error` with the given message if the condition is false. * * @returns A boolean value to make linting happy... * * @example * ```typescript * function foo(a1: number) { * // not suitable for a production check, the programmer lied about the input type they supplied * _BUILD.DEBUG && _Debug.assert(a1 != null, "a1 must be supplied"); * } * ``` * * @remarks * If `_BUILD.DISABLE_BREAKPOINT_FLAG` is false or unset then a breakpoint will be hit first. * * * Debug asserts are useful for providing hints to the programmer that they aren't meeting the contract of the API. */ static assert(condition, errorMessage) { logDebugMisuse(); if (!condition) { if (!_Debug.isFlagSet("DISABLE_BREAKPOINT")) { _Debug.breakpoint(); } throw new Error(`assert fail: ${errorMessage}`); } return true; } /** * Throws an `Error` with the given message. * @returns A boolean value to make linting happy... will never return. * * @example * ```typescript * if (errorCondition) { * // in debug mode we error * _BUILD.DEBUG && _Debug.error("oopsy"); * // in production we fall back to some other behavior * return errorConditionValue; * } * ``` * * @remarks * If `_BUILD.DEBUG` is true and `_BUILD.DISABLE_BREAKPOINT` is false or unset then a breakpoint will be hit first. */ static error(message) { logDebugMisuse(); if (!_Debug.isFlagSet("DISABLE_BREAKPOINT")) { _Debug.breakpoint(); } throw new Error(message); } /** * Used in place of `debugger` statements when writing libraries. Should generally not be used directly. */ static breakpoint() { _Debug.onBreakpoint(); return true; } /** * Logging which can be conditionally enabled by setting either `VERBOSE` to true on build options (enabling everything), * or by calling setLoggingTags and specifying which tags you'd like enabled. * * @example * ```typescript * function foo(a1: number) { * _BUILD.DEBUG && _Debug.verboseLog(`got me a ${a1}`); * } * ``` */ static verboseLog(tags, message, ancillaryObject) { logDebugMisuse(); (0, array_add_to_set_js_1.arrayAddToSet)(tags, _Debug._seenTags); if (!_Debug.isFlagSet("VERBOSE")) { return; } // by default, all logging is enabled const enabledTags = _Debug._enabledTags; if (enabledTags != null && !tags.some(tag => enabledTags.has(tag))) { return; } // if any tag is disabled, the message should not be logged const disabledTags = _Debug._disabledTags; if (disabledTags != null && tags.some(tag => disabledTags.has(tag))) { return; } const tagString = tags.length > 0 ? ["[", tags.join(", "), "]"].join("") : ""; const labelString = _Debug.label == null ? "" : [_Debug.label, ":"].join(""); const prefix = [tagString, labelString].join(" ").trim(); const prefixedMessage = [prefix, message] .join(" ") .trimStart(); if (ancillaryObject == null) { // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access console.debug(prefixedMessage); } else { // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access console.debug(prefixedMessage, ancillaryObject); } } static logError(message) { logDebugMisuse(); // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access console.error(message); } static getStackTrace() { const error = new Error(); let stack = error.stack; if (stack == null) { try { // noinspection ExceptionCaughtLocallyJS throw error; } catch (e) { stack = e.stack; } } return stack.toString(); } /** * Used to set debug flags in an environment independent way. */ static setFlag(flag, value) { var _a; var _b; const build = ((_a = (_b = (0, get_global_js_1.getGlobal)())["_BUILD"]) !== null && _a !== void 0 ? _a : (_b["_BUILD"] = {})); build[flag] = value; } /** * Used to get debug flags in an environment independent way. */ static isFlagSet(flag) { var _a, _b; const build = ((_a = (0, get_global_js_1.getGlobal)()["_BUILD"]) !== null && _a !== void 0 ? _a : {}); return (_b = build[flag]) !== null && _b !== void 0 ? _b : false; } static setEnabledLoggingTags(tags) { _Debug._enabledTags = tags.length === 0 ? null : new Set(tags); _Debug._disabledTags = null; } static setDisabledLoggingTags(tags) { _Debug._enabledTags = null; _Debug._disabledTags = tags.length === 0 ? null : new Set(tags); } /** * @returns The tags that have been seen so far. Tags will be added as `verboseLog` is called.... */ static getTags() { return (0, set_values_to_array_js_1.setValuesToArray)(this._seenTags); } } exports._Debug = _Debug; _Debug.onBreakpoint = () => { // eslint-disable-next-line no-debugger debugger; }; _Debug._label = undefined; _Debug._enabledTags = null; _Debug._disabledTags = null; _Debug._seenTags = new Set(); // not every part of _Debug should be used outside _BUILD.DEBUG builds function logDebugMisuse() { if (!_BUILD.DEBUG) { // ideally we'd throw, but making the program less "strict" in a production build would be a source of disgustingly awful bugs // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access console.error("_Debug was called outside of a debug build, you should consider this an error. Use _Production instead."); } } //# sourceMappingURL=_debug.js.map