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
JavaScript
;
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