why-render-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [YEAR] [COPYRIGHT HOLDER]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,290 @@
1
+ # Why Render
2
+
3
+ React DevTools shows that something re-rendered. Why Render shows you why.
4
+
5
+ Add `useWhyRerender` to any component and get a console diff of exactly which props changed since the last render, so you can immediately tell whether a re-render was caused by a real prop change — or something else entirely.
6
+
7
+ ## Table of Contents
8
+
9
+ - [Install](#install)
10
+ - [Setup](#setup)
11
+ - [Wiring up global setup](#wiring-up-global-setup)
12
+ - [Usage](#usage)
13
+ - [Options](#options)
14
+ - [Examples](#examples)
15
+ - [1. Basic component usage](#1-basic-component-usage)
16
+ - [2. A component whose props change](#2-a-component-whose-props-change)
17
+ - [3. A component that re-renders without its props changing](#3-a-component-that-re-renders-without-its-props-changing)
18
+ - [4. Inspecting previous and next values with `verbose`](#4-inspecting-previous-and-next-values-with-verbose)
19
+ - [5. Temporarily disabling tracking for one component](#5-temporarily-disabling-tracking-for-one-component)
20
+ - [6. A realistic parent/child example](#6-a-realistic-parentchild-example)
21
+ - [Expected Output](#expected-output)
22
+ - [Troubleshooting](#troubleshooting)
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ npm install why-render-react
28
+ ```
29
+
30
+ ## Setup
31
+
32
+ Why Render is off by default. Call `setup()` once, before your app renders, to turn it on:
33
+
34
+ ```ts
35
+ import { setup } from "why-render-react"
36
+
37
+ setup(true)
38
+ ```
39
+
40
+ `setup()` also accepts strings, so you can wire it to an environment variable:
41
+
42
+ ```ts
43
+ setup(process.env.USE_WHY_RENDER) // enabled only if the value is "true" (case-insensitive)
44
+ ```
45
+
46
+ Pass `false` (or leave it unset) to keep Why Render off:
47
+
48
+ ```ts
49
+ setup(false)
50
+ ```
51
+
52
+ This is a one-time, app-wide switch — call it once at your entry point, not inside individual components.
53
+
54
+ > **Important:** if any component calls `useWhyRerender` before `setup()` has run, it will throw an error. Always call `setup()` first.
55
+
56
+ ### Wiring up global setup
57
+
58
+ `setup()` only needs to run once for your whole app — typically in your entry file, before rendering:
59
+
60
+ ```tsx
61
+ // main.tsx
62
+ import { setup } from "why-render-react"
63
+ import { createRoot } from "react-dom/client"
64
+ import App from "./App"
65
+
66
+ setup(process.env.NODE_ENV !== "production")
67
+
68
+ createRoot(document.getElementById("root")!).render(<App />)
69
+ ```
70
+
71
+ Every component that calls `useWhyRerender` anywhere in the tree will start (or stop) logging based on that single call. *(full file: [`examples/00-setup.tsx`](./examples/00-setup.tsx))*
72
+
73
+ ## Usage
74
+
75
+ Add `useWhyRerender` inside any component you want to debug, passing it a name and that component's current props:
76
+
77
+ ```tsx
78
+ import { useWhyRerender } from "why-render-react"
79
+
80
+ function CarCard(props: CarCardProps) {
81
+ useWhyRerender({ name: "CarCard", props })
82
+
83
+ return <div>{props.color}</div>
84
+ }
85
+ ```
86
+
87
+ It's a React hook, so it follows the same rules as other hooks — call it unconditionally, at the top level of your component. Every time `CarCard` renders, Why Render logs what changed (or didn't) since the last render.
88
+
89
+ ## Options
90
+
91
+ `useWhyRerender` takes a single options object:
92
+
93
+ | Option | Type | Required | Default | What it does |
94
+ | ------------ | ----------- | -------- | --------- | --------------------------------------------------------------------------------------------- |
95
+ | `name` | `string` | Yes | — | The label shown for this component in the console output. |
96
+ | `props` | `object` | Yes | — | The component's current props to track. |
97
+ | `verbose` | `boolean` | No | `false` | Also logs each changed prop's previous and next value. |
98
+ | `isActive` | `boolean` | No | `true` | Set to`false` to turn off logging for just this component, without affecting anything else. |
99
+
100
+ > Note: the field is `isActive`. If your editor or docs show `isActivate` anywhere, that's an internal naming detail — the option you pass is `isActive`.
101
+
102
+ ## Examples
103
+
104
+ ### 1. Basic component usage
105
+
106
+ *(full file: [`examples/01-basic-usage.tsx`](./examples/01-basic-usage.tsx))*
107
+
108
+ ```tsx
109
+ function CarCard(props: CarCardProps) {
110
+ useWhyRerender({ name: "CarCard", props })
111
+
112
+ return <div>{props.color}</div>
113
+ }
114
+ ```
115
+
116
+ ### 2. A component whose props change
117
+
118
+ *(full file: [`examples/02-props-change.tsx`](./examples/02-props-change.tsx))*
119
+
120
+ ```tsx
121
+ function CarCard({ color }: { color: string }) {
122
+ useWhyRerender({ name: "CarCard", props: { color } })
123
+ return <div>{color}</div>
124
+ }
125
+
126
+ function Garage() {
127
+ const [color, setColor] = useState("blue")
128
+
129
+ return (
130
+ <>
131
+ <button onClick={() => setColor("red")}>Repaint</button>
132
+ <CarCard color={color} />
133
+ </>
134
+ )
135
+ }
136
+ ```
137
+
138
+ Clicking "Repaint" changes `color` and re-renders `CarCard`. The console shows `color` as the prop that changed — confirming the re-render was caused by that prop.
139
+
140
+ ### 3. A component that re-renders without its props changing
141
+
142
+ Components can re-render for reasons that have nothing to do with their own props — most commonly, because their parent re-rendered. This is exactly the case Why Render helps you catch:
143
+
144
+ *(full file: [`examples/03-rerender-without-prop-change.tsx`](./examples/03-rerender-without-prop-change.tsx))*
145
+
146
+ ```tsx
147
+ function CarCard({ color }: { color: string }) {
148
+ useWhyRerender({ name: "CarCard", props: { color } })
149
+ return <div>{color}</div>
150
+ }
151
+
152
+ function Garage() {
153
+ const [, forceTick] = useState(0)
154
+
155
+ return (
156
+ <>
157
+ {/* Re-renders Garage (and CarCard) every second, but color never changes */}
158
+ <button onClick={() => forceTick((t) => t + 1)}>Refresh</button>
159
+ <CarCard color="blue" />
160
+ </>
161
+ )
162
+ }
163
+ ```
164
+
165
+ Clicking "Refresh" re-renders `Garage`, which re-renders `CarCard` even though `color` is still `"blue"`. Why Render logs that no props changed on that render — telling you to look at the parent, state, or context instead of the props.
166
+
167
+ ### 4. Inspecting previous and next values with `verbose`
168
+
169
+ *(full file: [`examples/04-verbose.tsx`](./examples/04-verbose.tsx))*
170
+
171
+ ```tsx
172
+ function CarCard({ color }: { color: string }) {
173
+ useWhyRerender({ name: "CarCard", props: { color }, verbose: true })
174
+ return <div>{color}</div>
175
+ }
176
+ ```
177
+
178
+ With `verbose: true`, each changed prop's previous and next value is included in the output, so you don't have to guess what it changed *from* and *to*.
179
+
180
+ ### 5. Temporarily disabling tracking for one component
181
+
182
+ *(full file: [`examples/05-disable-instance.tsx`](./examples/05-disable-instance.tsx))*
183
+
184
+ ```tsx
185
+ function NoisyWidget(props: NoisyWidgetProps) {
186
+ useWhyRerender({ name: "NoisyWidget", props, isActive: false })
187
+ return (/* ... */)
188
+ }
189
+ ```
190
+
191
+ Use this when one component re-renders constantly (e.g. on every keystroke) and its logging is drowning out everything else. Setting `isActive: false` only silences `NoisyWidget` — `setup()` stays enabled, and every other component's `useWhyRerender` call keeps logging normally:
192
+
193
+ ```tsx
194
+ function Dashboard(props: DashboardProps) {
195
+ useWhyRerender({ name: "Dashboard", props }) // still logs
196
+
197
+ return (
198
+ <>
199
+ <Sidebar {...sidebarProps} /> {/* still logs */}
200
+ <NoisyWidget {...widgetProps} /> {/* silenced */}
201
+ </>
202
+ )
203
+ }
204
+ ```
205
+
206
+ ### 6. A realistic parent/child example
207
+
208
+ *(full file: [`examples/06-parent-child.tsx`](./examples/06-parent-child.tsx))*
209
+
210
+ ```tsx
211
+ function CarCard({ id, color, onSelect }: CarCardProps) {
212
+ useWhyRerender({ name: "CarCard", props: { id, color, onSelect }, verbose: true })
213
+
214
+ return <div onClick={() => onSelect(id)}>{color}</div>
215
+ }
216
+
217
+ function CarList({ cars }: { cars: Car[] }) {
218
+ const [selectedId, setSelectedId] = useState<number | null>(null)
219
+
220
+ return (
221
+ <>
222
+ {cars.map((car) => (
223
+ <CarCard
224
+ key={car.id}
225
+ id={car.id}
226
+ color={car.color}
227
+ // A new function is created on every CarList render, so this prop
228
+ // "changes" on every render even though the click behavior is the same.
229
+ onSelect={(id) => setSelectedId(id)}
230
+ />
231
+ ))}
232
+ </>
233
+ )
234
+ }
235
+ ```
236
+
237
+ Every time `CarList` re-renders (e.g. when `selectedId` changes), each `CarCard` gets a brand-new `onSelect` function. Why Render flags `onSelect` as changed on every render — a common, easy-to-miss cause of unnecessary re-renders that's hard to spot just by eyeballing the code.
238
+
239
+ ## Expected Output
240
+
241
+ **First render — nothing to compare yet:**
242
+
243
+ ```
244
+ [why-rerender] <CarCard> render #1 → initial mount
245
+ ```
246
+
247
+ **A later render where a prop actually changed** (this line expands to show details):
248
+
249
+ ```
250
+ ▶ [why-rerender] <CarCard> render #3 → 1 prop changed
251
+ CHANGED color
252
+ ```
253
+
254
+ With `verbose: true`, the same line also includes the previous and next values:
255
+
256
+ ```
257
+ ▶ [why-rerender] <CarCard> render #3 → 1 prop changed
258
+ CHANGED color { prev: "blue", next: "red" }
259
+ ```
260
+
261
+ **A render where no props changed** (see [Example 3](#3-a-component-that-re-renders-without-its-props-changing)):
262
+
263
+ ```
264
+ [why-rerender] <CarCard> render #4 → no prop changes — check state, context, or parent re-render
265
+ ```
266
+
267
+ ## Troubleshooting
268
+
269
+ **Error: "WhyRenderConfig has not been configured"** — `setup()` hasn't run yet. Call it once, before your app renders (see [Setup](#setup)).
270
+
271
+ **Nothing is logging, and no error was thrown** — check:
272
+
273
+ 1. `setup()` was actually called with a value that resolves to enabled — `setup(false)`, a missing environment variable, or a typo like `"tru"` will silently disable it.
274
+ 2. That component's call doesn't have `isActive: false` set (see [Example 5](#5-temporarily-disabling-tracking-for-one-component)).
275
+
276
+ **One component is too noisy to debug around** — set `isActive: false` on that component instead of disabling `setup()` for the whole app.
277
+
278
+ **A prop keeps showing as "changed" even though it looks the same** — this usually means a new object, array, or function is being created for that prop on every render (see [Example 6](#6-a-realistic-parentchild-example)). Since these are different values in JavaScript even when their contents look identical, Why Render correctly reports them as changed.
279
+
280
+ # License
281
+
282
+ ### MIT License
283
+
284
+ Copyright (c) why-render contributors
285
+
286
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
287
+
288
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
289
+
290
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/dist/diff.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ import type { DIFFERENT_PROPS, PropChange } from "./global/index.t.js";
2
+ /**
3
+ * Compares a component's previous and current props and returns the
4
+ * list of props that were added, removed, or changed.
5
+ *
6
+ * Values are compared with `Object.is`, so:
7
+ * - Primitives are compared by value (`NaN` is treated as equal to
8
+ * `NaN`; `+0` and `-0` are treated as different).
9
+ * - Objects, arrays, and functions are compared by reference — a new
10
+ * object/array/function on every render (e.g. an inline `() => {}` or
11
+ * `{ ...props }`) is reported as `"changed"` even if its contents are
12
+ * identical.
13
+ *
14
+ * @param prev - Props from the previous render, or `null` on the first
15
+ * render.
16
+ * @param next - Props from the current render.
17
+ *
18
+ * @returns
19
+ * An empty array when `prev` is `null` (initial mount) or when no props
20
+ * differ. Otherwise, one `PropChange` entry per prop that was added,
21
+ * removed, or changed. Keys are iterated via a `Set` built from
22
+ * `Object.keys(prev)` followed by `Object.keys(next)`, so ordering
23
+ * follows first-seen order across the two objects rather than any
24
+ * guaranteed sort.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * diffProps({ prev: { id: 1 }, next: { id: 2 } })
29
+ * // => [{ prop: "id", prev: 1, next: 2, type: "changed" }]
30
+ * ```
31
+ */
32
+ declare const diffProps: ({ prev, next }: DIFFERENT_PROPS) => PropChange[];
33
+ export default diffProps;
34
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,QAAA,MAAM,SAAS,GAAI,gBAAa,eAAe,KAAE,UAAU,EAuB1D,CAAA;AAED,eAAe,SAAS,CAAA"}
package/dist/diff.js ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Compares a component's previous and current props and returns the
3
+ * list of props that were added, removed, or changed.
4
+ *
5
+ * Values are compared with `Object.is`, so:
6
+ * - Primitives are compared by value (`NaN` is treated as equal to
7
+ * `NaN`; `+0` and `-0` are treated as different).
8
+ * - Objects, arrays, and functions are compared by reference — a new
9
+ * object/array/function on every render (e.g. an inline `() => {}` or
10
+ * `{ ...props }`) is reported as `"changed"` even if its contents are
11
+ * identical.
12
+ *
13
+ * @param prev - Props from the previous render, or `null` on the first
14
+ * render.
15
+ * @param next - Props from the current render.
16
+ *
17
+ * @returns
18
+ * An empty array when `prev` is `null` (initial mount) or when no props
19
+ * differ. Otherwise, one `PropChange` entry per prop that was added,
20
+ * removed, or changed. Keys are iterated via a `Set` built from
21
+ * `Object.keys(prev)` followed by `Object.keys(next)`, so ordering
22
+ * follows first-seen order across the two objects rather than any
23
+ * guaranteed sort.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * diffProps({ prev: { id: 1 }, next: { id: 2 } })
28
+ * // => [{ prop: "id", prev: 1, next: 2, type: "changed" }]
29
+ * ```
30
+ */
31
+ const diffProps = ({ prev, next }) => {
32
+ if (!prev)
33
+ return [];
34
+ const changes = [];
35
+ const keys = new Set([
36
+ ...Object.keys(prev),
37
+ ...Object.keys(next),
38
+ ]);
39
+ for (const key of keys) {
40
+ const inPrev = key in prev;
41
+ const inNext = key in next;
42
+ if (!inPrev) {
43
+ changes.push({ prop: key, prev: undefined, next: next[key], type: "added" });
44
+ }
45
+ else if (!inNext) {
46
+ changes.push({ prop: key, prev: prev[key], next: undefined, type: "removed" });
47
+ }
48
+ else if (!Object.is(prev[key], next[key])) {
49
+ changes.push({ prop: key, prev: prev[key], next: next[key], type: "changed" });
50
+ }
51
+ }
52
+ return changes;
53
+ };
54
+ export default diffProps;
55
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,SAAS,GAAG,CAAC,EAAC,IAAI,EAAE,IAAI,EAAiB,EAAe,EAAE;IAC5D,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAA;IAEpB,MAAM,OAAO,GAAiB,EAAE,CAAA;IAEhC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC;QACjB,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QACpB,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;KACvB,CAAC,CAAA;IAEF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,GAAG,IAAI,IAAI,CAAA;QAC1B,MAAM,MAAM,GAAG,GAAG,IAAI,IAAI,CAAA;QAE1B,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,OAAO,CAAC,IAAI,CAAC,EAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAC,SAAS,EAAE,IAAI,EAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,EAAC,CAAC,CAAA;QAC5E,CAAC;aAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,OAAO,CAAC,IAAI,CAAC,EAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAC,SAAS,EAAE,IAAI,EAAE,SAAS,EAAC,CAAC,CAAA;QAC9E,CAAC;aAAM,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAC1C,OAAO,CAAC,IAAI,CAAC,EAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,EAAC,CAAC,CAAA;QAC9E,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAA;AAClB,CAAC,CAAA;AAED,eAAe,SAAS,CAAA"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Discriminates the kind of change detected for a single prop between
3
+ * two renders.
4
+ *
5
+ * - `"changed"` — the prop exists on both the previous and next render,
6
+ * but its value differs (compared with `Object.is`).
7
+ * - `"added"` — the prop exists on the next render but did not exist on
8
+ * the previous render.
9
+ * - `"removed"` — the prop existed on the previous render but no longer
10
+ * exists on the next render.
11
+ */
12
+ export type PropChangeType = 'changed' | 'added' | 'removed';
13
+ /**
14
+ * Shape of a component's props object as seen by Why Render.
15
+ *
16
+ * This is intentionally a loose `Record<string, unknown>` so that
17
+ * `useWhyRerender` can accept any component's props without requiring
18
+ * callers to type them explicitly.
19
+ */
20
+ export type META_TYPE = Record<string, unknown>;
21
+ /**
22
+ * Describes a single prop that differs between the previous and the
23
+ * current render, as produced by `diffProps`.
24
+ */
25
+ export interface PropChange {
26
+ /** The prop's key/name. */
27
+ prop: string;
28
+ /** The prop's value on the previous render, or `undefined` if it was added. */
29
+ prev: unknown;
30
+ /** The prop's value on the current render, or `undefined` if it was removed. */
31
+ next: unknown;
32
+ /** What kind of change this is. */
33
+ type: PropChangeType;
34
+ }
35
+ /**
36
+ * Input to `diffProps`: the previous and current props objects to
37
+ * compare.
38
+ */
39
+ export interface DIFFERENT_PROPS {
40
+ /**
41
+ * Props from the previous render, or `null` on the very first render
42
+ * (in which case `diffProps` returns an empty array).
43
+ */
44
+ prev: META_TYPE | null;
45
+ /** Props from the current render. */
46
+ next: META_TYPE;
47
+ }
48
+ /**
49
+ * Identifies a component instance and how many times it has rendered,
50
+ * used to build the console log header.
51
+ */
52
+ export interface LabelOptions {
53
+ /** Human-readable component name, as passed to `useWhyRerender`. */
54
+ name: string;
55
+ /** 1-based render count for this component instance. */
56
+ count: number;
57
+ }
58
+ /**
59
+ * Options passed to the internal `log` function to print a single
60
+ * render's console output.
61
+ */
62
+ export interface LogOptions extends LabelOptions {
63
+ /**
64
+ * The list of prop changes to print, or one of two sentinel strings:
65
+ * `"initial"` for the first render, or `"no-prop-changes"` for a
66
+ * render the hook determined had no prop changes.
67
+ */
68
+ changes: PropChange[] | 'initial' | 'no-prop-changes';
69
+ /**
70
+ * When `true`, each changed prop is logged together with its
71
+ * previous and next values. When `false`, only the prop name and
72
+ * change type are logged.
73
+ */
74
+ verbose: boolean;
75
+ }
76
+ //# sourceMappingURL=index.t.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.t.d.ts","sourceRoot":"","sources":["../../src/global/index.t.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAA;AAE5D;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAE/C;;;GAGG;AACH,MAAM,WAAW,UAAU;IACvB,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,IAAI,EAAE,OAAO,CAAC;IACd,gFAAgF;IAChF,IAAI,EAAE,OAAO,CAAC;IACd,mCAAmC;IACnC,IAAI,EAAE,cAAc,CAAC;CACxB;AAGD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC5B;;;OAGG;IACH,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IACvB,qCAAqC;IACrC,IAAI,EAAE,SAAS,CAAC;CACnB;AAGD;;;EAGE;AACF,MAAM,WAAW,YAAY;IACzB,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;CACjB;AAGD;;;GAGG;AACH,MAAM,WAAW,UAAW,SAAQ,YAAY;IAC5C;;;;OAIG;IACH,OAAO,EAAE,UAAU,EAAE,GAAG,SAAS,GAAG,iBAAiB,CAAC;IACtD;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAA;CACnB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.t.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.t.js","sourceRoot":"","sources":["../../src/global/index.t.ts"],"names":[],"mappings":""}
@@ -0,0 +1,26 @@
1
+ import type { META_TYPE } from "./index.t.js";
2
+ /**
3
+ * Arguments accepted by `useWhyRerender`.
4
+ */
5
+ export interface Rerender {
6
+ /** Human-readable name for the component, used in console output. */
7
+ name: string;
8
+ /** The component's current props to compare against the previous render. */
9
+ props: META_TYPE;
10
+ /**
11
+ * When `true`, logs the previous and next value for each changed
12
+ * prop. Defaults to `false` when omitted.
13
+ */
14
+ verbose?: boolean;
15
+ /**
16
+ *Controls whether Why Render should track this component.
17
+ * Set to `false` to temporarily disable logging for this component.
18
+ * This does not disable Why Render globally.
19
+ * @default true
20
+ * @example
21
+ * ```ts
22
+ * isActivate: false * ```
23
+ * */
24
+ isActive?: boolean;
25
+ }
26
+ //# sourceMappingURL=useRerender.t.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRerender.t.d.ts","sourceRoot":"","sources":["../../src/global/useRerender.t.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAG7C;;EAEE;AACF,MAAM,WAAW,QAAQ;IACrB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,KAAK,EAAC,SAAS,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;;;;;;;QAQI;IACJ,QAAQ,CAAC,EAAC,OAAO,CAAA;CACpB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=useRerender.t.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRerender.t.js","sourceRoot":"","sources":["../../src/global/useRerender.t.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import { useWhyRerender, setup } from "./useWhyRerender.js";
2
+ import type { Rerender } from "./global/useRerender.t.js";
3
+ export { useWhyRerender, setup, type Rerender };
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAE1D,OAAO,EACH,cAAc,EACd,KAAK,EACL,KAAK,QAAQ,EAChB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import { useWhyRerender, setup } from "./useWhyRerender.js";
2
+ export { useWhyRerender, setup };
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAG5D,OAAO,EACH,cAAc,EACd,KAAK,EAER,CAAA"}
@@ -0,0 +1,25 @@
1
+ import type { LogOptions } from "./global/index.t.js";
2
+ /**
3
+ * Prints a single render's diagnostic output to the console.
4
+ *
5
+ * Behavior depends on `changes`:
6
+ * - `"initial"` — logs a single `console.log` line noting the initial
7
+ * mount.
8
+ * - `"no-prop-changes"` — logs a single `console.log` line suggesting
9
+ * the re-render was caused by state, context, or a parent re-render
10
+ * rather than a prop change.
11
+ * - An array of `PropChange` — opens a collapsed console group
12
+ * (`console.groupCollapsed` / `console.groupEnd`) with one color-coded
13
+ * `console.log` line per entry, labeled `CHANGED`, `ADDED`, or
14
+ * `REMOVED`. When `verbose` is `true`, each line also includes an
15
+ * object with the prop's previous and next values.
16
+ *
17
+ * This function only produces console output; it has no return value
18
+ * and throws no errors of its own.
19
+ *
20
+ * @param LogOptions - The component name, render count, changes to report,
21
+ * and verbosity flag.
22
+ */
23
+ declare const log: ({ name, count, changes, verbose }: LogOptions) => void;
24
+ export default log;
25
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAMtD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,QAAA,MAAM,GAAG,GAAI,mCAAiC,UAAU,KAAE,IA8BzD,CAAA;AAED,eAAe,GAAG,CAAA"}
package/dist/logger.js ADDED
@@ -0,0 +1,47 @@
1
+ import { PAD_SIZE, setBodyText, setHeader, LABEL_STYLE } from "./utils/utils.js";
2
+ /**
3
+ * Prints a single render's diagnostic output to the console.
4
+ *
5
+ * Behavior depends on `changes`:
6
+ * - `"initial"` — logs a single `console.log` line noting the initial
7
+ * mount.
8
+ * - `"no-prop-changes"` — logs a single `console.log` line suggesting
9
+ * the re-render was caused by state, context, or a parent re-render
10
+ * rather than a prop change.
11
+ * - An array of `PropChange` — opens a collapsed console group
12
+ * (`console.groupCollapsed` / `console.groupEnd`) with one color-coded
13
+ * `console.log` line per entry, labeled `CHANGED`, `ADDED`, or
14
+ * `REMOVED`. When `verbose` is `true`, each line also includes an
15
+ * object with the prop's previous and next values.
16
+ *
17
+ * This function only produces console output; it has no return value
18
+ * and throws no errors of its own.
19
+ *
20
+ * @param LogOptions - The component name, render count, changes to report,
21
+ * and verbosity flag.
22
+ */
23
+ const log = ({ name, count, changes, verbose }) => {
24
+ const header = setHeader({ name, count });
25
+ if (changes === "initial") {
26
+ console.log(header, LABEL_STYLE, '→ initial mount');
27
+ return;
28
+ }
29
+ if (changes === "no-prop-changes") {
30
+ console.log(header, LABEL_STYLE, '→ no prop changes — check state, context, or parent re-render');
31
+ return;
32
+ }
33
+ console.groupCollapsed(header, LABEL_STYLE, `→ ${changes.length} prop${changes.length > 1 ? 's' : ''} changed`);
34
+ for (const c of changes) {
35
+ const label = c.type.toUpperCase().padEnd(PAD_SIZE);
36
+ const typeStyle = setBodyText(c.type);
37
+ if (verbose) {
38
+ console.log(`%c ${label} %c${c.prop}`, typeStyle, '', { prev: c.prev, next: c.next });
39
+ }
40
+ else {
41
+ console.log(`%c ${label} %c${c.prop}`, typeStyle, '');
42
+ }
43
+ }
44
+ console.groupEnd();
45
+ };
46
+ export default log;
47
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAKjF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,GAAG,GAAG,CAAC,EAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAa,EAAO,EAAE;IAC7D,MAAM,MAAM,GAAG,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IACzC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAA;QACnD,OAAO;IACX,CAAC;IAED,IAAI,OAAO,KAAK,iBAAiB,EAAE,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,+DAA+D,CAAC,CAAA;QACjG,OAAM;IACV,CAAC;IAED,OAAO,CAAC,cAAc,CAClB,MAAM,EACN,WAAW,EACX,KAAK,OAAO,CAAC,MAAM,QAAQ,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,UAAU,CACrE,CAAA;IAED,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QACnD,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QAErC,IAAI,OAAO,EAAE,CAAC;YACV,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;QACzF,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,CAAA;QACzD,CAAC;IACL,CAAC;IACD,OAAO,CAAC,QAAQ,EAAE,CAAA;AAEtB,CAAC,CAAA;AAED,eAAe,GAAG,CAAA"}