Skip to content

Instantly share code, notes, and snippets.

@lydell
Created October 30, 2022 13:05
Show Gist options
  • Save lydell/20bb50d84addac671fe0985d17cc4a26 to your computer and use it in GitHub Desktop.
Save lydell/20bb50d84addac671fe0985d17cc4a26 to your computer and use it in GitHub Desktop.
Patch for Elm’s Debug.log that gives you an interactive value in the console
var _Debug_log = F2(function(tag, value)
{
console.log(tag + ': ' + _Debug_toString(value));
return value;
});
////////////////////////// START Debug.log replacement //////////////////////////
var _Debug_log = F2(function(tag, value)
{
const js = _Debug_toJs(value);
console.log(tag + ':', typeof js === 'string' ? new String_(js) : js);
return value;
});
// Firefox does not print the name of user-defined classes in the console.
// Instead, it prints the name of the "native" object it (eventually) inherits from,
// like Object, Array or Error. This means that:
// - Char is displayed as string.
// - Tuple, List and Array are all displayed as arrays.
// - Unit is displayed as an empty array.
// - Dict is displayed as Map.
// - Custom type constructor names are shown as the first array element.
// Note: We can't use the user agent to detect Firefox, because this needs to work
// even when the responsive design mode is enabled (which also swaps the user agent).
const IS_FIREFOX = 'MozAppearance' in document.documentElement.style;
class Array_ extends Array {}
class Char extends Array {}
class Dict extends Map {}
class Internals {}
class List extends Array {}
class String_ extends Array {}
class Tuple extends Array {}
class Unit extends Array {}
class WeirdConstructor extends Array {}
const _DEBUG_UPPER_NAME = /^\p{Lu}\p{ID_Continue}*$/u;
// Originally based on _Debug_toAnsiString.
function _Debug_toJs(value) {
if (typeof value !== 'object' || value === null) {
return value;
}
if (value instanceof String) {
return IS_FIREFOX ? value.toString() : new Char(value.toString());
}
if (value.constructor.name !== 'Object') {
return value;
}
if ('$' in value) {
// It can be a number for internals (like Json and Html).
const tag = value.$.toString();
if (tag === '#0') {
return new Unit();
}
if (tag[0] === '#') {
const output = new Tuple();
for (const key in value) {
if (key === '$') continue;
output.push(_Debug_toJs(value[key]));
}
return output;
}
if (tag === 'Set_elm_builtin') {
return new Set(_Debug_toJsArray($elm$core$Set$toList(value), []));
}
if (tag === 'RBNode_elm_builtin' || tag === 'RBEmpty_elm_builtin') {
return new Dict(_Debug_toJsArray($elm$core$Dict$toList(value), []));
}
if (tag === 'Array_elm_builtin') {
return _Debug_toJsArray($elm$core$Array$toList(value), new Array_());
}
if (tag === '::' || tag === '[]') {
return _Debug_toJsArray(value, new List());
}
if (!_DEBUG_UPPER_NAME.test(tag)) {
const output = new Internals();
for (const key in value) {
output[key] = value[key];
}
return output;
}
let output;
if (IS_FIREFOX) {
output = [tag];
} else {
try {
const constructor = new Function('return class ' + tag + ' extends Array {}')();
output = new constructor();
} catch (error) {
output = new WeirdConstructor(tag);
output.push(tag);
output.error = error;
}
}
for (const key in value) {
if (key === '$') continue;
output.push(_Debug_toJs(value[key]));
}
return output;
}
const output = {};
for (const key in value) {
const field = key[0] === '_' ? key.slice(1) : key;
output[field] = _Debug_toJs(value[key]);
}
return output;
}
function _Debug_toJsArray(value, output) {
for (; value.b; value = value.b) {
output.push(_Debug_toJs(value.a));
}
return output;
}
////////////////////////// END Debug.log replacement //////////////////////////
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment