@forumone/throughline-design-system 0.0.0 → 1.0.0-next.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/CHANGELOG.md +180 -0
- package/LICENSE +21 -0
- package/README.md +21 -0
- package/bin/check-block-props.mjs +51 -0
- package/bin/stub-styles.mjs +28 -0
- package/dist/admin/BlockGuidance.d.ts +13 -0
- package/dist/admin/BlockGuidance.d.ts.map +1 -0
- package/dist/admin/BlockGuidance.js +13 -0
- package/dist/admin/BlockGuidance.js.map +1 -0
- package/dist/admin/BlockSummary.d.ts +10 -0
- package/dist/admin/BlockSummary.d.ts.map +1 -0
- package/dist/admin/BlockSummary.js +73 -0
- package/dist/admin/BlockSummary.js.map +1 -0
- package/dist/admin/RowSummary.d.ts +15 -0
- package/dist/admin/RowSummary.d.ts.map +1 -0
- package/dist/admin/RowSummary.js +16 -0
- package/dist/admin/RowSummary.js.map +1 -0
- package/dist/admin/summary.d.ts +18 -0
- package/dist/admin/summary.d.ts.map +1 -0
- package/dist/admin/summary.js +48 -0
- package/dist/admin/summary.js.map +1 -0
- package/dist/altText.d.ts +16 -0
- package/dist/altText.d.ts.map +1 -0
- package/dist/altText.js +54 -0
- package/dist/altText.js.map +1 -0
- package/dist/client.d.ts +15 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +12 -0
- package/dist/client.js.map +1 -0
- package/dist/contract/index.d.ts +5 -0
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/index.js +4 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/contract/lint.d.ts +38 -0
- package/dist/contract/lint.d.ts.map +1 -0
- package/dist/contract/lint.js +110 -0
- package/dist/contract/lint.js.map +1 -0
- package/dist/contract/loader.d.ts +49 -0
- package/dist/contract/loader.d.ts.map +1 -0
- package/dist/contract/loader.js +95 -0
- package/dist/contract/loader.js.map +1 -0
- package/dist/contract/manifest.d.ts +454 -0
- package/dist/contract/manifest.d.ts.map +1 -0
- package/dist/contract/manifest.js +29 -0
- package/dist/contract/manifest.js.map +1 -0
- package/dist/contract/schema.d.ts +334 -0
- package/dist/contract/schema.d.ts.map +1 -0
- package/dist/contract/schema.js +224 -0
- package/dist/contract/schema.js.map +1 -0
- package/dist/generate/blocks.d.ts +79 -0
- package/dist/generate/blocks.d.ts.map +1 -0
- package/dist/generate/blocks.js +118 -0
- package/dist/generate/blocks.js.map +1 -0
- package/dist/generate/fields.d.ts +60 -0
- package/dist/generate/fields.d.ts.map +1 -0
- package/dist/generate/fields.js +478 -0
- package/dist/generate/fields.js.map +1 -0
- package/dist/generate/guidance.d.ts +5 -0
- package/dist/generate/guidance.d.ts.map +1 -0
- package/dist/generate/guidance.js +29 -0
- package/dist/generate/guidance.js.map +1 -0
- package/dist/generate/index.d.ts +13 -0
- package/dist/generate/index.d.ts.map +1 -0
- package/dist/generate/index.js +9 -0
- package/dist/generate/index.js.map +1 -0
- package/dist/generate/labels.d.ts +42 -0
- package/dist/generate/labels.d.ts.map +1 -0
- package/dist/generate/labels.js +226 -0
- package/dist/generate/labels.js.map +1 -0
- package/dist/generate/layout.d.ts +42 -0
- package/dist/generate/layout.d.ts.map +1 -0
- package/dist/generate/layout.js +252 -0
- package/dist/generate/layout.js.map +1 -0
- package/dist/generate/selectOptionSnapshot.d.ts +26 -0
- package/dist/generate/selectOptionSnapshot.d.ts.map +1 -0
- package/dist/generate/selectOptionSnapshot.js +47 -0
- package/dist/generate/selectOptionSnapshot.js.map +1 -0
- package/dist/generate/selectOptions.d.ts +64 -0
- package/dist/generate/selectOptions.d.ts.map +1 -0
- package/dist/generate/selectOptions.js +145 -0
- package/dist/generate/selectOptions.js.map +1 -0
- package/dist/overrides.d.ts +71 -0
- package/dist/overrides.d.ts.map +1 -0
- package/dist/overrides.js +16 -0
- package/dist/overrides.js.map +1 -0
- package/dist/render/RenderBlocks.d.ts +67 -0
- package/dist/render/RenderBlocks.d.ts.map +1 -0
- package/dist/render/RenderBlocks.js +31 -0
- package/dist/render/RenderBlocks.js.map +1 -0
- package/dist/render/coerce.d.ts +78 -0
- package/dist/render/coerce.d.ts.map +1 -0
- package/dist/render/coerce.js +243 -0
- package/dist/render/coerce.js.map +1 -0
- package/dist/render/index.d.ts +5 -0
- package/dist/render/index.d.ts.map +1 -0
- package/dist/render/index.js +3 -0
- package/dist/render/index.js.map +1 -0
- package/dist/testing/checkBlockProps.d.ts +66 -0
- package/dist/testing/checkBlockProps.d.ts.map +1 -0
- package/dist/testing/checkBlockProps.js +241 -0
- package/dist/testing/checkBlockProps.js.map +1 -0
- package/dist/testing/checkBlockPropsCli.d.ts +12 -0
- package/dist/testing/checkBlockPropsCli.d.ts.map +1 -0
- package/dist/testing/checkBlockPropsCli.js +86 -0
- package/dist/testing/checkBlockPropsCli.js.map +1 -0
- package/dist/testing/contractDefaults.d.ts +21 -0
- package/dist/testing/contractDefaults.d.ts.map +1 -0
- package/dist/testing/contractDefaults.js +65 -0
- package/dist/testing/contractDefaults.js.map +1 -0
- package/dist/testing/describeBlockInvariants.d.ts +39 -0
- package/dist/testing/describeBlockInvariants.d.ts.map +1 -0
- package/dist/testing/describeBlockInvariants.js +71 -0
- package/dist/testing/describeBlockInvariants.js.map +1 -0
- package/dist/testing/index.d.ts +18 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +14 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/untouchedBlocks.d.ts +48 -0
- package/dist/testing/untouchedBlocks.d.ts.map +1 -0
- package/dist/testing/untouchedBlocks.js +155 -0
- package/dist/testing/untouchedBlocks.js.map +1 -0
- package/package.json +108 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The hand-authored half of the bridge.
|
|
3
|
+
*
|
|
4
|
+
* A design-system contract describes a *content model* — what an author writes.
|
|
5
|
+
* A React component has a *prop model* — what it renders from. They are the
|
|
6
|
+
* same thing 99 times out of 100, which is what makes generation worth doing,
|
|
7
|
+
* and the exceptions are few enough to enumerate. This is where they are
|
|
8
|
+
* enumerated, once, for both the generator and the renderer: a field renamed
|
|
9
|
+
* here is renamed in the Payload schema *and* in what gets passed to the
|
|
10
|
+
* component, so the two can never disagree.
|
|
11
|
+
*/
|
|
12
|
+
/** Look an override up by component and dotted field path. */
|
|
13
|
+
export function fieldOverride(overrides, component, path) {
|
|
14
|
+
return overrides[component]?.fields?.[path];
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=overrides.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overrides.js","sourceRoot":"","sources":["../src/overrides.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA8DH,8DAA8D;AAC9D,MAAM,UAAU,aAAa,CAC3B,SAAoB,EACpB,SAAiB,EACjB,IAAY;IAEZ,OAAO,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,CAAA;AAC7C,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type ComponentType, type JSX, type ReactNode } from 'react';
|
|
2
|
+
import type { ContentField } from '../generate/fields.js';
|
|
3
|
+
import { type CoerceContext } from './coerce.js';
|
|
4
|
+
/** The shape every stored block has, whatever its type. */
|
|
5
|
+
export interface StoredBlock {
|
|
6
|
+
blockType: string;
|
|
7
|
+
id?: string | null;
|
|
8
|
+
blockName?: string | null;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
export interface RenderBlocksProps {
|
|
12
|
+
blocks: readonly StoredBlock[] | null | undefined;
|
|
13
|
+
/** blockType → the React component that renders it. */
|
|
14
|
+
registry: Readonly<Record<string, ComponentType<Record<string, unknown>>>>;
|
|
15
|
+
/** blockType → the contract fields the block was generated from. */
|
|
16
|
+
fields: Readonly<Record<string, ContentField[]>>;
|
|
17
|
+
context: CoerceContext;
|
|
18
|
+
/**
|
|
19
|
+
* What to do with a block whose type has no component. Defaults to rendering
|
|
20
|
+
* nothing — a page with one unrenderable block should still serve.
|
|
21
|
+
*/
|
|
22
|
+
onUnknownBlock?: (blockType: string) => ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* Extra props merged into a block's, keyed by `blockType`.
|
|
25
|
+
*
|
|
26
|
+
* For the things a route supplies and an editor cannot author. The filter row
|
|
27
|
+
* in the Insights hero is the case this exists for: a row of links built from
|
|
28
|
+
* the taxonomy and the current URL, handed to `ImageHero` as `children`.
|
|
29
|
+
* Nobody types it into a block, and `coerceBlock` only ever produces props the
|
|
30
|
+
* contract declares — so without a way in, route-driven content has no way to
|
|
31
|
+
* reach a component that a block renders.
|
|
32
|
+
*
|
|
33
|
+
* Applies to **every** block of that type on the page, which is right for
|
|
34
|
+
* what it is for: route-level knowledge is the same for every block on the
|
|
35
|
+
* route. When two blocks of one type need to differ from each other, the
|
|
36
|
+
* difference is a property of the *block*, not of the route — use
|
|
37
|
+
* `resolveProps`.
|
|
38
|
+
*/
|
|
39
|
+
slots?: Readonly<Record<string, Record<string, unknown>>>;
|
|
40
|
+
/**
|
|
41
|
+
* Extra props derived from the stored block itself.
|
|
42
|
+
*
|
|
43
|
+
* The other axis from `slots`. That one answers "what does this route know
|
|
44
|
+
* that no block does"; this one answers "what does this block hold that the
|
|
45
|
+
* contract does not declare". `coerceBlock` only ever produces props the
|
|
46
|
+
* contract declares, so a value spliced into a stored block by a hydration
|
|
47
|
+
* pass — a form definition fetched for the form this block points at — has no
|
|
48
|
+
* other way through.
|
|
49
|
+
*
|
|
50
|
+
* Two forms on one campaign page is the case that needs it, and it is the
|
|
51
|
+
* ordinary case rather than the edge one: a signup band and a download form,
|
|
52
|
+
* each pointing at a different form. Keyed by type, they would be handed the
|
|
53
|
+
* same one.
|
|
54
|
+
*
|
|
55
|
+
* Applied before `slots`, so a route still outranks a block.
|
|
56
|
+
*/
|
|
57
|
+
resolveProps?: (block: StoredBlock) => Record<string, unknown> | undefined;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Renders a stored `layout` as design-system components.
|
|
61
|
+
*
|
|
62
|
+
* A server component. The components that call hooks carry their own
|
|
63
|
+
* `'use client'`, so the boundary is drawn per component by the design system
|
|
64
|
+
* rather than here — which is what keeps the rest out of the client bundle.
|
|
65
|
+
*/
|
|
66
|
+
export declare function RenderBlocks({ blocks, registry, fields, context, onUnknownBlock, slots, resolveProps, }: RenderBlocksProps): JSX.Element | null;
|
|
67
|
+
//# sourceMappingURL=RenderBlocks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RenderBlocks.d.ts","sourceRoot":"","sources":["../../src/render/RenderBlocks.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,aAAa,EAAE,KAAK,GAAG,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAC9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACzD,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,aAAa,CAAA;AAE7D,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,IAAI,GAAG,SAAS,CAAA;IACjD,uDAAuD;IACvD,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAA;IAC1E,oEAAoE;IACpE,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC,CAAA;IAChD,OAAO,EAAE,aAAa,CAAA;IACtB;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,SAAS,CAAA;IACjD;;;;;;;;;;;;;;;OAeG;IACH,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IACzD;;;;;;;;;;;;;;;;OAgBG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;CAC3E;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,QAAQ,EACR,MAAM,EACN,OAAO,EACP,cAAc,EACd,KAAK,EACL,YAAY,GACb,EAAE,iBAAiB,GAAG,GAAG,CAAC,OAAO,GAAG,IAAI,CA2BxC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment } from 'react';
|
|
3
|
+
import { coerceBlock } from './coerce.js';
|
|
4
|
+
/**
|
|
5
|
+
* Renders a stored `layout` as design-system components.
|
|
6
|
+
*
|
|
7
|
+
* A server component. The components that call hooks carry their own
|
|
8
|
+
* `'use client'`, so the boundary is drawn per component by the design system
|
|
9
|
+
* rather than here — which is what keeps the rest out of the client bundle.
|
|
10
|
+
*/
|
|
11
|
+
export function RenderBlocks({ blocks, registry, fields, context, onUnknownBlock, slots, resolveProps, }) {
|
|
12
|
+
if (!blocks || blocks.length === 0)
|
|
13
|
+
return null;
|
|
14
|
+
return (_jsx(_Fragment, { children: blocks.map((block, index) => {
|
|
15
|
+
const Component = registry[block.blockType];
|
|
16
|
+
const contractFields = fields[block.blockType];
|
|
17
|
+
if (!Component || !contractFields) {
|
|
18
|
+
return (_jsx(Fragment, { children: onUnknownBlock?.(block.blockType) ?? null }, block.id ?? index));
|
|
19
|
+
}
|
|
20
|
+
/* Slot props last: the route knows things the stored block does not,
|
|
21
|
+
and neither a block nor its own resolver can author its way into
|
|
22
|
+
overriding them. */
|
|
23
|
+
const props = {
|
|
24
|
+
...coerceBlock(block.blockType, contractFields, block, context),
|
|
25
|
+
...resolveProps?.(block),
|
|
26
|
+
...slots?.[block.blockType],
|
|
27
|
+
};
|
|
28
|
+
return _jsx(Component, { ...props }, block.id ?? index);
|
|
29
|
+
}) }));
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=RenderBlocks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RenderBlocks.js","sourceRoot":"","sources":["../../src/render/RenderBlocks.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAgD,MAAM,OAAO,CAAA;AAE9E,OAAO,EAAE,WAAW,EAAsB,MAAM,aAAa,CAAA;AA2D7D;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,EAC3B,MAAM,EACN,QAAQ,EACR,MAAM,EACN,OAAO,EACP,cAAc,EACd,KAAK,EACL,YAAY,GACM;IAClB,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAE/C,OAAO,CACL,4BACG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YAC3B,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;YAC3C,MAAM,cAAc,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;YAE9C,IAAI,CAAC,SAAS,IAAI,CAAC,cAAc,EAAE,CAAC;gBAClC,OAAO,CACL,KAAC,QAAQ,cAA0B,cAAc,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,IAAI,IAA7D,KAAK,CAAC,EAAE,IAAI,KAAK,CAAwD,CACzF,CAAA;YACH,CAAC;YAED;;kCAEsB;YACtB,MAAM,KAAK,GAAG;gBACZ,GAAG,WAAW,CAAC,KAAK,CAAC,SAAS,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,CAAC;gBAC/D,GAAG,YAAY,EAAE,CAAC,KAAK,CAAC;gBACxB,GAAG,KAAK,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC;aAC5B,CAAA;YACD,OAAO,KAAC,SAAS,OAA6B,KAAK,IAA5B,KAAK,CAAC,EAAE,IAAI,KAAK,CAAe,CAAA;QACzD,CAAC,CAAC,GACD,CACJ,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { type Overrides } from '../overrides.js';
|
|
3
|
+
import type { ContentField } from '../generate/fields.js';
|
|
4
|
+
/**
|
|
5
|
+
* What Payload stores is not what a component takes.
|
|
6
|
+
*
|
|
7
|
+
* An `image` field is a media id or a populated document; the component wants
|
|
8
|
+
* a URL. A `link` is a group with a mode and a relationship; the component
|
|
9
|
+
* wants an href. An `array` row carries Payload's own `id`, which the component
|
|
10
|
+
* has no field for. Coercion is that translation, and it is driven by the same
|
|
11
|
+
* `ContentField[]` the block was generated from — so the two cannot describe
|
|
12
|
+
* different shapes.
|
|
13
|
+
*/
|
|
14
|
+
export interface MediaLike {
|
|
15
|
+
url?: string | null;
|
|
16
|
+
alt?: string | null;
|
|
17
|
+
filename?: string | null;
|
|
18
|
+
/**
|
|
19
|
+
* The stored file's own width. The host uses it to keep a `srcSet` from
|
|
20
|
+
* offering candidates larger than the original, which no optimizer can
|
|
21
|
+
* produce and which would therefore all be the same bytes.
|
|
22
|
+
*/
|
|
23
|
+
width?: number | null;
|
|
24
|
+
/**
|
|
25
|
+
* Candidate widths for the same image, as a `srcset` value. Optional: a host
|
|
26
|
+
* with no image pipeline resolves a `url` and nothing else, and every
|
|
27
|
+
* component treats `srcSet` as absent rather than empty.
|
|
28
|
+
*/
|
|
29
|
+
srcSet?: string | null;
|
|
30
|
+
/**
|
|
31
|
+
* How wide the image is drawn, as a `sizes` value. Ignored without a
|
|
32
|
+
* `srcSet`, which is the only thing it selects from.
|
|
33
|
+
*/
|
|
34
|
+
sizes?: string | null;
|
|
35
|
+
/**
|
|
36
|
+
* The stored file's own mime type. The media collection accepts video as
|
|
37
|
+
* well as images, and the two cannot be treated alike: a host that resizes
|
|
38
|
+
* images through an optimizer has to leave an `video/mp4` URL alone, because
|
|
39
|
+
* an image optimizer handed a video returns nothing it can play.
|
|
40
|
+
*/
|
|
41
|
+
mimeType?: string | null;
|
|
42
|
+
}
|
|
43
|
+
export interface LinkValue {
|
|
44
|
+
mode?: ('internal' | 'external' | 'anchor') | null;
|
|
45
|
+
reference?: {
|
|
46
|
+
relationTo: string;
|
|
47
|
+
value: unknown;
|
|
48
|
+
} | null;
|
|
49
|
+
url?: string | null;
|
|
50
|
+
anchor?: string | null;
|
|
51
|
+
newTab?: boolean | null;
|
|
52
|
+
}
|
|
53
|
+
export interface CoerceContext {
|
|
54
|
+
overrides: Overrides;
|
|
55
|
+
/** Turns a stored media value into a URL. Null when it cannot be resolved. */
|
|
56
|
+
/**
|
|
57
|
+
* Turns a stored upload into a media document.
|
|
58
|
+
*
|
|
59
|
+
* `where` names the component and field the image is for — `CardGrid` /
|
|
60
|
+
* `items.image`, `ImageHero` / `image.src`. The host uses it to decide how
|
|
61
|
+
* large the image needs to be, which it cannot know from the value alone: a
|
|
62
|
+
* card and a full-bleed hero hold the same shape and want very different
|
|
63
|
+
* numbers of pixels.
|
|
64
|
+
*/
|
|
65
|
+
resolveMedia: (value: unknown, where: {
|
|
66
|
+
component: string;
|
|
67
|
+
path: string;
|
|
68
|
+
}) => MediaLike | null;
|
|
69
|
+
/** Turns an internal reference into a path. */
|
|
70
|
+
resolveHref: (link: LinkValue) => string | undefined;
|
|
71
|
+
/** Turns Lexical state into React. Project-specific, so it is injected. */
|
|
72
|
+
renderRichText: (value: unknown) => ReactNode;
|
|
73
|
+
/** Turns an icon name into a rendered glyph. */
|
|
74
|
+
renderIcon: (name: string) => ReactNode;
|
|
75
|
+
}
|
|
76
|
+
/** One component's block data, as props. */
|
|
77
|
+
export declare function coerceBlock(component: string, fields: ContentField[], data: Record<string, unknown>, ctx: CoerceContext): Record<string, unknown>;
|
|
78
|
+
//# sourceMappingURL=coerce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coerce.d.ts","sourceRoot":"","sources":["../../src/render/coerce.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEzD;;;;;;;;;GASG;AAEH,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,CAAC,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAA;IAClD,SAAS,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAA;IACzD,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,SAAS,CAAA;IACpB,8EAA8E;IAC9E;;;;;;;;OAQG;IACH,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,SAAS,GAAG,IAAI,CAAA;IAC9F,+CAA+C;IAC/C,WAAW,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,MAAM,GAAG,SAAS,CAAA;IACpD,2EAA2E;IAC3E,cAAc,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,SAAS,CAAA;IAC7C,gDAAgD;IAChD,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAAA;CACxC;AAgKD,4CAA4C;AAC5C,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EAAE,EACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,aAAa,GACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAIzB"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { pairedAltFields } from '../altText.js';
|
|
2
|
+
import { fieldOverride } from '../overrides.js';
|
|
3
|
+
/**
|
|
4
|
+
* Drops a key entirely rather than passing `undefined`.
|
|
5
|
+
*
|
|
6
|
+
* `exactOptionalPropertyTypes` is on, so `{ heading: undefined }` is not
|
|
7
|
+
* assignable to `{ heading?: string }`. Every optional prop has to be absent
|
|
8
|
+
* rather than explicitly undefined, and there are several hundred of them.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Whether a stored value amounts to nothing, all the way down.
|
|
12
|
+
*
|
|
13
|
+
* `mode` is skipped because `linkField` gives it a default: it says which kind
|
|
14
|
+
* of link this *would* be, and is present whether or not anybody chose
|
|
15
|
+
* anything. `false` is skipped for the same kind of reason — Payload stores a
|
|
16
|
+
* checkbox's default and an author's deliberate untick identically, so an
|
|
17
|
+
* unticked box is no evidence that anybody has been here.
|
|
18
|
+
*
|
|
19
|
+
* This is `isEmpty` in `generate/fields.ts`, and the two have to agree: one
|
|
20
|
+
* decides whether a group may be saved, the other whether it reaches a
|
|
21
|
+
* component. They did not agree about `false`, and the gap was live.
|
|
22
|
+
* `ManagedForm.consent` is an optional group whose `required` boolean is
|
|
23
|
+
* stored `false` on every block nobody has configured — so the validation
|
|
24
|
+
* called it empty and let the block save, and this called it filled and handed
|
|
25
|
+
* the component `{ required: false, text: '' }`. That is a truthy object of
|
|
26
|
+
* empty values, which is exactly the shape the note in the `group` branch
|
|
27
|
+
* below exists to prevent, arriving by the one route that note did not cover.
|
|
28
|
+
*/
|
|
29
|
+
function isEmptyValue(value) {
|
|
30
|
+
if (value === undefined || value === null || value === '')
|
|
31
|
+
return true;
|
|
32
|
+
if (value === false)
|
|
33
|
+
return true;
|
|
34
|
+
if (Array.isArray(value))
|
|
35
|
+
return value.every(isEmptyValue);
|
|
36
|
+
if (typeof value === 'object') {
|
|
37
|
+
return Object.entries(value).every(([key, held]) => key === 'mode' || isEmptyValue(held));
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
function put(target, key, value) {
|
|
42
|
+
if (value === undefined || value === null)
|
|
43
|
+
return;
|
|
44
|
+
target[key] = value;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The props that carry an image's candidate widths, beside the one that carries
|
|
48
|
+
* its URL.
|
|
49
|
+
*
|
|
50
|
+
* The design system names them by suffixing the image's own prop — `image` has
|
|
51
|
+
* `imageSrcSet` and `imageSizes`, `poster` has `posterSrcSet` and
|
|
52
|
+
* `posterSizes` — except where the image prop is already called `src`, inside
|
|
53
|
+
* an `ImageRef` group or array row, where the siblings are the bare `srcSet`
|
|
54
|
+
* and `sizes` of the HTML attributes.
|
|
55
|
+
*
|
|
56
|
+
* They are not fields in any contract, and must not become ones: a `srcset` is
|
|
57
|
+
* not content an author writes, it is what an optimizer computed. So the CMS
|
|
58
|
+
* offers `src` and `alt`, `resolveMedia` returns the rest, and this is the one
|
|
59
|
+
* place that knows where they go.
|
|
60
|
+
*/
|
|
61
|
+
function responsiveProps(imageProp) {
|
|
62
|
+
return imageProp === 'src'
|
|
63
|
+
? { srcSet: 'srcSet', sizes: 'sizes' }
|
|
64
|
+
: { srcSet: `${imageProp}SrcSet`, sizes: `${imageProp}Sizes` };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Writes one field into `target` under the prop name — or names — it maps to.
|
|
68
|
+
*
|
|
69
|
+
* Every field but an image is one key. An image is up to three, which is why
|
|
70
|
+
* this exists rather than the three call sites each putting a single returned
|
|
71
|
+
* value.
|
|
72
|
+
*
|
|
73
|
+
* Returns the media document an image resolved to, so `putFields` can fall back
|
|
74
|
+
* to its alt.
|
|
75
|
+
*/
|
|
76
|
+
function putField(target, component, field, value, ctx, path) {
|
|
77
|
+
const override = fieldOverride(ctx.overrides, component, path);
|
|
78
|
+
if (override?.omit)
|
|
79
|
+
return undefined;
|
|
80
|
+
// The one name mismatch in the system — ArticleBody's `body` is the
|
|
81
|
+
// component's `children` — declared in the overrides so the generator and
|
|
82
|
+
// this agree by construction rather than by memory.
|
|
83
|
+
const name = override?.propName ?? field.name;
|
|
84
|
+
if (field.type === 'image' && override?.as !== 'icon' && value !== undefined && value !== null) {
|
|
85
|
+
const media = ctx.resolveMedia(value, { component, path });
|
|
86
|
+
if (!media?.url)
|
|
87
|
+
return undefined;
|
|
88
|
+
const responsive = responsiveProps(name);
|
|
89
|
+
put(target, name, media.url);
|
|
90
|
+
put(target, responsive.srcSet, media.srcSet);
|
|
91
|
+
put(target, responsive.sizes, media.sizes);
|
|
92
|
+
return media;
|
|
93
|
+
}
|
|
94
|
+
/*
|
|
95
|
+
An uploaded video is one key, never three. A `srcset` picks between candidate
|
|
96
|
+
widths of the same picture, and there are no candidate widths of a video file
|
|
97
|
+
— the browser cannot re-encode one, and offering `videoSrcSet` would put a
|
|
98
|
+
prop on the component that no component takes.
|
|
99
|
+
*/
|
|
100
|
+
if (field.type === 'video' &&
|
|
101
|
+
override?.as === 'videoUpload' &&
|
|
102
|
+
value !== undefined &&
|
|
103
|
+
value !== null) {
|
|
104
|
+
const media = ctx.resolveMedia(value, { component, path });
|
|
105
|
+
if (!media?.url)
|
|
106
|
+
return undefined;
|
|
107
|
+
put(target, name, media.url);
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
put(target, name, coerceField(component, field, value, ctx, path));
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Writes a block's, group's or row's fields into `target`, then fills each
|
|
115
|
+
* empty alt beside an image from the image's media document.
|
|
116
|
+
*
|
|
117
|
+
* After the loop rather than inside it, because the alt field may come before
|
|
118
|
+
* or after its image, and an empty one written later would overwrite the
|
|
119
|
+
* fallback. Only an alt the contract declares is filled — a component with no
|
|
120
|
+
* alt prop beside an image is not handed one. See `altText.ts`.
|
|
121
|
+
*/
|
|
122
|
+
function putFields(target, component, fields, source, ctx, parentPath) {
|
|
123
|
+
const pathOf = (name) => (parentPath ? `${parentPath}.${name}` : name);
|
|
124
|
+
const resolved = new Map();
|
|
125
|
+
for (const field of fields) {
|
|
126
|
+
const media = putField(target, component, field, source[field.name], ctx, pathOf(field.name));
|
|
127
|
+
if (media)
|
|
128
|
+
resolved.set(field.name, media);
|
|
129
|
+
}
|
|
130
|
+
for (const [image, alt] of pairedAltFields(fields)) {
|
|
131
|
+
const fallback = resolved.get(image)?.alt?.trim();
|
|
132
|
+
if (!fallback)
|
|
133
|
+
continue;
|
|
134
|
+
const override = fieldOverride(ctx.overrides, component, pathOf(alt));
|
|
135
|
+
if (override?.omit)
|
|
136
|
+
continue;
|
|
137
|
+
const name = override?.propName ?? alt;
|
|
138
|
+
const own = target[name];
|
|
139
|
+
if (typeof own === 'string' && own.trim() !== '')
|
|
140
|
+
continue;
|
|
141
|
+
target[name] = fallback;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/** One component's block data, as props. */
|
|
145
|
+
export function coerceBlock(component, fields, data, ctx) {
|
|
146
|
+
const props = {};
|
|
147
|
+
putFields(props, component, fields, data, ctx);
|
|
148
|
+
return props;
|
|
149
|
+
}
|
|
150
|
+
function coerceField(component, field, value, ctx, path) {
|
|
151
|
+
if (value === undefined || value === null)
|
|
152
|
+
return undefined;
|
|
153
|
+
const override = fieldOverride(ctx.overrides, component, path);
|
|
154
|
+
if (override?.as === 'icon' && typeof value === 'string') {
|
|
155
|
+
return ctx.renderIcon(value);
|
|
156
|
+
}
|
|
157
|
+
switch (field.type) {
|
|
158
|
+
case 'text':
|
|
159
|
+
case 'video':
|
|
160
|
+
case 'select':
|
|
161
|
+
case 'number':
|
|
162
|
+
return value;
|
|
163
|
+
case 'boolean':
|
|
164
|
+
return Boolean(value);
|
|
165
|
+
case 'richtext':
|
|
166
|
+
return ctx.renderRichText(value);
|
|
167
|
+
/*
|
|
168
|
+
The URL alone, with no responsive siblings — because there is nowhere to put
|
|
169
|
+
them. This branch is only reached through the scalar-array path below, where
|
|
170
|
+
a row *is* its single value and has no object to hold a `srcSet` beside it.
|
|
171
|
+
Every image field in the design system today arrives through `putField`
|
|
172
|
+
instead; no contract has a single-image-child array. If one ever does, its
|
|
173
|
+
images will be right-sized but not responsive, and the fix is a row object
|
|
174
|
+
rather than a scalar.
|
|
175
|
+
*/
|
|
176
|
+
case 'image': {
|
|
177
|
+
const media = ctx.resolveMedia(value, { component, path });
|
|
178
|
+
return media?.url ?? undefined;
|
|
179
|
+
}
|
|
180
|
+
case 'link':
|
|
181
|
+
return ctx.resolveHref(value);
|
|
182
|
+
case 'group': {
|
|
183
|
+
// A slot: no children in the contract, nothing stored, nothing to pass.
|
|
184
|
+
if (!field.of)
|
|
185
|
+
return undefined;
|
|
186
|
+
/*
|
|
187
|
+
An untouched optional group must arrive as `undefined`, not as an object
|
|
188
|
+
of empty values.
|
|
189
|
+
|
|
190
|
+
Payload stores one either way — `ChallengeOverview.caseStudy` comes back
|
|
191
|
+
as `{ stat: {}, href: { mode: 'internal' } }` when nobody has filled it
|
|
192
|
+
in, because `linkField` defaults `mode`. Passed through, that is truthy,
|
|
193
|
+
and a component guarding with `{caseStudy && <RelatedCard ... />}` renders
|
|
194
|
+
a card with no title: an empty `<h3>` in the page, which is an axe
|
|
195
|
+
violation and a heading a screen reader announces as nothing.
|
|
196
|
+
*/
|
|
197
|
+
if (isEmptyValue(value))
|
|
198
|
+
return undefined;
|
|
199
|
+
const nested = value;
|
|
200
|
+
const out = {};
|
|
201
|
+
putFields(out, component, field.of, nested, ctx, path);
|
|
202
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
203
|
+
}
|
|
204
|
+
case 'array': {
|
|
205
|
+
if (!field.of || !Array.isArray(value))
|
|
206
|
+
return undefined;
|
|
207
|
+
/*
|
|
208
|
+
An array of primitives comes back as an array of primitives.
|
|
209
|
+
|
|
210
|
+
A contract that says `tags: string[]` is expressed in the manifest as an
|
|
211
|
+
array whose `of` is a single `text` child — `[{ name: 'tag' }]` — because
|
|
212
|
+
Payload has no bare array-of-strings field and a row needs a column to
|
|
213
|
+
live in. Mapping every row to an object regardless turns `['AI']` into
|
|
214
|
+
`[{ tag: 'AI' }]`, which type-checks nowhere and renders as
|
|
215
|
+
`[object Object]`.
|
|
216
|
+
|
|
217
|
+
Every single-child array in the design system is a scalar array —
|
|
218
|
+
`KeyPoints.points`, `TagList.tags`, `AudioPlayer.speeds` (numbers, hence
|
|
219
|
+
coercing the value rather than stringifying it), `Card.tags`. Checked
|
|
220
|
+
against each component's own prop type, not inferred from the shape. A
|
|
221
|
+
count used to be written here and had drifted by two by the time anyone
|
|
222
|
+
re-ran it, so it is gone: the invariant is what holds, not the total. If
|
|
223
|
+
a genuine one-field *object* row ever appears, this is where it breaks,
|
|
224
|
+
and the fix is a marker in the contract rather than a guess here.
|
|
225
|
+
*/
|
|
226
|
+
const [only] = field.of;
|
|
227
|
+
if (field.of.length === 1 && only && !only.of) {
|
|
228
|
+
return value
|
|
229
|
+
.map(row => coerceField(component, only, row[only.name], ctx, `${path}.${only.name}`))
|
|
230
|
+
.filter(entry => entry !== undefined);
|
|
231
|
+
}
|
|
232
|
+
return value.map(row => {
|
|
233
|
+
const source = row;
|
|
234
|
+
const out = {};
|
|
235
|
+
putFields(out, component, field.of ?? [], source, ctx, path);
|
|
236
|
+
// `id` is Payload's row key. The component has no such prop, and React
|
|
237
|
+
// would pass it straight through to the DOM.
|
|
238
|
+
return out;
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=coerce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coerce.js","sourceRoot":"","sources":["../../src/render/coerce.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAkB,MAAM,iBAAiB,CAAA;AAyE/D;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,IAAI,CAAA;IACtE,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,IAAI,CAAA;IAChC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;IAC1D,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,MAAM,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,CAAA;IAC3F,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,GAAG,CAAC,MAA+B,EAAE,GAAW,EAAE,KAAc;IACvE,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAM;IACjD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;AACrB,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAS,eAAe,CAAC,SAAiB;IACxC,OAAO,SAAS,KAAK,KAAK;QACxB,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE;QACtC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,QAAQ,EAAE,KAAK,EAAE,GAAG,SAAS,OAAO,EAAE,CAAA;AAClE,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,QAAQ,CACf,MAA+B,EAC/B,SAAiB,EACjB,KAAmB,EACnB,KAAc,EACd,GAAkB,EAClB,IAAY;IAEZ,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,CAAA;IAC9D,IAAI,QAAQ,EAAE,IAAI;QAAE,OAAO,SAAS,CAAA;IAEpC,oEAAoE;IACpE,0EAA0E;IAC1E,oDAAoD;IACpD,MAAM,IAAI,GAAG,QAAQ,EAAE,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAA;IAE7C,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,QAAQ,EAAE,EAAE,KAAK,MAAM,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC/F,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAC1D,IAAI,CAAC,KAAK,EAAE,GAAG;YAAE,OAAO,SAAS,CAAA;QACjC,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;QACxC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;QAC5B,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;QAC5C,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;QAC1C,OAAO,KAAK,CAAA;IACd,CAAC;IAED;;;;;MAKE;IACF,IACE,KAAK,CAAC,IAAI,KAAK,OAAO;QACtB,QAAQ,EAAE,EAAE,KAAK,aAAa;QAC9B,KAAK,KAAK,SAAS;QACnB,KAAK,KAAK,IAAI,EACd,CAAC;QACD,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAC1D,IAAI,CAAC,KAAK,EAAE,GAAG;YAAE,OAAO,SAAS,CAAA;QACjC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;QAC5B,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAA;IAClE,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,SAAS,CAChB,MAA+B,EAC/B,SAAiB,EACjB,MAA+B,EAC/B,MAA+B,EAC/B,GAAkB,EAClB,UAAmB;IAEnB,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IAC9E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAqB,CAAA;IAE7C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;QAC7F,IAAI,KAAK;YAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC5C,CAAC;IAED,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAA;QACjD,IAAI,CAAC,QAAQ;YAAE,SAAQ;QACvB,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QACrE,IAAI,QAAQ,EAAE,IAAI;YAAE,SAAQ;QAC5B,MAAM,IAAI,GAAG,QAAQ,EAAE,QAAQ,IAAI,GAAG,CAAA;QACtC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAA;QACxB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,SAAQ;QAC1D,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAA;IACzB,CAAC;AACH,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,WAAW,CACzB,SAAiB,EACjB,MAAsB,EACtB,IAA6B,EAC7B,GAAkB;IAElB,MAAM,KAAK,GAA4B,EAAE,CAAA;IACzC,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAA;IAC9C,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,WAAW,CAClB,SAAiB,EACjB,KAAmB,EACnB,KAAc,EACd,GAAkB,EAClB,IAAY;IAEZ,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAA;IAC3D,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,CAAA;IAE9D,IAAI,QAAQ,EAAE,EAAE,KAAK,MAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACzD,OAAO,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;IAC9B,CAAC;IAED,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO,CAAC;QACb,KAAK,QAAQ,CAAC;QACd,KAAK,QAAQ;YACX,OAAO,KAAK,CAAA;QAEd,KAAK,SAAS;YACZ,OAAO,OAAO,CAAC,KAAK,CAAC,CAAA;QAEvB,KAAK,UAAU;YACb,OAAO,GAAG,CAAC,cAAc,CAAC,KAAK,CAAC,CAAA;QAElC;;;;;;;;UAQE;QACF,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YAC1D,OAAO,KAAK,EAAE,GAAG,IAAI,SAAS,CAAA;QAChC,CAAC;QAED,KAAK,MAAM;YACT,OAAO,GAAG,CAAC,WAAW,CAAC,KAAkB,CAAC,CAAA;QAE5C,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,wEAAwE;YACxE,IAAI,CAAC,KAAK,CAAC,EAAE;gBAAE,OAAO,SAAS,CAAA;YAC/B;;;;;;;;;;cAUE;YACF,IAAI,YAAY,CAAC,KAAK,CAAC;gBAAE,OAAO,SAAS,CAAA;YACzC,MAAM,MAAM,GAAG,KAAgC,CAAA;YAC/C,MAAM,GAAG,GAA4B,EAAE,CAAA;YACvC,SAAS,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;YACtD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAA;QACtD,CAAC;QAED,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,OAAO,SAAS,CAAA;YAExD;;;;;;;;;;;;;;;;;;cAkBE;YACF,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,EAAE,CAAA;YACvB,IAAI,KAAK,CAAC,EAAE,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;gBAC9C,OAAO,KAAK;qBACT,GAAG,CAAC,GAAG,CAAC,EAAE,CACT,WAAW,CACT,SAAS,EACT,IAAI,EACH,GAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,EAC3C,GAAG,EACH,GAAG,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CACvB,CACF;qBACA,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAA;YACzC,CAAC;YAED,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;gBACrB,MAAM,MAAM,GAAG,GAA8B,CAAA;gBAC7C,MAAM,GAAG,GAA4B,EAAE,CAAA;gBACvC,SAAS,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;gBAC5D,uEAAuE;gBACvE,6CAA6C;gBAC7C,OAAO,GAAG,CAAA;YACZ,CAAC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { coerceBlock } from './coerce.js';
|
|
2
|
+
export type { CoerceContext, LinkValue, MediaLike } from './coerce.js';
|
|
3
|
+
export { RenderBlocks } from './RenderBlocks.js';
|
|
4
|
+
export type { RenderBlocksProps, StoredBlock } from './RenderBlocks.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAEzC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ContentField } from '../generate/fields.js';
|
|
2
|
+
import type { Overrides } from '../overrides.js';
|
|
3
|
+
import { type CoerceContext } from '../render/coerce.js';
|
|
4
|
+
/**
|
|
5
|
+
* Real overrides, stubbed resolvers. Overrides change the *shape* — they omit
|
|
6
|
+
* fields, rename `body` to `children`, turn an icon name into a glyph — so they
|
|
7
|
+
* have to be the host's own. The resolvers turn an id into a URL or Lexical
|
|
8
|
+
* state into React; a stub produces the same shape as the real thing.
|
|
9
|
+
*/
|
|
10
|
+
export declare function stubCoerceContext(overrides?: Overrides): CoerceContext;
|
|
11
|
+
/** What Payload stores for a field of each type, filled in. */
|
|
12
|
+
export declare function storedValue(field: ContentField): unknown;
|
|
13
|
+
/** A whole block's stored data, for one component's fields. */
|
|
14
|
+
export declare function storedObject(fields: readonly ContentField[]): Record<string, unknown>;
|
|
15
|
+
export type PropKind = 'string' | 'number' | 'boolean' | 'array' | 'object' | 'node' | 'absent';
|
|
16
|
+
export declare function kindOf(value: unknown): PropKind;
|
|
17
|
+
export interface PropMismatch {
|
|
18
|
+
component: string;
|
|
19
|
+
prop: string;
|
|
20
|
+
coerced: PropKind;
|
|
21
|
+
args: PropKind;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Compare two values structurally, recording where they disagree. Recurses
|
|
25
|
+
* into objects and into the first element of an array, where a row's shape
|
|
26
|
+
* lives; depth-limited, because a contract nests no deeper than an array of
|
|
27
|
+
* groups and a cycle in an args file must not hang the check.
|
|
28
|
+
*/
|
|
29
|
+
export declare function compareShapes(component: string, prop: string, coerced: unknown, args: unknown, found: PropMismatch[], depth?: number): void;
|
|
30
|
+
/** The manifest slice this reads. */
|
|
31
|
+
export interface PropsManifest {
|
|
32
|
+
components: Record<string, {
|
|
33
|
+
content: {
|
|
34
|
+
fields: ContentField[];
|
|
35
|
+
};
|
|
36
|
+
}>;
|
|
37
|
+
}
|
|
38
|
+
export interface CheckBlockPropsOptions {
|
|
39
|
+
manifest: PropsManifest;
|
|
40
|
+
/** Finds a component's args, or `undefined` when it has none. */
|
|
41
|
+
loadArgs: (component: string) => Promise<Record<string, unknown> | undefined>;
|
|
42
|
+
/** The host's overrides — the same object the generator and renderer use. */
|
|
43
|
+
overrides?: Overrides;
|
|
44
|
+
}
|
|
45
|
+
export interface CheckBlockPropsResult {
|
|
46
|
+
checked: string[];
|
|
47
|
+
/** Components with no args file, which nothing could be compared with. */
|
|
48
|
+
skipped: string[];
|
|
49
|
+
mismatches: PropMismatch[];
|
|
50
|
+
}
|
|
51
|
+
/** Coerce every component's contract and compare the props with its args. */
|
|
52
|
+
export declare function checkBlockProps(options: CheckBlockPropsOptions): Promise<CheckBlockPropsResult>;
|
|
53
|
+
/**
|
|
54
|
+
* Finds `<Name>/<name>args.<ext>` — the case is not guessed — in the first of
|
|
55
|
+
* `componentDirs` that has one, and returns its default export.
|
|
56
|
+
*
|
|
57
|
+
* TypeScript args files need a loader that understands them: the
|
|
58
|
+
* `check-block-props` bin registers `tsx`, and vitest transforms on its own.
|
|
59
|
+
*/
|
|
60
|
+
export declare function argsFileLoader(componentDirs: readonly string[]): (component: string) => Promise<Record<string, unknown> | undefined>;
|
|
61
|
+
/** The report a person reads, and whether it is a failure. */
|
|
62
|
+
export declare function formatBlockPropsResult(result: CheckBlockPropsResult): {
|
|
63
|
+
ok: boolean;
|
|
64
|
+
text: string;
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=checkBlockProps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkBlockProps.d.ts","sourceRoot":"","sources":["../../src/testing/checkBlockProps.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAqCrE;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,GAAE,SAAc,GAAG,aAAa,CAc1E;AAED,+DAA+D;AAC/D,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CA+BxD;AAED,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAIrF;AAED,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAA;AAE/F,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAa/C;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,QAAQ,CAAA;IACjB,IAAI,EAAE,QAAQ,CAAA;CACf;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,YAAY,EAAE,EACrB,KAAK,SAAI,GACR,IAAI,CAsCN;AAED,qCAAqC;AACrC,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE;YAAE,MAAM,EAAE,YAAY,EAAE,CAAA;SAAE,CAAA;KAAE,CAAC,CAAA;CACpE;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,aAAa,CAAA;IACvB,iEAAiE;IACjE,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAAA;IAC7E,6EAA6E;IAC7E,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,UAAU,EAAE,YAAY,EAAE,CAAA;CAC3B;AAED,6EAA6E;AAC7E,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CAoBhC;AAID;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAsBrE;AAED,8DAA8D;AAC9D,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,qBAAqB,GAAG;IACrE,EAAE,EAAE,OAAO,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;CACb,CAsCA"}
|