@vickbk/tests 0.2.0 → 0.2.1
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/CHANGELOG.md +6 -0
- package/dist/react.cjs +26 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.mjs +26 -1
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.1] - 2026-10-28
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Prevented `resolveRscTree` from directly invoking components whose source appears to contain React hook calls, including hooks in Vite-transformed import expressions.
|
|
15
|
+
|
|
10
16
|
## [0.2.0] - 2026-10-27
|
|
11
17
|
|
|
12
18
|
### Added
|
package/dist/react.cjs
CHANGED
|
@@ -58,6 +58,31 @@ async function executeServerComponent(type, props) {
|
|
|
58
58
|
return await type(props);
|
|
59
59
|
}
|
|
60
60
|
//#endregion
|
|
61
|
+
//#region src/features/react/utils/mayUseHooks.ts
|
|
62
|
+
/**
|
|
63
|
+
* Determines whether a function's source appears to contain a React hook call.
|
|
64
|
+
*
|
|
65
|
+
* Recognizes calls whose names are `use` or begin with `use` followed by an
|
|
66
|
+
* uppercase letter, digit, or underscore. It also supports transformed calls
|
|
67
|
+
* where a closing parenthesis appears between the hook name and its opening
|
|
68
|
+
* call parenthesis, such as `(0, imported.useState)(initialValue)`.
|
|
69
|
+
*
|
|
70
|
+
* Comments are removed before matching to reduce false positives from
|
|
71
|
+
* hook-like text in comments.
|
|
72
|
+
*
|
|
73
|
+
* This is a heuristic, not a complete JavaScript parser. In particular,
|
|
74
|
+
* comment-like sequences inside strings or template literals can confuse
|
|
75
|
+
* comment removal, and hook calls can be missed or falsely detected when
|
|
76
|
+
* source code is transformed or contains hook-like text in string literals.
|
|
77
|
+
*
|
|
78
|
+
* @param type - The function whose source should be inspected.
|
|
79
|
+
* @returns `true` if the source matches a likely hook call; otherwise `false`.
|
|
80
|
+
*/
|
|
81
|
+
function mayUseHooks(type) {
|
|
82
|
+
const withoutComments = Function.prototype.toString.call(type).replace(/\/\*[\s\S]*?\*\/|\/\/[^\r\n]*/g, "");
|
|
83
|
+
return /\buse(?:[A-Z0-9_]\w*)?\s*\)?\s*\(/.test(withoutComments);
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
61
86
|
//#region src/features/react/utils/isClientComponent.ts
|
|
62
87
|
/**
|
|
63
88
|
* Determines whether a component should be kept behind a client boundary.
|
|
@@ -75,7 +100,7 @@ function isClientComponent(type) {
|
|
|
75
100
|
if (type === null || typeof type !== "function" && typeof type !== "object") return false;
|
|
76
101
|
const reference = type;
|
|
77
102
|
if (reference.$$typeof === Symbol.for("react.client.reference") || reference.$$typeof === Symbol.for("react.module.reference") || typeof reference.$$id === "string") return true;
|
|
78
|
-
return typeof type === "function" &&
|
|
103
|
+
return typeof type === "function" && mayUseHooks(type);
|
|
79
104
|
}
|
|
80
105
|
//#endregion
|
|
81
106
|
//#region src/features/react/utils/resolveProps.ts
|
package/dist/react.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.cjs","names":["React","ClassComponent","FunctionComponent","React","React","React"],"sources":["../src/features/react/utils/resolveArrayChild.ts","../src/features/react/utils/isClassComponent.ts","../src/features/react/utils/executeServerComponent.ts","../src/features/react/utils/isClientComponent.ts","../src/features/react/utils/resolveProps.ts","../src/features/react/utils/resolveElement.ts","../src/features/react/utils/traverseTree.ts","../src/features/react/utils/resolveRscTree.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport * as React from \"react\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Resolves one array child and supplies a stable key when the result is an\n * element without one.\n *\n * @param child - The child value to resolve.\n * @param index - The child's position in its containing array.\n * @param resolveTree - The recursive tree resolver.\n * @returns The resolved child.\n * @example\n * ```ts\n * await resolveArrayChild(<Page />, 0, resolveRscTree);\n * ```\n */\nexport async function resolveArrayChild(\n child: ReactNode,\n index: number,\n resolveTree: ResolveTree,\n): Promise<ReactNode> {\n const resolvedChild = await resolveTree(child);\n if (React.isValidElement(resolvedChild)) {\n const keyToUse =\n resolvedChild.key ??\n (child as { key?: string })?.key ??\n `rsc-key-${index}`;\n return React.cloneElement(resolvedChild, { key: keyToUse });\n }\n return resolvedChild;\n}\n","/**\n * Determines whether a component type is a React class component.\n *\n * @param type - The value to inspect.\n * @returns `true` when the value is a React class component.\n * @example\n * ```ts\n * isClassComponent(MyClassComponent); // true\n * ```\n */\nexport function isClassComponent(type: unknown): boolean {\n return (\n typeof type === \"function\" &&\n Boolean(\n (type as { prototype?: { isReactComponent?: unknown } }).prototype\n ?.isReactComponent,\n )\n );\n}\n","import type * as React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { isClassComponent } from \"./isClassComponent\";\n\n/**\n * Executes a server component and awaits an asynchronous result when present.\n * Component errors intentionally propagate to the caller.\n *\n * @param type - The server component to execute.\n * @param props - Props passed to the component.\n * @returns The component's resolved render result.\n * @example\n * ```ts\n * await executeServerComponent(Page, { title: \"Hello\" });\n * ```\n */\nexport async function executeServerComponent(\n type: React.ComponentType<Record<string, unknown>>,\n props: Record<string, unknown>,\n): Promise<ReactNode> {\n if (isClassComponent(type)) {\n const ClassComponent = type as React.ComponentClass<\n Record<string, unknown>\n >;\n return new ClassComponent(props).render();\n }\n\n const FunctionComponent = type as React.FunctionComponent<\n Record<string, unknown>\n >;\n return await FunctionComponent(props);\n}\n","/**\n * Determines whether a component should be kept behind a client boundary.\n * React client/module references and functions containing hook calls are not\n * invoked directly by the resolver.\n *\n * @param type - The React element type to inspect.\n * @returns `true` when the type represents a client boundary.\n * @example\n * ```ts\n * isClientComponent({ $$typeof: Symbol.for(\"react.client.reference\") }); // true\n * ```\n */\nexport function isClientComponent(type: unknown): boolean {\n if (type === null || (typeof type !== \"function\" && typeof type !== \"object\")) {\n return false;\n }\n\n const reference = type as { $$typeof?: symbol; $$id?: unknown };\n if (\n reference.$$typeof === Symbol.for(\"react.client.reference\") ||\n reference.$$typeof === Symbol.for(\"react.module.reference\") ||\n typeof reference.$$id === \"string\"\n ) {\n return true;\n }\n\n return (\n typeof type === \"function\" &&\n /\\buse[A-Z0-9_]\\w*\\s*\\(/.test(Function.prototype.toString.call(type))\n );\n}\n","import * as React from \"react\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Resolves React elements and arrays nested in a component's props.\n *\n * @param props - The component props to inspect.\n * @param resolveTree - The recursive tree resolver.\n * @returns Updated props and whether any prop value changed.\n * @example\n * ```ts\n * const result = await resolveProps({ children: <Page /> }, resolveRscTree);\n * ```\n */\nexport async function resolveProps(\n props: Record<string, unknown>,\n resolveTree: ResolveTree,\n): Promise<{ newProps: Record<string, unknown>; hasChanged: boolean }> {\n const newProps: Record<string, unknown> = { ...props };\n let hasChanged = false;\n\n for (const key of Object.keys(newProps)) {\n const value = newProps[key];\n if (React.isValidElement(value) || Array.isArray(value)) {\n const resolved = await resolveTree(value as React.ReactNode);\n if (resolved !== value) {\n newProps[key] = resolved;\n hasChanged = true;\n }\n }\n }\n\n return { newProps, hasChanged };\n}\n","import type { ReactNode } from \"react\";\nimport * as React from \"react\";\nimport { executeServerComponent } from \"./executeServerComponent\";\nimport { isClientComponent } from \"./isClientComponent\";\nimport { resolveProps } from \"./resolveProps\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Resolves a server component element or recursively updates its element props.\n * Client component boundaries remain untouched and component errors propagate.\n *\n * @param node - The React element to resolve.\n * @param resolveTree - The recursive tree resolver.\n * @returns The resolved render result or the updated original element.\n * @example\n * ```ts\n * await resolveElement(<ServerPage />, resolveRscTree);\n * ```\n */\nexport async function resolveElement(\n node: React.ReactElement,\n resolveTree: ResolveTree,\n): Promise<ReactNode> {\n const { type, props } = node;\n\n if (typeof type === \"function\" && !isClientComponent(type)) {\n const serverResult = await executeServerComponent(\n type as React.ComponentType<Record<string, unknown>>,\n props as Record<string, unknown>,\n );\n return resolveTree(serverResult);\n }\n\n if (props && typeof props === \"object\") {\n const { newProps, hasChanged } = await resolveProps(\n props as Record<string, unknown>,\n resolveTree,\n );\n if (hasChanged) {\n return React.cloneElement(node, newProps);\n }\n }\n\n return node;\n}\n","import * as React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { resolveArrayChild } from \"./resolveArrayChild\";\nimport { resolveElement } from \"./resolveElement\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Traverses arrays and React elements, delegating component resolution.\n *\n * @param node - The tree value to visit.\n * @param resolveTree - The recursive tree resolver.\n * @returns The traversed value with server components resolved.\n * @example\n * ```ts\n * await traverseTree(tree, resolveRscTree);\n * ```\n */\nexport async function traverseTree(\n node: ReactNode,\n resolveTree: ResolveTree,\n): Promise<ReactNode> {\n if (!node || typeof node !== \"object\") return node;\n\n if (Array.isArray(node)) {\n return Promise.all(\n node.map((child, index) => resolveArrayChild(child, index, resolveTree)),\n );\n }\n\n if (React.isValidElement(node)) {\n return resolveElement(node, resolveTree);\n }\n\n return node;\n}\n","import type { ReactNode } from \"react\";\nimport { traverseTree } from \"./traverseTree\";\n\n/**\n * Recursively resolves synchronous and asynchronous React Server Components.\n * Client component boundaries are preserved, and component errors are allowed\n * to reject the returned promise.\n *\n * @param node - The React node or tree to resolve.\n * @returns A promise for the resolved React tree.\n * @example\n * ```ts\n * const resolved = await resolveRscTree(<ServerPage />);\n * ```\n */\nexport async function resolveRscTree(node: ReactNode): Promise<ReactNode> {\n return traverseTree(node, resolveRscTree);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAiBA,eAAsB,kBACpB,OACA,OACA,aACoB;CACpB,MAAM,gBAAgB,MAAM,YAAY,KAAK;CAC7C,IAAIA,MAAM,eAAe,aAAa,GAAG;EACvC,MAAM,WACJ,cAAc,OACb,OAA4B,OAC7B,WAAW;EACb,OAAOA,MAAM,aAAa,eAAe,EAAE,KAAK,SAAS,CAAC;CAC5D;CACA,OAAO;AACT;;;;;;;;;;;;;ACrBA,SAAgB,iBAAiB,MAAwB;CACvD,OACE,OAAO,SAAS,cAChB,QACG,KAAwD,WACrD,gBACN;AAEJ;;;;;;;;;;;;;;;ACFA,eAAsB,uBACpB,MACA,OACoB;CACpB,IAAI,iBAAiB,IAAI,GAIvB,OAAO,IAAIC,KAAe,KAAK,CAAC,CAAC,OAAO;CAM1C,OAAO,MAAMC,KAAkB,KAAK;AACtC;;;;;;;;;;;;;;;ACnBA,SAAgB,kBAAkB,MAAwB;CACxD,IAAI,SAAS,QAAS,OAAO,SAAS,cAAc,OAAO,SAAS,UAClE,OAAO;CAGT,MAAM,YAAY;CAClB,IACE,UAAU,aAAa,OAAO,IAAI,wBAAwB,KAC1D,UAAU,aAAa,OAAO,IAAI,wBAAwB,KAC1D,OAAO,UAAU,SAAS,UAE1B,OAAO;CAGT,OACE,OAAO,SAAS,cAChB,yBAAyB,KAAK,SAAS,UAAU,SAAS,KAAK,IAAI,CAAC;AAExE;;;;;;;;;;;;;;AChBA,eAAsB,aACpB,OACA,aACqE;CACrE,MAAM,WAAoC,EAAE,GAAG,MAAM;CACrD,IAAI,aAAa;CAEjB,KAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,GAAG;EACvC,MAAM,QAAQ,SAAS;EACvB,IAAIC,MAAM,eAAe,KAAK,KAAK,MAAM,QAAQ,KAAK,GAAG;GACvD,MAAM,WAAW,MAAM,YAAY,KAAwB;GAC3D,IAAI,aAAa,OAAO;IACtB,SAAS,OAAO;IAChB,aAAa;GACf;EACF;CACF;CAEA,OAAO;EAAE;EAAU;CAAW;AAChC;;;;;;;;;;;;;;;ACdA,eAAsB,eACpB,MACA,aACoB;CACpB,MAAM,EAAE,MAAM,UAAU;CAExB,IAAI,OAAO,SAAS,cAAc,CAAC,kBAAkB,IAAI,GAKvD,OAAO,YAAY,MAJQ,uBACzB,MACA,KACF,CAC+B;CAGjC,IAAI,SAAS,OAAO,UAAU,UAAU;EACtC,MAAM,EAAE,UAAU,eAAe,MAAM,aACrC,OACA,WACF;EACA,IAAI,YACF,OAAOC,MAAM,aAAa,MAAM,QAAQ;CAE5C;CAEA,OAAO;AACT;;;;;;;;;;;;;;AC3BA,eAAsB,aACpB,MACA,aACoB;CACpB,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAE9C,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO,QAAQ,IACb,KAAK,KAAK,OAAO,UAAU,kBAAkB,OAAO,OAAO,WAAW,CAAC,CACzE;CAGF,IAAIC,MAAM,eAAe,IAAI,GAC3B,OAAO,eAAe,MAAM,WAAW;CAGzC,OAAO;AACT;;;;;;;;;;;;;;;ACnBA,eAAsB,eAAe,MAAqC;CACxE,OAAO,aAAa,MAAM,cAAc;AAC1C"}
|
|
1
|
+
{"version":3,"file":"react.cjs","names":["React","ClassComponent","FunctionComponent","React","React","React"],"sources":["../src/features/react/utils/resolveArrayChild.ts","../src/features/react/utils/isClassComponent.ts","../src/features/react/utils/executeServerComponent.ts","../src/features/react/utils/mayUseHooks.ts","../src/features/react/utils/isClientComponent.ts","../src/features/react/utils/resolveProps.ts","../src/features/react/utils/resolveElement.ts","../src/features/react/utils/traverseTree.ts","../src/features/react/utils/resolveRscTree.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport * as React from \"react\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Resolves one array child and supplies a stable key when the result is an\n * element without one.\n *\n * @param child - The child value to resolve.\n * @param index - The child's position in its containing array.\n * @param resolveTree - The recursive tree resolver.\n * @returns The resolved child.\n * @example\n * ```ts\n * await resolveArrayChild(<Page />, 0, resolveRscTree);\n * ```\n */\nexport async function resolveArrayChild(\n child: ReactNode,\n index: number,\n resolveTree: ResolveTree,\n): Promise<ReactNode> {\n const resolvedChild = await resolveTree(child);\n if (React.isValidElement(resolvedChild)) {\n const keyToUse =\n resolvedChild.key ??\n (child as { key?: string })?.key ??\n `rsc-key-${index}`;\n return React.cloneElement(resolvedChild, { key: keyToUse });\n }\n return resolvedChild;\n}\n","/**\n * Determines whether a component type is a React class component.\n *\n * @param type - The value to inspect.\n * @returns `true` when the value is a React class component.\n * @example\n * ```ts\n * isClassComponent(MyClassComponent); // true\n * ```\n */\nexport function isClassComponent(type: unknown): boolean {\n return (\n typeof type === \"function\" &&\n Boolean(\n (type as { prototype?: { isReactComponent?: unknown } }).prototype\n ?.isReactComponent,\n )\n );\n}\n","import type * as React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { isClassComponent } from \"./isClassComponent\";\n\n/**\n * Executes a server component and awaits an asynchronous result when present.\n * Component errors intentionally propagate to the caller.\n *\n * @param type - The server component to execute.\n * @param props - Props passed to the component.\n * @returns The component's resolved render result.\n * @example\n * ```ts\n * await executeServerComponent(Page, { title: \"Hello\" });\n * ```\n */\nexport async function executeServerComponent(\n type: React.ComponentType<Record<string, unknown>>,\n props: Record<string, unknown>,\n): Promise<ReactNode> {\n if (isClassComponent(type)) {\n const ClassComponent = type as React.ComponentClass<\n Record<string, unknown>\n >;\n return new ClassComponent(props).render();\n }\n\n const FunctionComponent = type as React.FunctionComponent<\n Record<string, unknown>\n >;\n return await FunctionComponent(props);\n}\n","/**\n * Determines whether a function's source appears to contain a React hook call.\n *\n * Recognizes calls whose names are `use` or begin with `use` followed by an\n * uppercase letter, digit, or underscore. It also supports transformed calls\n * where a closing parenthesis appears between the hook name and its opening\n * call parenthesis, such as `(0, imported.useState)(initialValue)`.\n *\n * Comments are removed before matching to reduce false positives from\n * hook-like text in comments.\n *\n * This is a heuristic, not a complete JavaScript parser. In particular,\n * comment-like sequences inside strings or template literals can confuse\n * comment removal, and hook calls can be missed or falsely detected when\n * source code is transformed or contains hook-like text in string literals.\n *\n * @param type - The function whose source should be inspected.\n * @returns `true` if the source matches a likely hook call; otherwise `false`.\n */\nexport function mayUseHooks(type: (...args: never[]) => unknown): boolean {\n const source = Function.prototype.toString.call(type);\n\n const withoutComments = source.replace(/\\/\\*[\\s\\S]*?\\*\\/|\\/\\/[^\\r\\n]*/g, \"\");\n\n return /\\buse(?:[A-Z0-9_]\\w*)?\\s*\\)?\\s*\\(/.test(withoutComments);\n}\n","import { mayUseHooks } from \"./mayUseHooks\";\n\n/**\n * Determines whether a component should be kept behind a client boundary.\n * React client/module references and functions containing hook calls are not\n * invoked directly by the resolver.\n *\n * @param type - The React element type to inspect.\n * @returns `true` when the type represents a client boundary.\n * @example\n * ```ts\n * isClientComponent({ $$typeof: Symbol.for(\"react.client.reference\") }); // true\n * ```\n */\nexport function isClientComponent(type: unknown): boolean {\n if (\n type === null ||\n (typeof type !== \"function\" && typeof type !== \"object\")\n ) {\n return false;\n }\n\n const reference = type as { $$typeof?: symbol; $$id?: unknown };\n if (\n reference.$$typeof === Symbol.for(\"react.client.reference\") ||\n reference.$$typeof === Symbol.for(\"react.module.reference\") ||\n typeof reference.$$id === \"string\"\n ) {\n return true;\n }\n\n return typeof type === \"function\" && mayUseHooks(type as never);\n}\n","import * as React from \"react\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Resolves React elements and arrays nested in a component's props.\n *\n * @param props - The component props to inspect.\n * @param resolveTree - The recursive tree resolver.\n * @returns Updated props and whether any prop value changed.\n * @example\n * ```ts\n * const result = await resolveProps({ children: <Page /> }, resolveRscTree);\n * ```\n */\nexport async function resolveProps(\n props: Record<string, unknown>,\n resolveTree: ResolveTree,\n): Promise<{ newProps: Record<string, unknown>; hasChanged: boolean }> {\n const newProps: Record<string, unknown> = { ...props };\n let hasChanged = false;\n\n for (const key of Object.keys(newProps)) {\n const value = newProps[key];\n if (React.isValidElement(value) || Array.isArray(value)) {\n const resolved = await resolveTree(value as React.ReactNode);\n if (resolved !== value) {\n newProps[key] = resolved;\n hasChanged = true;\n }\n }\n }\n\n return { newProps, hasChanged };\n}\n","import type { ReactNode } from \"react\";\nimport * as React from \"react\";\nimport { executeServerComponent } from \"./executeServerComponent\";\nimport { isClientComponent } from \"./isClientComponent\";\nimport { resolveProps } from \"./resolveProps\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Resolves a server component element or recursively updates its element props.\n * Client component boundaries remain untouched and component errors propagate.\n *\n * @param node - The React element to resolve.\n * @param resolveTree - The recursive tree resolver.\n * @returns The resolved render result or the updated original element.\n * @example\n * ```ts\n * await resolveElement(<ServerPage />, resolveRscTree);\n * ```\n */\nexport async function resolveElement(\n node: React.ReactElement,\n resolveTree: ResolveTree,\n): Promise<ReactNode> {\n const { type, props } = node;\n\n if (typeof type === \"function\" && !isClientComponent(type)) {\n const serverResult = await executeServerComponent(\n type as React.ComponentType<Record<string, unknown>>,\n props as Record<string, unknown>,\n );\n return resolveTree(serverResult);\n }\n\n if (props && typeof props === \"object\") {\n const { newProps, hasChanged } = await resolveProps(\n props as Record<string, unknown>,\n resolveTree,\n );\n if (hasChanged) {\n return React.cloneElement(node, newProps);\n }\n }\n\n return node;\n}\n","import * as React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { resolveArrayChild } from \"./resolveArrayChild\";\nimport { resolveElement } from \"./resolveElement\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Traverses arrays and React elements, delegating component resolution.\n *\n * @param node - The tree value to visit.\n * @param resolveTree - The recursive tree resolver.\n * @returns The traversed value with server components resolved.\n * @example\n * ```ts\n * await traverseTree(tree, resolveRscTree);\n * ```\n */\nexport async function traverseTree(\n node: ReactNode,\n resolveTree: ResolveTree,\n): Promise<ReactNode> {\n if (!node || typeof node !== \"object\") return node;\n\n if (Array.isArray(node)) {\n return Promise.all(\n node.map((child, index) => resolveArrayChild(child, index, resolveTree)),\n );\n }\n\n if (React.isValidElement(node)) {\n return resolveElement(node, resolveTree);\n }\n\n return node;\n}\n","import type { ReactNode } from \"react\";\nimport { traverseTree } from \"./traverseTree\";\n\n/**\n * Recursively resolves synchronous and asynchronous React Server Components.\n * Client component boundaries are preserved, and component errors are allowed\n * to reject the returned promise.\n *\n * @param node - The React node or tree to resolve.\n * @returns A promise for the resolved React tree.\n * @example\n * ```ts\n * const resolved = await resolveRscTree(<ServerPage />);\n * ```\n */\nexport async function resolveRscTree(node: ReactNode): Promise<ReactNode> {\n return traverseTree(node, resolveRscTree);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAiBA,eAAsB,kBACpB,OACA,OACA,aACoB;CACpB,MAAM,gBAAgB,MAAM,YAAY,KAAK;CAC7C,IAAIA,MAAM,eAAe,aAAa,GAAG;EACvC,MAAM,WACJ,cAAc,OACb,OAA4B,OAC7B,WAAW;EACb,OAAOA,MAAM,aAAa,eAAe,EAAE,KAAK,SAAS,CAAC;CAC5D;CACA,OAAO;AACT;;;;;;;;;;;;;ACrBA,SAAgB,iBAAiB,MAAwB;CACvD,OACE,OAAO,SAAS,cAChB,QACG,KAAwD,WACrD,gBACN;AAEJ;;;;;;;;;;;;;;;ACFA,eAAsB,uBACpB,MACA,OACoB;CACpB,IAAI,iBAAiB,IAAI,GAIvB,OAAO,IAAIC,KAAe,KAAK,CAAC,CAAC,OAAO;CAM1C,OAAO,MAAMC,KAAkB,KAAK;AACtC;;;;;;;;;;;;;;;;;;;;;;ACZA,SAAgB,YAAY,MAA8C;CAGxE,MAAM,kBAFS,SAAS,UAAU,SAAS,KAAK,IAEnB,CAAC,CAAC,QAAQ,kCAAkC,EAAE;CAE3E,OAAO,oCAAoC,KAAK,eAAe;AACjE;;;;;;;;;;;;;;;ACXA,SAAgB,kBAAkB,MAAwB;CACxD,IACE,SAAS,QACR,OAAO,SAAS,cAAc,OAAO,SAAS,UAE/C,OAAO;CAGT,MAAM,YAAY;CAClB,IACE,UAAU,aAAa,OAAO,IAAI,wBAAwB,KAC1D,UAAU,aAAa,OAAO,IAAI,wBAAwB,KAC1D,OAAO,UAAU,SAAS,UAE1B,OAAO;CAGT,OAAO,OAAO,SAAS,cAAc,YAAY,IAAa;AAChE;;;;;;;;;;;;;;AClBA,eAAsB,aACpB,OACA,aACqE;CACrE,MAAM,WAAoC,EAAE,GAAG,MAAM;CACrD,IAAI,aAAa;CAEjB,KAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,GAAG;EACvC,MAAM,QAAQ,SAAS;EACvB,IAAIC,MAAM,eAAe,KAAK,KAAK,MAAM,QAAQ,KAAK,GAAG;GACvD,MAAM,WAAW,MAAM,YAAY,KAAwB;GAC3D,IAAI,aAAa,OAAO;IACtB,SAAS,OAAO;IAChB,aAAa;GACf;EACF;CACF;CAEA,OAAO;EAAE;EAAU;CAAW;AAChC;;;;;;;;;;;;;;;ACdA,eAAsB,eACpB,MACA,aACoB;CACpB,MAAM,EAAE,MAAM,UAAU;CAExB,IAAI,OAAO,SAAS,cAAc,CAAC,kBAAkB,IAAI,GAKvD,OAAO,YAAY,MAJQ,uBACzB,MACA,KACF,CAC+B;CAGjC,IAAI,SAAS,OAAO,UAAU,UAAU;EACtC,MAAM,EAAE,UAAU,eAAe,MAAM,aACrC,OACA,WACF;EACA,IAAI,YACF,OAAOC,MAAM,aAAa,MAAM,QAAQ;CAE5C;CAEA,OAAO;AACT;;;;;;;;;;;;;;AC3BA,eAAsB,aACpB,MACA,aACoB;CACpB,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAE9C,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO,QAAQ,IACb,KAAK,KAAK,OAAO,UAAU,kBAAkB,OAAO,OAAO,WAAW,CAAC,CACzE;CAGF,IAAIC,MAAM,eAAe,IAAI,GAC3B,OAAO,eAAe,MAAM,WAAW;CAGzC,OAAO;AACT;;;;;;;;;;;;;;;ACnBA,eAAsB,eAAe,MAAqC;CACxE,OAAO,aAAa,MAAM,cAAc;AAC1C"}
|
package/dist/react.mjs
CHANGED
|
@@ -55,6 +55,31 @@ async function executeServerComponent(type, props) {
|
|
|
55
55
|
return await type(props);
|
|
56
56
|
}
|
|
57
57
|
//#endregion
|
|
58
|
+
//#region src/features/react/utils/mayUseHooks.ts
|
|
59
|
+
/**
|
|
60
|
+
* Determines whether a function's source appears to contain a React hook call.
|
|
61
|
+
*
|
|
62
|
+
* Recognizes calls whose names are `use` or begin with `use` followed by an
|
|
63
|
+
* uppercase letter, digit, or underscore. It also supports transformed calls
|
|
64
|
+
* where a closing parenthesis appears between the hook name and its opening
|
|
65
|
+
* call parenthesis, such as `(0, imported.useState)(initialValue)`.
|
|
66
|
+
*
|
|
67
|
+
* Comments are removed before matching to reduce false positives from
|
|
68
|
+
* hook-like text in comments.
|
|
69
|
+
*
|
|
70
|
+
* This is a heuristic, not a complete JavaScript parser. In particular,
|
|
71
|
+
* comment-like sequences inside strings or template literals can confuse
|
|
72
|
+
* comment removal, and hook calls can be missed or falsely detected when
|
|
73
|
+
* source code is transformed or contains hook-like text in string literals.
|
|
74
|
+
*
|
|
75
|
+
* @param type - The function whose source should be inspected.
|
|
76
|
+
* @returns `true` if the source matches a likely hook call; otherwise `false`.
|
|
77
|
+
*/
|
|
78
|
+
function mayUseHooks(type) {
|
|
79
|
+
const withoutComments = Function.prototype.toString.call(type).replace(/\/\*[\s\S]*?\*\/|\/\/[^\r\n]*/g, "");
|
|
80
|
+
return /\buse(?:[A-Z0-9_]\w*)?\s*\)?\s*\(/.test(withoutComments);
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
58
83
|
//#region src/features/react/utils/isClientComponent.ts
|
|
59
84
|
/**
|
|
60
85
|
* Determines whether a component should be kept behind a client boundary.
|
|
@@ -72,7 +97,7 @@ function isClientComponent(type) {
|
|
|
72
97
|
if (type === null || typeof type !== "function" && typeof type !== "object") return false;
|
|
73
98
|
const reference = type;
|
|
74
99
|
if (reference.$$typeof === Symbol.for("react.client.reference") || reference.$$typeof === Symbol.for("react.module.reference") || typeof reference.$$id === "string") return true;
|
|
75
|
-
return typeof type === "function" &&
|
|
100
|
+
return typeof type === "function" && mayUseHooks(type);
|
|
76
101
|
}
|
|
77
102
|
//#endregion
|
|
78
103
|
//#region src/features/react/utils/resolveProps.ts
|
package/dist/react.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.mjs","names":["ClassComponent","FunctionComponent"],"sources":["../src/features/react/utils/resolveArrayChild.ts","../src/features/react/utils/isClassComponent.ts","../src/features/react/utils/executeServerComponent.ts","../src/features/react/utils/isClientComponent.ts","../src/features/react/utils/resolveProps.ts","../src/features/react/utils/resolveElement.ts","../src/features/react/utils/traverseTree.ts","../src/features/react/utils/resolveRscTree.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport * as React from \"react\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Resolves one array child and supplies a stable key when the result is an\n * element without one.\n *\n * @param child - The child value to resolve.\n * @param index - The child's position in its containing array.\n * @param resolveTree - The recursive tree resolver.\n * @returns The resolved child.\n * @example\n * ```ts\n * await resolveArrayChild(<Page />, 0, resolveRscTree);\n * ```\n */\nexport async function resolveArrayChild(\n child: ReactNode,\n index: number,\n resolveTree: ResolveTree,\n): Promise<ReactNode> {\n const resolvedChild = await resolveTree(child);\n if (React.isValidElement(resolvedChild)) {\n const keyToUse =\n resolvedChild.key ??\n (child as { key?: string })?.key ??\n `rsc-key-${index}`;\n return React.cloneElement(resolvedChild, { key: keyToUse });\n }\n return resolvedChild;\n}\n","/**\n * Determines whether a component type is a React class component.\n *\n * @param type - The value to inspect.\n * @returns `true` when the value is a React class component.\n * @example\n * ```ts\n * isClassComponent(MyClassComponent); // true\n * ```\n */\nexport function isClassComponent(type: unknown): boolean {\n return (\n typeof type === \"function\" &&\n Boolean(\n (type as { prototype?: { isReactComponent?: unknown } }).prototype\n ?.isReactComponent,\n )\n );\n}\n","import type * as React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { isClassComponent } from \"./isClassComponent\";\n\n/**\n * Executes a server component and awaits an asynchronous result when present.\n * Component errors intentionally propagate to the caller.\n *\n * @param type - The server component to execute.\n * @param props - Props passed to the component.\n * @returns The component's resolved render result.\n * @example\n * ```ts\n * await executeServerComponent(Page, { title: \"Hello\" });\n * ```\n */\nexport async function executeServerComponent(\n type: React.ComponentType<Record<string, unknown>>,\n props: Record<string, unknown>,\n): Promise<ReactNode> {\n if (isClassComponent(type)) {\n const ClassComponent = type as React.ComponentClass<\n Record<string, unknown>\n >;\n return new ClassComponent(props).render();\n }\n\n const FunctionComponent = type as React.FunctionComponent<\n Record<string, unknown>\n >;\n return await FunctionComponent(props);\n}\n","/**\n * Determines whether a component should be kept behind a client boundary.\n * React client/module references and functions containing hook calls are not\n * invoked directly by the resolver.\n *\n * @param type - The React element type to inspect.\n * @returns `true` when the type represents a client boundary.\n * @example\n * ```ts\n * isClientComponent({ $$typeof: Symbol.for(\"react.client.reference\") }); // true\n * ```\n */\nexport function isClientComponent(type: unknown): boolean {\n if (type === null || (typeof type !== \"function\" && typeof type !== \"object\")) {\n return false;\n }\n\n const reference = type as { $$typeof?: symbol; $$id?: unknown };\n if (\n reference.$$typeof === Symbol.for(\"react.client.reference\") ||\n reference.$$typeof === Symbol.for(\"react.module.reference\") ||\n typeof reference.$$id === \"string\"\n ) {\n return true;\n }\n\n return (\n typeof type === \"function\" &&\n /\\buse[A-Z0-9_]\\w*\\s*\\(/.test(Function.prototype.toString.call(type))\n );\n}\n","import * as React from \"react\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Resolves React elements and arrays nested in a component's props.\n *\n * @param props - The component props to inspect.\n * @param resolveTree - The recursive tree resolver.\n * @returns Updated props and whether any prop value changed.\n * @example\n * ```ts\n * const result = await resolveProps({ children: <Page /> }, resolveRscTree);\n * ```\n */\nexport async function resolveProps(\n props: Record<string, unknown>,\n resolveTree: ResolveTree,\n): Promise<{ newProps: Record<string, unknown>; hasChanged: boolean }> {\n const newProps: Record<string, unknown> = { ...props };\n let hasChanged = false;\n\n for (const key of Object.keys(newProps)) {\n const value = newProps[key];\n if (React.isValidElement(value) || Array.isArray(value)) {\n const resolved = await resolveTree(value as React.ReactNode);\n if (resolved !== value) {\n newProps[key] = resolved;\n hasChanged = true;\n }\n }\n }\n\n return { newProps, hasChanged };\n}\n","import type { ReactNode } from \"react\";\nimport * as React from \"react\";\nimport { executeServerComponent } from \"./executeServerComponent\";\nimport { isClientComponent } from \"./isClientComponent\";\nimport { resolveProps } from \"./resolveProps\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Resolves a server component element or recursively updates its element props.\n * Client component boundaries remain untouched and component errors propagate.\n *\n * @param node - The React element to resolve.\n * @param resolveTree - The recursive tree resolver.\n * @returns The resolved render result or the updated original element.\n * @example\n * ```ts\n * await resolveElement(<ServerPage />, resolveRscTree);\n * ```\n */\nexport async function resolveElement(\n node: React.ReactElement,\n resolveTree: ResolveTree,\n): Promise<ReactNode> {\n const { type, props } = node;\n\n if (typeof type === \"function\" && !isClientComponent(type)) {\n const serverResult = await executeServerComponent(\n type as React.ComponentType<Record<string, unknown>>,\n props as Record<string, unknown>,\n );\n return resolveTree(serverResult);\n }\n\n if (props && typeof props === \"object\") {\n const { newProps, hasChanged } = await resolveProps(\n props as Record<string, unknown>,\n resolveTree,\n );\n if (hasChanged) {\n return React.cloneElement(node, newProps);\n }\n }\n\n return node;\n}\n","import * as React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { resolveArrayChild } from \"./resolveArrayChild\";\nimport { resolveElement } from \"./resolveElement\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Traverses arrays and React elements, delegating component resolution.\n *\n * @param node - The tree value to visit.\n * @param resolveTree - The recursive tree resolver.\n * @returns The traversed value with server components resolved.\n * @example\n * ```ts\n * await traverseTree(tree, resolveRscTree);\n * ```\n */\nexport async function traverseTree(\n node: ReactNode,\n resolveTree: ResolveTree,\n): Promise<ReactNode> {\n if (!node || typeof node !== \"object\") return node;\n\n if (Array.isArray(node)) {\n return Promise.all(\n node.map((child, index) => resolveArrayChild(child, index, resolveTree)),\n );\n }\n\n if (React.isValidElement(node)) {\n return resolveElement(node, resolveTree);\n }\n\n return node;\n}\n","import type { ReactNode } from \"react\";\nimport { traverseTree } from \"./traverseTree\";\n\n/**\n * Recursively resolves synchronous and asynchronous React Server Components.\n * Client component boundaries are preserved, and component errors are allowed\n * to reject the returned promise.\n *\n * @param node - The React node or tree to resolve.\n * @returns A promise for the resolved React tree.\n * @example\n * ```ts\n * const resolved = await resolveRscTree(<ServerPage />);\n * ```\n */\nexport async function resolveRscTree(node: ReactNode): Promise<ReactNode> {\n return traverseTree(node, resolveRscTree);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAiBA,eAAsB,kBACpB,OACA,OACA,aACoB;CACpB,MAAM,gBAAgB,MAAM,YAAY,KAAK;CAC7C,IAAI,MAAM,eAAe,aAAa,GAAG;EACvC,MAAM,WACJ,cAAc,OACb,OAA4B,OAC7B,WAAW;EACb,OAAO,MAAM,aAAa,eAAe,EAAE,KAAK,SAAS,CAAC;CAC5D;CACA,OAAO;AACT;;;;;;;;;;;;;ACrBA,SAAgB,iBAAiB,MAAwB;CACvD,OACE,OAAO,SAAS,cAChB,QACG,KAAwD,WACrD,gBACN;AAEJ;;;;;;;;;;;;;;;ACFA,eAAsB,uBACpB,MACA,OACoB;CACpB,IAAI,iBAAiB,IAAI,GAIvB,OAAO,IAAIA,KAAe,KAAK,CAAC,CAAC,OAAO;CAM1C,OAAO,MAAMC,KAAkB,KAAK;AACtC;;;;;;;;;;;;;;;ACnBA,SAAgB,kBAAkB,MAAwB;CACxD,IAAI,SAAS,QAAS,OAAO,SAAS,cAAc,OAAO,SAAS,UAClE,OAAO;CAGT,MAAM,YAAY;CAClB,IACE,UAAU,aAAa,OAAO,IAAI,wBAAwB,KAC1D,UAAU,aAAa,OAAO,IAAI,wBAAwB,KAC1D,OAAO,UAAU,SAAS,UAE1B,OAAO;CAGT,OACE,OAAO,SAAS,cAChB,yBAAyB,KAAK,SAAS,UAAU,SAAS,KAAK,IAAI,CAAC;AAExE;;;;;;;;;;;;;;AChBA,eAAsB,aACpB,OACA,aACqE;CACrE,MAAM,WAAoC,EAAE,GAAG,MAAM;CACrD,IAAI,aAAa;CAEjB,KAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,GAAG;EACvC,MAAM,QAAQ,SAAS;EACvB,IAAI,MAAM,eAAe,KAAK,KAAK,MAAM,QAAQ,KAAK,GAAG;GACvD,MAAM,WAAW,MAAM,YAAY,KAAwB;GAC3D,IAAI,aAAa,OAAO;IACtB,SAAS,OAAO;IAChB,aAAa;GACf;EACF;CACF;CAEA,OAAO;EAAE;EAAU;CAAW;AAChC;;;;;;;;;;;;;;;ACdA,eAAsB,eACpB,MACA,aACoB;CACpB,MAAM,EAAE,MAAM,UAAU;CAExB,IAAI,OAAO,SAAS,cAAc,CAAC,kBAAkB,IAAI,GAKvD,OAAO,YAAY,MAJQ,uBACzB,MACA,KACF,CAC+B;CAGjC,IAAI,SAAS,OAAO,UAAU,UAAU;EACtC,MAAM,EAAE,UAAU,eAAe,MAAM,aACrC,OACA,WACF;EACA,IAAI,YACF,OAAO,MAAM,aAAa,MAAM,QAAQ;CAE5C;CAEA,OAAO;AACT;;;;;;;;;;;;;;AC3BA,eAAsB,aACpB,MACA,aACoB;CACpB,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAE9C,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO,QAAQ,IACb,KAAK,KAAK,OAAO,UAAU,kBAAkB,OAAO,OAAO,WAAW,CAAC,CACzE;CAGF,IAAI,MAAM,eAAe,IAAI,GAC3B,OAAO,eAAe,MAAM,WAAW;CAGzC,OAAO;AACT;;;;;;;;;;;;;;;ACnBA,eAAsB,eAAe,MAAqC;CACxE,OAAO,aAAa,MAAM,cAAc;AAC1C"}
|
|
1
|
+
{"version":3,"file":"react.mjs","names":["ClassComponent","FunctionComponent"],"sources":["../src/features/react/utils/resolveArrayChild.ts","../src/features/react/utils/isClassComponent.ts","../src/features/react/utils/executeServerComponent.ts","../src/features/react/utils/mayUseHooks.ts","../src/features/react/utils/isClientComponent.ts","../src/features/react/utils/resolveProps.ts","../src/features/react/utils/resolveElement.ts","../src/features/react/utils/traverseTree.ts","../src/features/react/utils/resolveRscTree.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport * as React from \"react\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Resolves one array child and supplies a stable key when the result is an\n * element without one.\n *\n * @param child - The child value to resolve.\n * @param index - The child's position in its containing array.\n * @param resolveTree - The recursive tree resolver.\n * @returns The resolved child.\n * @example\n * ```ts\n * await resolveArrayChild(<Page />, 0, resolveRscTree);\n * ```\n */\nexport async function resolveArrayChild(\n child: ReactNode,\n index: number,\n resolveTree: ResolveTree,\n): Promise<ReactNode> {\n const resolvedChild = await resolveTree(child);\n if (React.isValidElement(resolvedChild)) {\n const keyToUse =\n resolvedChild.key ??\n (child as { key?: string })?.key ??\n `rsc-key-${index}`;\n return React.cloneElement(resolvedChild, { key: keyToUse });\n }\n return resolvedChild;\n}\n","/**\n * Determines whether a component type is a React class component.\n *\n * @param type - The value to inspect.\n * @returns `true` when the value is a React class component.\n * @example\n * ```ts\n * isClassComponent(MyClassComponent); // true\n * ```\n */\nexport function isClassComponent(type: unknown): boolean {\n return (\n typeof type === \"function\" &&\n Boolean(\n (type as { prototype?: { isReactComponent?: unknown } }).prototype\n ?.isReactComponent,\n )\n );\n}\n","import type * as React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { isClassComponent } from \"./isClassComponent\";\n\n/**\n * Executes a server component and awaits an asynchronous result when present.\n * Component errors intentionally propagate to the caller.\n *\n * @param type - The server component to execute.\n * @param props - Props passed to the component.\n * @returns The component's resolved render result.\n * @example\n * ```ts\n * await executeServerComponent(Page, { title: \"Hello\" });\n * ```\n */\nexport async function executeServerComponent(\n type: React.ComponentType<Record<string, unknown>>,\n props: Record<string, unknown>,\n): Promise<ReactNode> {\n if (isClassComponent(type)) {\n const ClassComponent = type as React.ComponentClass<\n Record<string, unknown>\n >;\n return new ClassComponent(props).render();\n }\n\n const FunctionComponent = type as React.FunctionComponent<\n Record<string, unknown>\n >;\n return await FunctionComponent(props);\n}\n","/**\n * Determines whether a function's source appears to contain a React hook call.\n *\n * Recognizes calls whose names are `use` or begin with `use` followed by an\n * uppercase letter, digit, or underscore. It also supports transformed calls\n * where a closing parenthesis appears between the hook name and its opening\n * call parenthesis, such as `(0, imported.useState)(initialValue)`.\n *\n * Comments are removed before matching to reduce false positives from\n * hook-like text in comments.\n *\n * This is a heuristic, not a complete JavaScript parser. In particular,\n * comment-like sequences inside strings or template literals can confuse\n * comment removal, and hook calls can be missed or falsely detected when\n * source code is transformed or contains hook-like text in string literals.\n *\n * @param type - The function whose source should be inspected.\n * @returns `true` if the source matches a likely hook call; otherwise `false`.\n */\nexport function mayUseHooks(type: (...args: never[]) => unknown): boolean {\n const source = Function.prototype.toString.call(type);\n\n const withoutComments = source.replace(/\\/\\*[\\s\\S]*?\\*\\/|\\/\\/[^\\r\\n]*/g, \"\");\n\n return /\\buse(?:[A-Z0-9_]\\w*)?\\s*\\)?\\s*\\(/.test(withoutComments);\n}\n","import { mayUseHooks } from \"./mayUseHooks\";\n\n/**\n * Determines whether a component should be kept behind a client boundary.\n * React client/module references and functions containing hook calls are not\n * invoked directly by the resolver.\n *\n * @param type - The React element type to inspect.\n * @returns `true` when the type represents a client boundary.\n * @example\n * ```ts\n * isClientComponent({ $$typeof: Symbol.for(\"react.client.reference\") }); // true\n * ```\n */\nexport function isClientComponent(type: unknown): boolean {\n if (\n type === null ||\n (typeof type !== \"function\" && typeof type !== \"object\")\n ) {\n return false;\n }\n\n const reference = type as { $$typeof?: symbol; $$id?: unknown };\n if (\n reference.$$typeof === Symbol.for(\"react.client.reference\") ||\n reference.$$typeof === Symbol.for(\"react.module.reference\") ||\n typeof reference.$$id === \"string\"\n ) {\n return true;\n }\n\n return typeof type === \"function\" && mayUseHooks(type as never);\n}\n","import * as React from \"react\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Resolves React elements and arrays nested in a component's props.\n *\n * @param props - The component props to inspect.\n * @param resolveTree - The recursive tree resolver.\n * @returns Updated props and whether any prop value changed.\n * @example\n * ```ts\n * const result = await resolveProps({ children: <Page /> }, resolveRscTree);\n * ```\n */\nexport async function resolveProps(\n props: Record<string, unknown>,\n resolveTree: ResolveTree,\n): Promise<{ newProps: Record<string, unknown>; hasChanged: boolean }> {\n const newProps: Record<string, unknown> = { ...props };\n let hasChanged = false;\n\n for (const key of Object.keys(newProps)) {\n const value = newProps[key];\n if (React.isValidElement(value) || Array.isArray(value)) {\n const resolved = await resolveTree(value as React.ReactNode);\n if (resolved !== value) {\n newProps[key] = resolved;\n hasChanged = true;\n }\n }\n }\n\n return { newProps, hasChanged };\n}\n","import type { ReactNode } from \"react\";\nimport * as React from \"react\";\nimport { executeServerComponent } from \"./executeServerComponent\";\nimport { isClientComponent } from \"./isClientComponent\";\nimport { resolveProps } from \"./resolveProps\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Resolves a server component element or recursively updates its element props.\n * Client component boundaries remain untouched and component errors propagate.\n *\n * @param node - The React element to resolve.\n * @param resolveTree - The recursive tree resolver.\n * @returns The resolved render result or the updated original element.\n * @example\n * ```ts\n * await resolveElement(<ServerPage />, resolveRscTree);\n * ```\n */\nexport async function resolveElement(\n node: React.ReactElement,\n resolveTree: ResolveTree,\n): Promise<ReactNode> {\n const { type, props } = node;\n\n if (typeof type === \"function\" && !isClientComponent(type)) {\n const serverResult = await executeServerComponent(\n type as React.ComponentType<Record<string, unknown>>,\n props as Record<string, unknown>,\n );\n return resolveTree(serverResult);\n }\n\n if (props && typeof props === \"object\") {\n const { newProps, hasChanged } = await resolveProps(\n props as Record<string, unknown>,\n resolveTree,\n );\n if (hasChanged) {\n return React.cloneElement(node, newProps);\n }\n }\n\n return node;\n}\n","import * as React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { resolveArrayChild } from \"./resolveArrayChild\";\nimport { resolveElement } from \"./resolveElement\";\nimport type { ResolveTree } from \"./types\";\n\n/**\n * Traverses arrays and React elements, delegating component resolution.\n *\n * @param node - The tree value to visit.\n * @param resolveTree - The recursive tree resolver.\n * @returns The traversed value with server components resolved.\n * @example\n * ```ts\n * await traverseTree(tree, resolveRscTree);\n * ```\n */\nexport async function traverseTree(\n node: ReactNode,\n resolveTree: ResolveTree,\n): Promise<ReactNode> {\n if (!node || typeof node !== \"object\") return node;\n\n if (Array.isArray(node)) {\n return Promise.all(\n node.map((child, index) => resolveArrayChild(child, index, resolveTree)),\n );\n }\n\n if (React.isValidElement(node)) {\n return resolveElement(node, resolveTree);\n }\n\n return node;\n}\n","import type { ReactNode } from \"react\";\nimport { traverseTree } from \"./traverseTree\";\n\n/**\n * Recursively resolves synchronous and asynchronous React Server Components.\n * Client component boundaries are preserved, and component errors are allowed\n * to reject the returned promise.\n *\n * @param node - The React node or tree to resolve.\n * @returns A promise for the resolved React tree.\n * @example\n * ```ts\n * const resolved = await resolveRscTree(<ServerPage />);\n * ```\n */\nexport async function resolveRscTree(node: ReactNode): Promise<ReactNode> {\n return traverseTree(node, resolveRscTree);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAiBA,eAAsB,kBACpB,OACA,OACA,aACoB;CACpB,MAAM,gBAAgB,MAAM,YAAY,KAAK;CAC7C,IAAI,MAAM,eAAe,aAAa,GAAG;EACvC,MAAM,WACJ,cAAc,OACb,OAA4B,OAC7B,WAAW;EACb,OAAO,MAAM,aAAa,eAAe,EAAE,KAAK,SAAS,CAAC;CAC5D;CACA,OAAO;AACT;;;;;;;;;;;;;ACrBA,SAAgB,iBAAiB,MAAwB;CACvD,OACE,OAAO,SAAS,cAChB,QACG,KAAwD,WACrD,gBACN;AAEJ;;;;;;;;;;;;;;;ACFA,eAAsB,uBACpB,MACA,OACoB;CACpB,IAAI,iBAAiB,IAAI,GAIvB,OAAO,IAAIA,KAAe,KAAK,CAAC,CAAC,OAAO;CAM1C,OAAO,MAAMC,KAAkB,KAAK;AACtC;;;;;;;;;;;;;;;;;;;;;;ACZA,SAAgB,YAAY,MAA8C;CAGxE,MAAM,kBAFS,SAAS,UAAU,SAAS,KAAK,IAEnB,CAAC,CAAC,QAAQ,kCAAkC,EAAE;CAE3E,OAAO,oCAAoC,KAAK,eAAe;AACjE;;;;;;;;;;;;;;;ACXA,SAAgB,kBAAkB,MAAwB;CACxD,IACE,SAAS,QACR,OAAO,SAAS,cAAc,OAAO,SAAS,UAE/C,OAAO;CAGT,MAAM,YAAY;CAClB,IACE,UAAU,aAAa,OAAO,IAAI,wBAAwB,KAC1D,UAAU,aAAa,OAAO,IAAI,wBAAwB,KAC1D,OAAO,UAAU,SAAS,UAE1B,OAAO;CAGT,OAAO,OAAO,SAAS,cAAc,YAAY,IAAa;AAChE;;;;;;;;;;;;;;AClBA,eAAsB,aACpB,OACA,aACqE;CACrE,MAAM,WAAoC,EAAE,GAAG,MAAM;CACrD,IAAI,aAAa;CAEjB,KAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,GAAG;EACvC,MAAM,QAAQ,SAAS;EACvB,IAAI,MAAM,eAAe,KAAK,KAAK,MAAM,QAAQ,KAAK,GAAG;GACvD,MAAM,WAAW,MAAM,YAAY,KAAwB;GAC3D,IAAI,aAAa,OAAO;IACtB,SAAS,OAAO;IAChB,aAAa;GACf;EACF;CACF;CAEA,OAAO;EAAE;EAAU;CAAW;AAChC;;;;;;;;;;;;;;;ACdA,eAAsB,eACpB,MACA,aACoB;CACpB,MAAM,EAAE,MAAM,UAAU;CAExB,IAAI,OAAO,SAAS,cAAc,CAAC,kBAAkB,IAAI,GAKvD,OAAO,YAAY,MAJQ,uBACzB,MACA,KACF,CAC+B;CAGjC,IAAI,SAAS,OAAO,UAAU,UAAU;EACtC,MAAM,EAAE,UAAU,eAAe,MAAM,aACrC,OACA,WACF;EACA,IAAI,YACF,OAAO,MAAM,aAAa,MAAM,QAAQ;CAE5C;CAEA,OAAO;AACT;;;;;;;;;;;;;;AC3BA,eAAsB,aACpB,MACA,aACoB;CACpB,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAE9C,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO,QAAQ,IACb,KAAK,KAAK,OAAO,UAAU,kBAAkB,OAAO,OAAO,WAAW,CAAC,CACzE;CAGF,IAAI,MAAM,eAAe,IAAI,GAC3B,OAAO,eAAe,MAAM,WAAW;CAGzC,OAAO;AACT;;;;;;;;;;;;;;;ACnBA,eAAsB,eAAe,MAAqC;CACxE,OAAO,aAAa,MAAM,cAAc;AAC1C"}
|