tilg
Version:
A tiny logger hook for debugging React components.
320 lines (318 loc) • 11 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/development.ts
var development_exports = {};
__export(development_exports, {
default: () => useTilg
});
module.exports = __toCommonJS(development_exports);
var import_react = __toESM(require("react"));
function inIframe() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
var IS_BROWSER = typeof window !== "undefined" && window.navigator.product === "Gecko";
var IS_UNSUPPORTED_CONSOLE = IS_BROWSER && /(\.stackblitz\.io|\.csb\.app)$/.test(location.host) && inIframe();
var SEPARATOR = /[-–—!$%^&*()_+|~=`{}\[\]:\/\\"'“”‘’;<>?,.@#\s\n\t\r]$/;
function md(strings, args = [], hints = {}, trace = "") {
const disableStyling = IS_BROWSER && IS_UNSUPPORTED_CONSOLE && args.some((arg) => {
return typeof arg === "function" || arg && typeof arg === "object";
});
let tokens = {};
let formatted = "";
let char = "";
const result = [];
const styles = [];
function process(type, open, close, next, prev) {
if (tokens[type] && (typeof next === "undefined" || SEPARATOR.test(next))) {
if (!disableStyling) {
if (IS_BROWSER) {
if (formatted.endsWith("%c")) {
styles[styles.length - 1] += close;
} else {
formatted += "%c";
styles.push(close);
}
} else {
formatted += close;
}
}
tokens[type] = false;
char = void 0;
} else if (!tokens[type] && (typeof prev === "undefined" || SEPARATOR.test(prev))) {
if (!disableStyling) {
if (IS_BROWSER) {
if (formatted.endsWith("%c")) {
styles[styles.length - 1] += open;
} else {
formatted += "%c";
styles.push(open);
}
} else {
formatted += open;
}
}
tokens[type] = true;
char = void 0;
} else {
char = type;
}
}
for (let i = 0; i < strings.length; i++) {
formatted = "";
let prev = void 0;
const str = strings[i];
for (let j = 0; j < str.length; j++) {
char = str[j];
if (char === "*") {
if (str[j + 1] === "*") {
j++;
process("**", IS_BROWSER ? "font-weight: bold;" : "\x1B[1m", IS_BROWSER ? "font-weight: normal;" : "\x1B[22m", str[j + 1], prev);
} else {
process("*", IS_BROWSER ? "font-style: italic;" : "\x1B[3m", IS_BROWSER ? "font-style: normal;" : "\x1B[23m", str[j + 1], prev);
}
} else if (char === "_") {
if (str[j + 1] === "_") {
j++;
process("__", IS_BROWSER ? "font-weight: bold;" : "\x1B[1m", IS_BROWSER ? "font-weight: normal;" : "\x1B[22m", str[j + 1], prev);
} else {
process("_", IS_BROWSER ? "font-style: italic;" : "\x1B[3m", IS_BROWSER ? "font-style: normal;" : "\x1B[23m", str[j + 1], prev);
}
} else if (char === "`") {
process("`", IS_BROWSER ? "background: hsla(0,0%,70%,.3); border-radius:3px; padding: 0 2px;" : "\x1B[96m\x1B[1m", IS_BROWSER ? "background: unset;" : "\x1B[39m\x1B[22m", str[j + 1], prev);
}
prev = char;
if (typeof char !== "undefined") {
formatted += char;
}
}
const hasPrevSlot = i > 0;
const hasNextSlot = i < args.length;
if (disableStyling) {
if (hasNextSlot && formatted.endsWith(" ")) {
formatted = formatted.slice(0, -1);
}
if (hasPrevSlot && formatted.startsWith(" ")) {
formatted = formatted.slice(1);
}
if (formatted !== "") {
result.push(formatted);
}
if (hasNextSlot) {
if (typeof args[i] === "string") {
result.push(JSON.stringify(args[i]));
} else {
result.push(args[i]);
}
}
} else {
if (!result.length)
result.push("");
if (hasNextSlot && hints[i]) {
process("~", IS_BROWSER ? "text-decoration: underline; text-decoration-color: green; text-decoration-style: wavy; padding-bottom: 1px; text-decoration-skip-ink: none;" : "", "");
}
if (formatted !== "") {
if (result.length) {
result[result.length - 1] += formatted;
} else {
result.push(formatted);
}
}
if (hasNextSlot) {
let serailized;
if (args[i] && (typeof args[i] === "object" || typeof args[i] === "function")) {
result[result.length - 1] += "%o";
styles.push(args[i]);
} else {
try {
serailized = JSON.stringify(args[i]);
} catch (e) {
serailized = "" + args[i];
}
result[result.length - 1] += serailized;
}
}
if (hasNextSlot && hints[i]) {
formatted = "";
process("~", IS_BROWSER ? "text-decoration: none; padding-bottom: 0;" : "", "");
result[result.length - 1] += formatted;
}
}
}
if (trace) {
if (disableStyling) {
result.push(` (@ ${trace})`);
} else {
if (!result.length)
result.push("");
if (IS_BROWSER) {
result[result.length - 1] += `%c(@ ${trace})`;
styles.push("color: #999; font-style: italic; font-size: 0.9em; padding-left: 2em;");
} else {
result[result.length - 1] += ` \x1B[2m(@ ${trace})\x1B[22m`;
}
}
}
result.push(...styles);
return result;
}
function log(...args) {
console.log(...args);
}
var components = /* @__PURE__ */ new WeakMap();
var instances = /* @__PURE__ */ new Map();
function useTilgCurrentComponentContext() {
const owner = import_react.default.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner.current || { type: { name: "unknown" }, memoizedProps: void 0 };
const name = owner.type.name;
const id = owner.type;
let path = "";
let logPath = "";
const stack = new Error().stack.split("\n");
for (const line of stack) {
const match = line.match(/^\s*at\s+(.*?)\s+\((.*?)\)$/);
const callerName = match == null ? void 0 : match[1];
const callerPath = match == null ? void 0 : match[2];
if (callerPath)
path += callerPath + ",";
if (callerName && !callerName.startsWith("useTilg")) {
logPath = match[2];
}
if (callerName && !callerName.startsWith("use") && !/.+\.use.+/.test(callerName)) {
break;
}
}
return [name, owner, id, path, logPath];
}
function useTilg(...inlined) {
const mark = (0, import_react.useState)(Math.random())[0];
const [name, owner, id, hookPath, logPath] = useTilgCurrentComponentContext();
const compute = () => {
let hookId = 0;
if (!components.has(id)) {
components.set(id, []);
}
const hooks = components.get(id);
hookId = hooks.indexOf(hookPath);
if (hookId === -1) {
hookId = hooks.length;
hooks.push(hookPath);
}
let componentName = name;
if (name) {
componentName = "`<" + name + "/>`";
} else {
componentName = "Component";
}
if (hookId !== 0)
return [componentName, hookId, 0];
if (!instances.has(id)) {
instances.set(id, []);
}
const instanceMarks = instances.get(id);
let index = instanceMarks.indexOf(mark);
if (index === -1) {
index = instanceMarks.length;
instanceMarks.push(mark);
}
return [componentName, hookId, index];
};
(0, import_react.useEffect)(() => {
const [componentName, hookId, instanceId] = compute();
if (hookId !== 0)
return;
const note = instanceId > 0 ? ` (${instanceId + 1})` : "";
log(...md([`${componentName}${note} mounted.`]));
return () => {
if (!instances.has(id)) {
instances.set(id, []);
}
const instanceMarks = instances.get(id);
let index = instanceMarks.indexOf(mark);
if (index !== -1) {
instanceMarks[index] = instanceMarks[instanceMarks.length - 1];
instanceMarks.length--;
}
if (!components.has(id)) {
components.set(id, []);
}
const hooks = components.get(id);
index = hooks.indexOf(hookPath);
if (index !== -1) {
hooks[index] = hooks[hooks.length - 1];
hooks.length--;
}
log(...md([`${componentName}${note} unmounted.`]));
};
}, []);
const loggerEffectContent = (0, import_react.useRef)(null);
loggerEffectContent.current = null;
const loggerPrevArgsContent = (0, import_react.useRef)(null);
const loggerArgsContent = (0, import_react.useRef)([]);
loggerArgsContent.current = [];
(0, import_react.useLayoutEffect)(() => {
const [componentName, hookId, instanceId] = compute();
if (hookId === 0) {
const note = instanceId > 0 ? ` (${instanceId + 1})` : "";
log(...md([`${componentName}${note} rendered with props: \``, "`."], [owner.memoizedProps]));
}
let changed = false;
let changedHint = {};
const prev = loggerPrevArgsContent.current;
const now = loggerArgsContent.current;
if (!prev || prev.length !== now.length) {
changed = true;
} else {
for (let i = 0; i < prev.length; i++) {
if (prev[i] !== now[i]) {
changed = true;
changedHint[i] = true;
}
}
}
loggerPrevArgsContent.current = now;
if (changed && loggerEffectContent.current) {
loggerEffectContent.current(changedHint);
}
});
if (inlined.length > 0) {
loggerArgsContent.current = inlined;
loggerEffectContent.current = (hints) => {
const message = md(inlined.map((_, i) => i > 0 ? ", " : "").concat(""), inlined, hints, logPath);
log(...message);
};
return inlined[0];
}
return function useTilgInner(strings, ...args) {
loggerEffectContent.current = (hints) => {
const message = strings.length ? md(strings, args, hints, logPath) : "";
if (message) {
log(...message);
}
};
loggerArgsContent.current = args;
return args[0];
};
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {});