UNPKG

tilg

Version:

A tiny logger hook for debugging React components.

320 lines (318 loc) 11 kB
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 = {});