@teamscala/service-boot 0.23.436 → 0.23.437
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/package.json
CHANGED
package/src/discover-blocks.ts
CHANGED
|
@@ -25,6 +25,7 @@ import { join } from "node:path";
|
|
|
25
25
|
import { getPrisma } from "@teamscala/db/prisma-registry";
|
|
26
26
|
import { BlockSchemaConfigSchema } from "@teamscala/db-validation/registry-schemas/block-schema";
|
|
27
27
|
import * as v from "valibot";
|
|
28
|
+
import { isReactElementType } from "./is-react-element-type.ts";
|
|
28
29
|
import type { RendererComponentEntry } from "./types.ts";
|
|
29
30
|
|
|
30
31
|
export interface DiscoveredBlocks {
|
|
@@ -159,7 +160,7 @@ export async function discoverBlocks(
|
|
|
159
160
|
),
|
|
160
161
|
])) as Record<string, unknown>;
|
|
161
162
|
const component = mod[componentExport] ?? mod.default;
|
|
162
|
-
if (
|
|
163
|
+
if (!isReactElementType(component)) {
|
|
163
164
|
skipped.push(
|
|
164
165
|
`${row.slug} (${componentExport} not a component in ${componentPath})`,
|
|
165
166
|
);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system ui-service-boot
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
*
|
|
6
|
+
* Pins the three React idioms a `typeof === "function"` check silently rejects.
|
|
7
|
+
* The memo case is the measured one: PresenceAvatars rendered blank on every
|
|
8
|
+
* scala-projects page carrying it, because memo() returns an object.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { expect, test } from "bun:test";
|
|
12
|
+
import { forwardRef, lazy, memo } from "react";
|
|
13
|
+
import { isReactElementType } from "./is-react-element-type.ts";
|
|
14
|
+
|
|
15
|
+
function Plain() {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
test("accepts a plain function component", () => {
|
|
20
|
+
expect(isReactElementType(Plain)).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("accepts memo() — the PresenceAvatars shape that rendered blank", () => {
|
|
24
|
+
expect(isReactElementType(memo(Plain))).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("accepts forwardRef()", () => {
|
|
28
|
+
expect(isReactElementType(forwardRef(() => null))).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("accepts lazy()", () => {
|
|
32
|
+
expect(isReactElementType(lazy(async () => ({ default: Plain })))).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("still rejects what genuinely cannot render", () => {
|
|
36
|
+
expect(isReactElementType(undefined)).toBe(false);
|
|
37
|
+
expect(isReactElementType(null)).toBe(false);
|
|
38
|
+
expect(isReactElementType({})).toBe(false);
|
|
39
|
+
expect(isReactElementType("PresenceAvatarsBlock")).toBe(false);
|
|
40
|
+
expect(isReactElementType(42)).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("rejects a plain object wearing a non-symbol $$typeof", () => {
|
|
44
|
+
// A data blob with the key but not the symbol must not pass as renderable.
|
|
45
|
+
expect(isReactElementType({ $$typeof: "react.memo" })).toBe(false);
|
|
46
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @system ui-service-boot
|
|
3
|
+
* @status handwritten
|
|
4
|
+
* @edit edit directly
|
|
5
|
+
*
|
|
6
|
+
* Is a resolved module export something React can render?
|
|
7
|
+
*
|
|
8
|
+
* `typeof x === "function"` is NOT that question. memo(), forwardRef() and
|
|
9
|
+
* lazy() all return OBJECTS carrying a `$$typeof` symbol, so a shape check on
|
|
10
|
+
* `function` rejects three of React's own idioms as "not a component".
|
|
11
|
+
* Measured 2026-09-01: scala-projects skipped PresenceAvatars for exactly this
|
|
12
|
+
* reason — the block is `memo(function PresenceAvatarsBlock(...))` — and every
|
|
13
|
+
* page rendering it came out blank with no error beyond one boot WARN.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export function isReactElementType(value: unknown): boolean {
|
|
17
|
+
if (typeof value === "function") return true;
|
|
18
|
+
if (typeof value !== "object" || value === null) return false;
|
|
19
|
+
return typeof (value as { $$typeof?: unknown }).$$typeof === "symbol";
|
|
20
|
+
}
|