@flotrace/runtime-core 2.2.4 → 2.3.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.
@@ -0,0 +1,64 @@
1
+ import { jsxDEV as jsxDEV$1 } from 'react/jsx-dev-runtime';
2
+ export { Fragment, JSX } from 'react/jsx-dev-runtime';
3
+
4
+ /**
5
+ * Development JSX runtime entry — instrumented wrapper around React's `jsxDEV`.
6
+ *
7
+ * When the user sets `"jsxImportSource": "@flotrace/runtime-core"` in
8
+ * tsconfig.json, the compiler emits `jsxDEV(...)` calls importing from this
9
+ * module in dev builds. We intercept each call to:
10
+ *
11
+ * 1. Mark the global adoption sentinel (one-time, on first call).
12
+ * 2. Compute a stable per-callsite ID from the bundler-normalized
13
+ * `file:line:col`.
14
+ * 3. Record the render timestamp into a 60-entry FIFO ring buffer keyed by
15
+ * callSiteId (powers the Hot Call Sites tab + conditional-render hint).
16
+ * 4. Detect inline-literal props (`fn` / `obj` / `arr`) that would survive
17
+ * every memo boundary — only observable here, before React processes the
18
+ * props.
19
+ * 5. Attach a `FlotraceJsxSource` object to props under the `FLOTRACE_SOURCE`
20
+ * symbol key. Symbol-keyed entries don't appear in `Object.keys` or
21
+ * React's unknown-prop warnings, but they DO survive React's prop pipeline
22
+ * and end up on `fiber.memoizedProps` unchanged — where the fiber walker
23
+ * reads them back.
24
+ * 6. Delegate to React's real `jsxDEV` with the enriched props.
25
+ *
26
+ * The wrapper passes through unchanged when `source` or `props` is missing
27
+ * (classic runtime, non-dev compiler, stripped source plugin, etc.) — the
28
+ * existing heuristic ladder in fiberTreeWalker handles those fibers.
29
+ *
30
+ * Performance target: <10µs median per call (see jsx-dev-runtime.bench.ts).
31
+ * Memory: ring buffer caps at 60 × callSiteId-count timestamps (~2.4 MB for a
32
+ * 5000-callsite app); symbol-keyed source object adds ~200 bytes per
33
+ * user-component fiber (~1 MB for 5000 fibers).
34
+ */
35
+
36
+ /**
37
+ * The wrapper takes the same arguments the compiler would pass to React's
38
+ * `jsxDEV`. We mirror React's argument types via `Parameters<typeof origJsxDEV>`
39
+ * rather than re-declaring them, so any drift in React's signature surfaces
40
+ * at compile time here. Return type is `ReactNode` (whatever React's jsxDEV
41
+ * returns — opaque to us).
42
+ */
43
+ type JsxDEVArgs = Parameters<typeof jsxDEV$1>;
44
+ type JsxDEVReturn = ReturnType<typeof jsxDEV$1>;
45
+ type JsxDEVType = JsxDEVArgs[0];
46
+ /** Shape of the compiler-supplied `source` argument to `jsxDEV`. Stable since React 17. */
47
+ interface JsxSource {
48
+ fileName: string;
49
+ lineNumber: number;
50
+ columnNumber: number;
51
+ }
52
+ declare function jsxDEV(type: JsxDEVType, props: Record<string, unknown> | null, key: JsxDEVArgs[2], isStaticChildren: boolean, source: JsxSource | undefined, self: JsxDEVArgs[5]): JsxDEVReturn;
53
+ /**
54
+ * `jsxsDEV` is what some compilers emit for statically-known children arrays.
55
+ * Behaviour is identical to `jsxDEV`; we alias rather than duplicate.
56
+ *
57
+ * React 17/18's `react/jsx-dev-runtime` doesn't export `jsxsDEV` directly —
58
+ * compilers call `jsxDEV` for both cases in dev. Re-exporting here under both
59
+ * names is defensive against compilers that happen to call `jsxsDEV` (some
60
+ * older Babel configs).
61
+ */
62
+ declare const jsxsDEV: typeof jsxDEV;
63
+
64
+ export { jsxDEV, jsxsDEV };
@@ -0,0 +1,64 @@
1
+ import { jsxDEV as jsxDEV$1 } from 'react/jsx-dev-runtime';
2
+ export { Fragment, JSX } from 'react/jsx-dev-runtime';
3
+
4
+ /**
5
+ * Development JSX runtime entry — instrumented wrapper around React's `jsxDEV`.
6
+ *
7
+ * When the user sets `"jsxImportSource": "@flotrace/runtime-core"` in
8
+ * tsconfig.json, the compiler emits `jsxDEV(...)` calls importing from this
9
+ * module in dev builds. We intercept each call to:
10
+ *
11
+ * 1. Mark the global adoption sentinel (one-time, on first call).
12
+ * 2. Compute a stable per-callsite ID from the bundler-normalized
13
+ * `file:line:col`.
14
+ * 3. Record the render timestamp into a 60-entry FIFO ring buffer keyed by
15
+ * callSiteId (powers the Hot Call Sites tab + conditional-render hint).
16
+ * 4. Detect inline-literal props (`fn` / `obj` / `arr`) that would survive
17
+ * every memo boundary — only observable here, before React processes the
18
+ * props.
19
+ * 5. Attach a `FlotraceJsxSource` object to props under the `FLOTRACE_SOURCE`
20
+ * symbol key. Symbol-keyed entries don't appear in `Object.keys` or
21
+ * React's unknown-prop warnings, but they DO survive React's prop pipeline
22
+ * and end up on `fiber.memoizedProps` unchanged — where the fiber walker
23
+ * reads them back.
24
+ * 6. Delegate to React's real `jsxDEV` with the enriched props.
25
+ *
26
+ * The wrapper passes through unchanged when `source` or `props` is missing
27
+ * (classic runtime, non-dev compiler, stripped source plugin, etc.) — the
28
+ * existing heuristic ladder in fiberTreeWalker handles those fibers.
29
+ *
30
+ * Performance target: <10µs median per call (see jsx-dev-runtime.bench.ts).
31
+ * Memory: ring buffer caps at 60 × callSiteId-count timestamps (~2.4 MB for a
32
+ * 5000-callsite app); symbol-keyed source object adds ~200 bytes per
33
+ * user-component fiber (~1 MB for 5000 fibers).
34
+ */
35
+
36
+ /**
37
+ * The wrapper takes the same arguments the compiler would pass to React's
38
+ * `jsxDEV`. We mirror React's argument types via `Parameters<typeof origJsxDEV>`
39
+ * rather than re-declaring them, so any drift in React's signature surfaces
40
+ * at compile time here. Return type is `ReactNode` (whatever React's jsxDEV
41
+ * returns — opaque to us).
42
+ */
43
+ type JsxDEVArgs = Parameters<typeof jsxDEV$1>;
44
+ type JsxDEVReturn = ReturnType<typeof jsxDEV$1>;
45
+ type JsxDEVType = JsxDEVArgs[0];
46
+ /** Shape of the compiler-supplied `source` argument to `jsxDEV`. Stable since React 17. */
47
+ interface JsxSource {
48
+ fileName: string;
49
+ lineNumber: number;
50
+ columnNumber: number;
51
+ }
52
+ declare function jsxDEV(type: JsxDEVType, props: Record<string, unknown> | null, key: JsxDEVArgs[2], isStaticChildren: boolean, source: JsxSource | undefined, self: JsxDEVArgs[5]): JsxDEVReturn;
53
+ /**
54
+ * `jsxsDEV` is what some compilers emit for statically-known children arrays.
55
+ * Behaviour is identical to `jsxDEV`; we alias rather than duplicate.
56
+ *
57
+ * React 17/18's `react/jsx-dev-runtime` doesn't export `jsxsDEV` directly —
58
+ * compilers call `jsxDEV` for both cases in dev. Re-exporting here under both
59
+ * names is defensive against compilers that happen to call `jsxsDEV` (some
60
+ * older Babel configs).
61
+ */
62
+ declare const jsxsDEV: typeof jsxDEV;
63
+
64
+ export { jsxDEV, jsxsDEV };
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/jsx-dev-runtime.ts
21
+ var jsx_dev_runtime_exports = {};
22
+ __export(jsx_dev_runtime_exports, {
23
+ Fragment: () => import_jsx_dev_runtime.Fragment,
24
+ jsxDEV: () => jsxDEV,
25
+ jsxsDEV: () => jsxsDEV
26
+ });
27
+ module.exports = __toCommonJS(jsx_dev_runtime_exports);
28
+ var import_jsx_dev_runtime = require("react/jsx-dev-runtime");
29
+
30
+ // src/jsxRuntimeUtils.ts
31
+ var FLOTRACE_SOURCE = /* @__PURE__ */ Symbol.for("flotrace.source");
32
+ var JSX_RUNTIME_ACTIVE_KEY = /* @__PURE__ */ Symbol.for("flotrace.jsx-runtime-active");
33
+ function normalizeJsxSourcePath(fileName) {
34
+ let p = fileName;
35
+ if (p.startsWith("file://")) p = p.slice("file://".length);
36
+ if (p.startsWith("webpack-internal:///./")) p = p.slice("webpack-internal:///./".length);
37
+ if (p.startsWith("[project]/")) p = p.slice("[project]/".length);
38
+ if (p.startsWith("./")) p = p.slice(2);
39
+ if (/^\/[a-zA-Z]:[\\/]/.test(p)) p = p.slice(1);
40
+ if (/^[a-zA-Z]:[\\/]/.test(p)) p = p[0].toLowerCase() + p.slice(1);
41
+ return p;
42
+ }
43
+ function computeCallSiteId(source) {
44
+ const normPath = normalizeJsxSourcePath(source.fileName);
45
+ const key = `${normPath}:${source.lineNumber}:${source.columnNumber}`;
46
+ let hash = 2166136261;
47
+ for (let i = 0; i < key.length; i++) {
48
+ hash ^= key.charCodeAt(i);
49
+ hash = Math.imul(hash, 16777619);
50
+ }
51
+ return (hash >>> 0).toString(16).padStart(8, "0");
52
+ }
53
+ var callSiteRenders = /* @__PURE__ */ new Map();
54
+ var RING_BUFFER_MAX = 60;
55
+ function recordCallSiteRender(callSiteId, now = performance.now()) {
56
+ const arr = callSiteRenders.get(callSiteId);
57
+ if (arr === void 0) {
58
+ callSiteRenders.set(callSiteId, [now]);
59
+ return;
60
+ }
61
+ arr.push(now);
62
+ if (arr.length > RING_BUFFER_MAX) arr.shift();
63
+ }
64
+ var duplicateKeyEmitter = null;
65
+ var currentKeyBatch = /* @__PURE__ */ new Map();
66
+ var keyBatchFlushScheduled = false;
67
+ function recordJsxKey(source, key) {
68
+ const keyType = typeof key;
69
+ if (keyType !== "string" && keyType !== "number" && keyType !== "boolean") return;
70
+ const keyStr = String(key);
71
+ const batchKey = `${source.callSiteId}|${keyStr}`;
72
+ const entry = currentKeyBatch.get(batchKey);
73
+ if (entry !== void 0) {
74
+ entry.count++;
75
+ } else {
76
+ currentKeyBatch.set(batchKey, { count: 1, source });
77
+ }
78
+ if (!keyBatchFlushScheduled) {
79
+ keyBatchFlushScheduled = true;
80
+ queueMicrotask(flushDuplicateKeys);
81
+ }
82
+ }
83
+ function flushDuplicateKeys() {
84
+ keyBatchFlushScheduled = false;
85
+ const emitter = duplicateKeyEmitter;
86
+ if (emitter === null) {
87
+ currentKeyBatch.clear();
88
+ return;
89
+ }
90
+ for (const [batchKey, { count, source }] of currentKeyBatch) {
91
+ if (count < 2) continue;
92
+ const sep = batchKey.indexOf("|");
93
+ const duplicateKey = batchKey.slice(sep + 1);
94
+ emitter({
95
+ callSiteId: source.callSiteId,
96
+ fileName: source.fileName,
97
+ lineNumber: source.lineNumber,
98
+ columnNumber: source.columnNumber,
99
+ duplicateKey,
100
+ occurrences: count
101
+ });
102
+ }
103
+ currentKeyBatch.clear();
104
+ }
105
+ function markJsxRuntimeActive() {
106
+ globalThis[JSX_RUNTIME_ACTIVE_KEY] = true;
107
+ }
108
+ var KNOWN_REACT_PROPS = /* @__PURE__ */ new Set(["key", "ref", "children", "className"]);
109
+ var REACT_ELEMENT_TYPEOF_LEGACY = /* @__PURE__ */ Symbol.for("react.element");
110
+ var REACT_ELEMENT_TYPEOF_R19 = /* @__PURE__ */ Symbol.for("react.transitional.element");
111
+ function isReactElement(v) {
112
+ const typeOf = v.$$typeof;
113
+ return typeOf === REACT_ELEMENT_TYPEOF_LEGACY || typeOf === REACT_ELEMENT_TYPEOF_R19;
114
+ }
115
+ function detectInlineLiterals(props) {
116
+ let out;
117
+ for (const k in props) {
118
+ if (KNOWN_REACT_PROPS.has(k)) continue;
119
+ const v = props[k];
120
+ if (typeof v === "function") {
121
+ if (!v.name) {
122
+ (out ?? (out = {}))[k] = "fn";
123
+ }
124
+ } else if (Array.isArray(v)) {
125
+ if (v.length > 0) {
126
+ (out ?? (out = {}))[k] = "arr";
127
+ }
128
+ } else if (v !== null && typeof v === "object" && // Skip elements processed by our own runtime (marker present).
129
+ !(FLOTRACE_SOURCE in v) && // Skip React elements processed by ANY runtime — `$$typeof` is set on
130
+ // every element regardless of which jsx-runtime created it, so this
131
+ // catches mixed-runtime codebases. Without this guard, an inline
132
+ // `<Outer child={<Inner/>} />` would false-positive on `child` when
133
+ // Inner went through a different jsxImportSource.
134
+ !isReactElement(v)) {
135
+ const proto = Object.getPrototypeOf(v);
136
+ if (proto === Object.prototype || proto === null) {
137
+ (out ?? (out = {}))[k] = "obj";
138
+ }
139
+ }
140
+ }
141
+ return out;
142
+ }
143
+
144
+ // src/jsx-dev-runtime.ts
145
+ var IS_BROWSER = typeof window !== "undefined" && typeof document !== "undefined";
146
+ function jsxDEV(type, props, key, isStaticChildren, source, self) {
147
+ if (!source || !props) {
148
+ return (0, import_jsx_dev_runtime.jsxDEV)(type, props, key, isStaticChildren, source, self);
149
+ }
150
+ if (!IS_BROWSER) {
151
+ return (0, import_jsx_dev_runtime.jsxDEV)(type, props, key, isStaticChildren, source, self);
152
+ }
153
+ markJsxRuntimeActive();
154
+ const callSiteId = computeCallSiteId(source);
155
+ const inline = detectInlineLiterals(props);
156
+ const flotraceSource = inline ? {
157
+ fileName: normalizeJsxSourcePath(source.fileName),
158
+ lineNumber: source.lineNumber,
159
+ columnNumber: source.columnNumber,
160
+ callSiteId,
161
+ inline
162
+ } : {
163
+ fileName: normalizeJsxSourcePath(source.fileName),
164
+ lineNumber: source.lineNumber,
165
+ columnNumber: source.columnNumber,
166
+ callSiteId
167
+ };
168
+ recordCallSiteRender(callSiteId);
169
+ recordJsxKey(flotraceSource, key);
170
+ const enrichedProps = { ...props, [FLOTRACE_SOURCE]: flotraceSource };
171
+ return (0, import_jsx_dev_runtime.jsxDEV)(type, enrichedProps, key, isStaticChildren, source, self);
172
+ }
173
+ var jsxsDEV = jsxDEV;
174
+ // Annotate the CommonJS export names for ESM import in node:
175
+ 0 && (module.exports = {
176
+ Fragment,
177
+ jsxDEV,
178
+ jsxsDEV
179
+ });
@@ -0,0 +1,46 @@
1
+ import {
2
+ FLOTRACE_SOURCE,
3
+ computeCallSiteId,
4
+ detectInlineLiterals,
5
+ markJsxRuntimeActive,
6
+ normalizeJsxSourcePath,
7
+ recordCallSiteRender,
8
+ recordJsxKey
9
+ } from "./chunk-QLOJU5F2.mjs";
10
+
11
+ // src/jsx-dev-runtime.ts
12
+ import { jsxDEV as origJsxDEV, Fragment } from "react/jsx-dev-runtime";
13
+ var IS_BROWSER = typeof window !== "undefined" && typeof document !== "undefined";
14
+ function jsxDEV(type, props, key, isStaticChildren, source, self) {
15
+ if (!source || !props) {
16
+ return origJsxDEV(type, props, key, isStaticChildren, source, self);
17
+ }
18
+ if (!IS_BROWSER) {
19
+ return origJsxDEV(type, props, key, isStaticChildren, source, self);
20
+ }
21
+ markJsxRuntimeActive();
22
+ const callSiteId = computeCallSiteId(source);
23
+ const inline = detectInlineLiterals(props);
24
+ const flotraceSource = inline ? {
25
+ fileName: normalizeJsxSourcePath(source.fileName),
26
+ lineNumber: source.lineNumber,
27
+ columnNumber: source.columnNumber,
28
+ callSiteId,
29
+ inline
30
+ } : {
31
+ fileName: normalizeJsxSourcePath(source.fileName),
32
+ lineNumber: source.lineNumber,
33
+ columnNumber: source.columnNumber,
34
+ callSiteId
35
+ };
36
+ recordCallSiteRender(callSiteId);
37
+ recordJsxKey(flotraceSource, key);
38
+ const enrichedProps = { ...props, [FLOTRACE_SOURCE]: flotraceSource };
39
+ return origJsxDEV(type, enrichedProps, key, isStaticChildren, source, self);
40
+ }
41
+ var jsxsDEV = jsxDEV;
42
+ export {
43
+ Fragment,
44
+ jsxDEV,
45
+ jsxsDEV
46
+ };
@@ -0,0 +1 @@
1
+ export { Fragment, JSX, jsx, jsxs } from 'react/jsx-runtime';
@@ -0,0 +1 @@
1
+ export { Fragment, JSX, jsx, jsxs } from 'react/jsx-runtime';
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/jsx-runtime.ts
21
+ var jsx_runtime_exports = {};
22
+ __export(jsx_runtime_exports, {
23
+ Fragment: () => import_jsx_runtime.Fragment,
24
+ jsx: () => import_jsx_runtime.jsx,
25
+ jsxs: () => import_jsx_runtime.jsxs
26
+ });
27
+ module.exports = __toCommonJS(jsx_runtime_exports);
28
+ var import_jsx_runtime = require("react/jsx-runtime");
29
+ // Annotate the CommonJS export names for ESM import in node:
30
+ 0 && (module.exports = {
31
+ Fragment,
32
+ jsx,
33
+ jsxs
34
+ });
@@ -0,0 +1,7 @@
1
+ // src/jsx-runtime.ts
2
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
+ export {
4
+ Fragment,
5
+ jsx,
6
+ jsxs
7
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flotrace/runtime-core",
3
- "version": "2.2.4",
3
+ "version": "2.3.0",
4
4
  "description": "Platform-agnostic core for FloTrace runtime — fiber walker, analyzers, trackers. Shared by @flotrace/runtime (web) and @flotrace/runtime-native (React Native).",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -10,8 +10,19 @@
10
10
  "types": "./dist/index.d.ts",
11
11
  "import": "./dist/index.mjs",
12
12
  "require": "./dist/index.js"
13
+ },
14
+ "./jsx-runtime": {
15
+ "types": "./dist/jsx-runtime.d.ts",
16
+ "import": "./dist/jsx-runtime.mjs",
17
+ "require": "./dist/jsx-runtime.js"
18
+ },
19
+ "./jsx-dev-runtime": {
20
+ "types": "./dist/jsx-dev-runtime.d.ts",
21
+ "import": "./dist/jsx-dev-runtime.mjs",
22
+ "require": "./dist/jsx-dev-runtime.js"
13
23
  }
14
24
  },
25
+ "sideEffects": false,
15
26
  "files": [
16
27
  "dist",
17
28
  "LICENSE",