blockprint 0.0.1 → 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/README.md +2 -0
- package/dist/Renderer.d.ts +8 -2
- package/dist/Renderer.js +28 -22
- package/dist/types.d.ts +10 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,6 +20,8 @@ Renders a config against a registry. `config` is `unknown` on purpose — the ou
|
|
|
20
20
|
|
|
21
21
|
The default fallback renders a warning box in development and `null` in production.
|
|
22
22
|
|
|
23
|
+
The optional `wrapBlock={(node, entry, index) => …}` prop wraps every rendered entry — blocks and fallbacks alike — with access to the config entry and its index. This is the hook point for visual-editing overlays, analytics boundaries, or per-block chrome.
|
|
24
|
+
|
|
23
25
|
### `parseConfig(input)`
|
|
24
26
|
|
|
25
27
|
The boundary validator, exported for consumers who want to fail at fetch time rather than render time. Throws `ConfigError` with the entry index and reason.
|
package/dist/Renderer.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import type { ComponentType, ReactNode } from "react";
|
|
2
|
-
import type { BlocksShape, FallbackProps, Registry } from "./types";
|
|
2
|
+
import type { BlockConfig, BlocksShape, FallbackProps, Registry } from "./types";
|
|
3
3
|
export interface RendererProps<T extends BlocksShape> {
|
|
4
4
|
/** Untrusted input; the outer shape is validated internally. */
|
|
5
5
|
config: unknown;
|
|
6
6
|
registry: Registry<T>;
|
|
7
7
|
fallback?: ComponentType<FallbackProps>;
|
|
8
|
+
/**
|
|
9
|
+
* Wraps every rendered entry — blocks and fallbacks alike — with access to
|
|
10
|
+
* the config entry and its index. The hook point for visual-editing
|
|
11
|
+
* overlays, analytics boundaries, or per-block chrome.
|
|
12
|
+
*/
|
|
13
|
+
wrapBlock?: (node: ReactNode, entry: BlockConfig, index: number) => ReactNode;
|
|
8
14
|
}
|
|
9
|
-
export declare function Renderer<T extends BlocksShape>({ config, registry, fallback, }: RendererProps<T>): ReactNode;
|
|
15
|
+
export declare function Renderer<T extends BlocksShape>({ config, registry, fallback, wrapBlock, }: RendererProps<T>): ReactNode;
|
package/dist/Renderer.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment } from "react";
|
|
2
3
|
import { parseConfig } from "./parseConfig";
|
|
3
4
|
import { DefaultFallback } from "./fallback";
|
|
4
5
|
// `memo`/`forwardRef` components are exotic objects, not functions, so the
|
|
@@ -9,32 +10,37 @@ function resolveDef(def) {
|
|
|
9
10
|
}
|
|
10
11
|
return { component: def };
|
|
11
12
|
}
|
|
12
|
-
export function Renderer({ config, registry, fallback, }) {
|
|
13
|
+
export function Renderer({ config, registry, fallback, wrapBlock, }) {
|
|
13
14
|
const Fallback = fallback ?? DefaultFallback;
|
|
14
15
|
const entries = parseConfig(config);
|
|
15
16
|
return entries.map((entry, index) => {
|
|
16
17
|
const key = entry.id ?? `${entry.type}-${index}`;
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
const node = renderEntry(entry, registry, Fallback);
|
|
19
|
+
// The key lives on a Fragment so it survives whatever wrapBlock returns.
|
|
20
|
+
return (_jsx(Fragment, { children: wrapBlock ? wrapBlock(node, entry, index) : node }, key));
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function renderEntry(entry, registry, Fallback) {
|
|
24
|
+
if (!Object.hasOwn(registry.blocks, entry.type)) {
|
|
25
|
+
return (_jsx(Fallback, { reason: "unknown-type", type: entry.type, props: entry.props }));
|
|
26
|
+
}
|
|
27
|
+
const { component: Component, schema } = resolveDef(registry.blocks[entry.type]);
|
|
28
|
+
let props = entry.props;
|
|
29
|
+
if (schema) {
|
|
30
|
+
const result = schema["~standard"].validate(entry.props);
|
|
31
|
+
// React render is synchronous; async schemas are a config mistake and
|
|
32
|
+
// are contained like any other invalid block.
|
|
33
|
+
if (result instanceof Promise) {
|
|
34
|
+
return (_jsx(Fallback, { reason: "invalid-props", type: entry.type, props: entry.props, issues: [
|
|
35
|
+
{
|
|
36
|
+
message: "schema validated asynchronously; blockprint only supports synchronous schemas",
|
|
37
|
+
},
|
|
38
|
+
] }));
|
|
19
39
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (schema) {
|
|
23
|
-
const result = schema["~standard"].validate(entry.props);
|
|
24
|
-
// React render is synchronous; async schemas are a config mistake and
|
|
25
|
-
// are contained like any other invalid block.
|
|
26
|
-
if (result instanceof Promise) {
|
|
27
|
-
return (_jsx(Fallback, { reason: "invalid-props", type: entry.type, props: entry.props, issues: [
|
|
28
|
-
{
|
|
29
|
-
message: "schema validated asynchronously; blockprint only supports synchronous schemas",
|
|
30
|
-
},
|
|
31
|
-
] }, key));
|
|
32
|
-
}
|
|
33
|
-
if (result.issues) {
|
|
34
|
-
return (_jsx(Fallback, { reason: "invalid-props", type: entry.type, props: entry.props, issues: result.issues }, key));
|
|
35
|
-
}
|
|
36
|
-
props = result.value;
|
|
40
|
+
if (result.issues) {
|
|
41
|
+
return (_jsx(Fallback, { reason: "invalid-props", type: entry.type, props: entry.props, issues: result.issues }));
|
|
37
42
|
}
|
|
38
|
-
|
|
39
|
-
}
|
|
43
|
+
props = result.value;
|
|
44
|
+
}
|
|
45
|
+
return _jsx(Component, { ...props });
|
|
40
46
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -28,6 +28,15 @@ export interface BlockConfig {
|
|
|
28
28
|
props?: unknown;
|
|
29
29
|
id?: string | number;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* The props a consumer may author for a definition. With a schema this is the
|
|
33
|
+
* schema's INPUT type — authored configs are pre-validation data, so fields
|
|
34
|
+
* with defaults are optional. (The component still receives the parsed
|
|
35
|
+
* output; see PropsOfDef.)
|
|
36
|
+
*/
|
|
37
|
+
export type InputOfDef<D> = D extends {
|
|
38
|
+
schema: StandardSchemaV1<infer I, any>;
|
|
39
|
+
} ? I : PropsOfDef<D>;
|
|
31
40
|
/**
|
|
32
41
|
* The closed, fully typed config union for a given registry — for configs
|
|
33
42
|
* authored in TypeScript rather than fetched from a CMS. Block names
|
|
@@ -36,7 +45,7 @@ export interface BlockConfig {
|
|
|
36
45
|
export type ConfigFor<R extends Registry<BlocksShape>> = R extends Registry<infer T> ? Array<{
|
|
37
46
|
[K in keyof T]: {
|
|
38
47
|
type: K;
|
|
39
|
-
props:
|
|
48
|
+
props: InputOfDef<T[K]>;
|
|
40
49
|
id?: string | number;
|
|
41
50
|
};
|
|
42
51
|
}[keyof T]> : never;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blockprint",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Typed, config-driven block renderer for React. Render arbitrary UI trees from untrusted JSON with compile-time registry safety and runtime schema validation.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|