@contentful/experiences-svelte 0.5.5 → 0.5.7
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 +24 -0
- package/README.md +8 -6
- package/dist/ClientExperienceRenderer.svelte +6 -11
- package/dist/ClientExperienceRenderer.svelte.d.ts.map +1 -1
- package/dist/DebugExperience.svelte +9 -2
- package/dist/DebugExperience.svelte.d.ts.map +1 -1
- package/dist/DebugExperience.test.js +22 -9
- package/dist/NodeRenderer.svelte +73 -19
- package/dist/NodeRenderer.svelte.d.ts +6 -1
- package/dist/NodeRenderer.svelte.d.ts.map +1 -1
- package/dist/NodesRenderer.svelte +40 -17
- package/dist/NodesRenderer.svelte.d.ts.map +1 -1
- package/dist/ServerExperienceRenderer.svelte +6 -11
- package/dist/ServerExperienceRenderer.svelte.d.ts.map +1 -1
- package/dist/design-utils.d.ts +0 -8
- package/dist/design-utils.d.ts.map +1 -1
- package/dist/design-utils.js +1 -10
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/nodes-renderer.ssr.test.d.ts +2 -0
- package/dist/nodes-renderer.ssr.test.d.ts.map +1 -0
- package/dist/nodes-renderer.ssr.test.js +126 -0
- package/dist/server-renderer.test.js +100 -44
- package/dist/test-fixtures/ExperienceTemplateFixture.svelte +8 -3
- package/dist/test-fixtures/ExperienceTemplateFixture.svelte.d.ts +1 -1
- package/dist/test-fixtures/ExperienceTemplateFixture.svelte.d.ts.map +1 -1
- package/dist/types.d.ts +28 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/use-active-viewport.svelte.d.ts.map +1 -1
- package/dist/use-active-viewport.svelte.js +1 -22
- package/package.json +5 -5
- package/dist/WrapWithExperienceTemplate.svelte +0 -90
- package/dist/WrapWithExperienceTemplate.svelte.d.ts +0 -13
- package/dist/WrapWithExperienceTemplate.svelte.d.ts.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
## 0.5.7 (2026-08-20)
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- **adapter-angular:** scaffold @contentful/experiences-angular [AIS-361] ([#134](https://github.com/contentful/experiences/pull/134))
|
|
6
|
+
|
|
7
|
+
### 🧱 Updated Dependencies
|
|
8
|
+
|
|
9
|
+
- Updated client to 0.3.6
|
|
10
|
+
- Updated design to 0.7.6
|
|
11
|
+
- Updated core to 0.7.6
|
|
12
|
+
|
|
13
|
+
## 0.5.6 (2026-08-12)
|
|
14
|
+
|
|
15
|
+
### 🩹 Fixes
|
|
16
|
+
|
|
17
|
+
- **core,adapters:** render Experience Templates as ordinary nodes [AIS-413] ([#133](https://github.com/contentful/experiences/pull/133))
|
|
18
|
+
|
|
19
|
+
### 🧱 Updated Dependencies
|
|
20
|
+
|
|
21
|
+
- Updated client to 0.3.5
|
|
22
|
+
- Updated design to 0.7.5
|
|
23
|
+
- Updated core to 0.7.5
|
|
24
|
+
|
|
1
25
|
## 0.5.5 (2026-08-11)
|
|
2
26
|
|
|
3
27
|
### 🚀 Features
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ The public API mirrors `@contentful/experiences-react` 1:1 in shape; only the re
|
|
|
20
20
|
|
|
21
21
|
```ts
|
|
22
22
|
defineComponent<Props>(config); // Type-narrowing identity for component-type configs
|
|
23
|
-
defineExperienceTemplate<Props>(config); // Same shape, for
|
|
23
|
+
defineExperienceTemplate<Props>(config); // Same shape, for coded Experience Template configs
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
### Resolver
|
|
@@ -35,7 +35,7 @@ resolveExperience(payload, config, opts?); // Async; walks payload, runs resolve
|
|
|
35
35
|
ServerExperienceRenderer; // SSR-safe; active viewport seeded from initialViewportId
|
|
36
36
|
ClientExperienceRenderer; // Subscribes to window.matchMedia via runes
|
|
37
37
|
MissingComponent; // Default fallback for unregistered component types
|
|
38
|
-
NodesRenderer; // Exposed so you can render
|
|
38
|
+
NodesRenderer; // Exposed so you can re-render a slot's raw nodes yourself (see Slot children)
|
|
39
39
|
useActiveViewport; // Rune-backed reactive object; you'll rarely need it directly
|
|
40
40
|
```
|
|
41
41
|
|
|
@@ -46,7 +46,7 @@ getDesignValues<T>(); // Optional helper: the same resolved design record that a
|
|
|
46
46
|
toCss(design, options?); // Turns a design record into a plain style object, keeping only real CSS keys
|
|
47
47
|
getExperience(); // RenderContext: debug, metadata, viewports, activeViewport
|
|
48
48
|
getContentfulComponent(); // Raw payload for the enclosing node (or undefined)
|
|
49
|
-
getContentfulExperienceTemplate(); // Same, for
|
|
49
|
+
getContentfulExperienceTemplate(); // Same, for an enclosing coded Experience Template node
|
|
50
50
|
type ToCssOptions;
|
|
51
51
|
```
|
|
52
52
|
|
|
@@ -61,7 +61,7 @@ type ComponentConfig, ExperienceTemplateConfig,
|
|
|
61
61
|
type ContentfulComponent, ContentfulExperienceTemplate,
|
|
62
62
|
type RenderContext, ResolveToken,
|
|
63
63
|
type ExperiencePayload, ExperienceNode, ComponentNode, ExperienceTemplateNode,
|
|
64
|
-
type PortableRenderPlan, PortableRenderNode,
|
|
64
|
+
type PortableRenderPlan, PortableRenderNode, PortableRegistration,
|
|
65
65
|
type DesignPropValue, ManualDesignValue, DesignToken, ValuesByViewport,
|
|
66
66
|
type ViewportDef, ExperienceContext, ResolveContext,
|
|
67
67
|
type ResolverConfig, ResolveExperienceOptions
|
|
@@ -129,7 +129,7 @@ export const experienceConfig: Config = { components, resolveToken };
|
|
|
129
129
|
|
|
130
130
|
### Slot children
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
Every slot arrives as a prop named after the slot, holding an **array of Snippets** (`Snippet[]`), one per child — not a single wrapping Snippet. For the common "just render them" case, iterate the array with `{#each}` and `{@render}`; to wrap, reorder, or drop children individually, do it inside that loop.
|
|
133
133
|
|
|
134
134
|
```svelte
|
|
135
135
|
<!-- Section.svelte -->
|
|
@@ -156,7 +156,9 @@ A component's default slot children arrive as an **array of Snippets** (`Snippet
|
|
|
156
156
|
-->
|
|
157
157
|
```
|
|
158
158
|
|
|
159
|
-
|
|
159
|
+
`children` is not special — it is simply the conventional name for the default slot. **Every** slot in the payload becomes a same-named `Snippet[]` prop, so a component with a `header` slot just declares `header?: Snippet[]` and renders it the same way. This applies identically to coded Experience Templates: a template with a `content` slot receives a `content` prop.
|
|
160
|
+
|
|
161
|
+
If you'd rather render a slot's raw nodes yourself, they are still on the payload at `getContentfulComponent().slots` (a `Record<string, PortableRenderNode[]>`) — hand them to `NodesRenderer`.
|
|
160
162
|
|
|
161
163
|
For the full getting-started walkthrough, the merge-precedence rules, viewport handling, and design rationale, see the [root README](../../README.md) and [`AGENTS.md`](../../AGENTS.md).
|
|
162
164
|
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
import DebugExperience from './DebugExperience.svelte';
|
|
15
15
|
import MissingComponent from './MissingComponent.svelte';
|
|
16
16
|
import NodesRenderer from './NodesRenderer.svelte';
|
|
17
|
-
import WrapWithExperienceTemplate from './WrapWithExperienceTemplate.svelte';
|
|
18
17
|
import type { ClientExperienceRendererProps } from './component-props.js';
|
|
19
18
|
import { setExperience } from './context.js';
|
|
20
19
|
import type { RenderContext } from './types.js';
|
|
@@ -75,14 +74,10 @@
|
|
|
75
74
|
{#if debug}
|
|
76
75
|
<DebugExperience {experience} />
|
|
77
76
|
{/if}
|
|
78
|
-
<
|
|
79
|
-
{
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
{renderUnknown}
|
|
85
|
-
/>
|
|
86
|
-
{/snippet}
|
|
87
|
-
</WrapWithExperienceTemplate>
|
|
77
|
+
<NodesRenderer
|
|
78
|
+
nodes={experience.nodes}
|
|
79
|
+
{config}
|
|
80
|
+
experience={liveContext}
|
|
81
|
+
{renderUnknown}
|
|
82
|
+
/>
|
|
88
83
|
{/if}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClientExperienceRenderer.svelte.d.ts","sourceRoot":"","sources":["../src/ClientExperienceRenderer.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ClientExperienceRenderer.svelte.d.ts","sourceRoot":"","sources":["../src/ClientExperienceRenderer.svelte.ts"],"names":[],"mappings":"AAqFA,QAAA,MAAM,wBAAwB,kGAAwC,CAAC;AACvE,KAAK,wBAAwB,GAAG,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC5E,eAAe,wBAAwB,CAAC"}
|
|
@@ -17,10 +17,17 @@
|
|
|
17
17
|
let { experience, defaultOpen = false }: DebugExperienceProps = $props();
|
|
18
18
|
|
|
19
19
|
const nodeCount = $derived(experience.nodes.length);
|
|
20
|
+
// Coded Experience Templates are ordinary top-level nodes, so name them from
|
|
21
|
+
// the node list — a composite Experience simply has none to report.
|
|
22
|
+
const templateIds = $derived(
|
|
23
|
+
experience.nodes
|
|
24
|
+
.filter((node) => node.registration.kind === 'experienceTemplate')
|
|
25
|
+
.map((node) => node.registration.id)
|
|
26
|
+
);
|
|
20
27
|
const summary = $derived(
|
|
21
28
|
`Experience debug — ${nodeCount} top-level node${nodeCount === 1 ? '' : 's'}${
|
|
22
|
-
|
|
23
|
-
? `, experience template: ${
|
|
29
|
+
templateIds.length
|
|
30
|
+
? `, experience template${templateIds.length === 1 ? '' : 's'}: ${templateIds.join(', ')}`
|
|
24
31
|
: ''
|
|
25
32
|
}`
|
|
26
33
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DebugExperience.svelte.d.ts","sourceRoot":"","sources":["../src/DebugExperience.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"DebugExperience.svelte.d.ts","sourceRoot":"","sources":["../src/DebugExperience.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AA4DjE,QAAA,MAAM,eAAe,0DAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
|
|
@@ -2,13 +2,20 @@ import { render } from '@testing-library/svelte';
|
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
3
|
import DebugExperience from './DebugExperience.svelte';
|
|
4
4
|
const emptyPlan = { nodes: [], viewports: [], fallbackViewportIndex: 0 };
|
|
5
|
-
function node(
|
|
5
|
+
function node(id, content = {}) {
|
|
6
6
|
return {
|
|
7
|
-
registration: {
|
|
7
|
+
registration: { kind: 'component', id },
|
|
8
8
|
props: { content, design: {}, designRaw: {} },
|
|
9
9
|
slots: {},
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
|
+
function templateNode(id) {
|
|
13
|
+
return {
|
|
14
|
+
registration: { kind: 'experienceTemplate', id },
|
|
15
|
+
props: { content: {}, design: {}, designRaw: {} },
|
|
16
|
+
slots: {},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
12
19
|
describe('DebugExperience.svelte', () => {
|
|
13
20
|
it('renders a collapsible details panel with the debug marker, collapsed by default', () => {
|
|
14
21
|
const { container } = render(DebugExperience, { props: { experience: emptyPlan } });
|
|
@@ -33,19 +40,25 @@ describe('DebugExperience.svelte', () => {
|
|
|
33
40
|
const { container } = render(DebugExperience, { props: { experience: one } });
|
|
34
41
|
expect(container.innerHTML).toContain('Experience debug — 1 top-level node');
|
|
35
42
|
});
|
|
36
|
-
it('names
|
|
43
|
+
it('names experienceTemplate nodes in the summary', () => {
|
|
37
44
|
const plan = {
|
|
38
45
|
viewports: [],
|
|
39
|
-
nodes: [],
|
|
40
|
-
experienceTemplate: {
|
|
41
|
-
experienceTemplateId: 'page',
|
|
42
|
-
props: { content: {}, design: {}, designRaw: {} },
|
|
43
|
-
},
|
|
46
|
+
nodes: [templateNode('page')],
|
|
44
47
|
fallbackViewportIndex: 0,
|
|
45
48
|
};
|
|
46
49
|
const { container } = render(DebugExperience, { props: { experience: plan } });
|
|
47
50
|
expect(container.innerHTML).toContain('experience template: page');
|
|
48
51
|
});
|
|
52
|
+
it('omits the experience-template summary for a composite experience', () => {
|
|
53
|
+
const plan = {
|
|
54
|
+
viewports: [],
|
|
55
|
+
nodes: [node('button'), node('text')],
|
|
56
|
+
fallbackViewportIndex: 0,
|
|
57
|
+
};
|
|
58
|
+
const { container } = render(DebugExperience, { props: { experience: plan } });
|
|
59
|
+
expect(container.innerHTML).toContain('Experience debug — 2 top-level nodes');
|
|
60
|
+
expect(container.innerHTML).not.toContain('experience template');
|
|
61
|
+
});
|
|
49
62
|
it('dumps the plan as pretty JSON', () => {
|
|
50
63
|
const plan = {
|
|
51
64
|
viewports: [],
|
|
@@ -53,7 +66,7 @@ describe('DebugExperience.svelte', () => {
|
|
|
53
66
|
fallbackViewportIndex: 0,
|
|
54
67
|
};
|
|
55
68
|
const { container } = render(DebugExperience, { props: { experience: plan } });
|
|
56
|
-
expect(container.innerHTML).toContain('
|
|
69
|
+
expect(container.innerHTML).toContain('registration');
|
|
57
70
|
expect(container.innerHTML).toContain('button');
|
|
58
71
|
expect(container.innerHTML).toContain('Go');
|
|
59
72
|
});
|
package/dist/NodeRenderer.svelte
CHANGED
|
@@ -2,18 +2,29 @@
|
|
|
2
2
|
* Single-node renderer. Splits out of NodesRenderer so the per-node
|
|
3
3
|
* `setContext` for the contentful payload happens during *that node's*
|
|
4
4
|
* component init (Svelte's setContext can only be called at top level).
|
|
5
|
+
*
|
|
6
|
+
* A node's `registration.kind` picks which half of the customer Config owns
|
|
7
|
+
* its implementation — `experienceTemplates` for a coded Experience Template,
|
|
8
|
+
* `components` otherwise. Everything else (slot props, merge precedence,
|
|
9
|
+
* design resolution) is identical across the two kinds.
|
|
5
10
|
-->
|
|
6
11
|
<script lang="ts">
|
|
7
12
|
import type { Snippet } from 'svelte';
|
|
8
13
|
|
|
9
14
|
import type { PortableRenderNode } from '@contentful/experiences-sdk-core';
|
|
15
|
+
import { selectResolvedDesign } from '@contentful/experiences-design';
|
|
10
16
|
|
|
11
|
-
import {
|
|
12
|
-
|
|
17
|
+
import {
|
|
18
|
+
setContentfulComponent,
|
|
19
|
+
setContentfulExperienceTemplate,
|
|
20
|
+
setResolvedDesign,
|
|
21
|
+
} from './context.js';
|
|
13
22
|
import type { RenderUnknown } from './component-props.js';
|
|
14
23
|
import {
|
|
15
24
|
normalizeComponentRegistration,
|
|
25
|
+
normalizeExperienceTemplateRegistration,
|
|
16
26
|
type ContentfulComponent,
|
|
27
|
+
type ContentfulExperienceTemplate,
|
|
17
28
|
type Config,
|
|
18
29
|
type RenderContext,
|
|
19
30
|
} from './types.js';
|
|
@@ -23,23 +34,49 @@
|
|
|
23
34
|
config: Config;
|
|
24
35
|
experience: RenderContext;
|
|
25
36
|
renderUnknown: RenderUnknown;
|
|
26
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Pre-rendered slot children keyed by slot name — one zero-arg Snippet per
|
|
39
|
+
* child node. Spread onto the customer component as named props, so a slot
|
|
40
|
+
* named `content` arrives as a `content` prop. `children` is not special.
|
|
41
|
+
*/
|
|
42
|
+
slotSnippets: Record<string, Snippet[]>;
|
|
27
43
|
}
|
|
28
44
|
|
|
29
|
-
let { node, config, experience, renderUnknown,
|
|
45
|
+
let { node, config, experience, renderUnknown, slotSnippets }: NodeRendererProps = $props();
|
|
46
|
+
|
|
47
|
+
const { kind, id } = node.registration;
|
|
48
|
+
const isExperienceTemplate = kind === 'experienceTemplate';
|
|
30
49
|
|
|
31
|
-
const entry = $derived(
|
|
32
|
-
|
|
50
|
+
const entry = $derived(
|
|
51
|
+
isExperienceTemplate ? config.experienceTemplates?.[id] : config.components[id]
|
|
52
|
+
);
|
|
53
|
+
const componentConfig = $derived.by(() => {
|
|
54
|
+
if (!entry) return null;
|
|
55
|
+
return isExperienceTemplate
|
|
56
|
+
? normalizeExperienceTemplateRegistration(entry)
|
|
57
|
+
: normalizeComponentRegistration(entry);
|
|
58
|
+
});
|
|
33
59
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
60
|
+
if (isExperienceTemplate) {
|
|
61
|
+
const contentful: ContentfulExperienceTemplate = {
|
|
62
|
+
experienceTemplateId: id,
|
|
63
|
+
nodeId: node.nodeId,
|
|
64
|
+
content: node.props.content,
|
|
65
|
+
design: node.props.designRaw,
|
|
66
|
+
resolved: node.props.resolved,
|
|
67
|
+
};
|
|
68
|
+
setContentfulExperienceTemplate(contentful);
|
|
69
|
+
} else {
|
|
70
|
+
const contentful: ContentfulComponent = {
|
|
71
|
+
componentId: id,
|
|
72
|
+
nodeId: node.nodeId,
|
|
73
|
+
content: node.props.content,
|
|
74
|
+
design: node.props.designRaw,
|
|
75
|
+
resolved: node.props.resolved,
|
|
76
|
+
slots: node.slots,
|
|
77
|
+
};
|
|
78
|
+
setContentfulComponent(contentful);
|
|
79
|
+
}
|
|
43
80
|
|
|
44
81
|
const tokenResolvedDesign = $derived.by(() => {
|
|
45
82
|
const { props, unresolved } = selectResolvedDesign(
|
|
@@ -51,7 +88,7 @@
|
|
|
51
88
|
);
|
|
52
89
|
if (unresolved.length && typeof console !== 'undefined') {
|
|
53
90
|
console.warn(
|
|
54
|
-
`[@contentful/experiences-svelte] resolveToken returned undefined for token id(s) on "${
|
|
91
|
+
`[@contentful/experiences-svelte] resolveToken returned undefined for token id(s) on ${kind} "${id}": ${unresolved.join(', ')}. getDesignValues() will omit those keys.`
|
|
55
92
|
);
|
|
56
93
|
}
|
|
57
94
|
return props;
|
|
@@ -59,7 +96,7 @@
|
|
|
59
96
|
|
|
60
97
|
setResolvedDesign(() => tokenResolvedDesign);
|
|
61
98
|
|
|
62
|
-
// Merge precedence (last wins): defaults < design < content < resolveData <
|
|
99
|
+
// Merge precedence (last wins): defaults < design < content < resolveData < slots.
|
|
63
100
|
const composed = $derived.by(() => {
|
|
64
101
|
if (!componentConfig) return null;
|
|
65
102
|
return {
|
|
@@ -67,18 +104,35 @@
|
|
|
67
104
|
...tokenResolvedDesign,
|
|
68
105
|
...node.props.content,
|
|
69
106
|
...node.props.resolved,
|
|
70
|
-
|
|
107
|
+
...slotSnippets,
|
|
71
108
|
};
|
|
72
109
|
});
|
|
110
|
+
|
|
111
|
+
// An unregistered Experience Template would blank the page if we swapped it
|
|
112
|
+
// for the missing-component box, so warn and render its slot children
|
|
113
|
+
// unwrapped — the content survives, the diagnostic names what's missing.
|
|
114
|
+
const orphanedSnippets = $derived.by(() => {
|
|
115
|
+
if (componentConfig || !isExperienceTemplate) return null;
|
|
116
|
+
if (typeof console !== 'undefined') {
|
|
117
|
+
console.warn(
|
|
118
|
+
`[@contentful/experiences-svelte] No experience template registered for id "${id}". Rendering its slot children without the experience template wrapper.`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return Object.values(slotSnippets).flat();
|
|
122
|
+
});
|
|
73
123
|
</script>
|
|
74
124
|
|
|
75
125
|
{#if componentConfig && composed}
|
|
76
126
|
{@const Cmp = componentConfig.component}
|
|
77
127
|
<Cmp {...composed} />
|
|
128
|
+
{:else if orphanedSnippets}
|
|
129
|
+
{#each orphanedSnippets as childSnippet, index (index)}
|
|
130
|
+
{@render childSnippet()}
|
|
131
|
+
{/each}
|
|
78
132
|
{:else}
|
|
79
133
|
{@const Unknown = renderUnknown}
|
|
80
134
|
<Unknown
|
|
81
|
-
componentId={
|
|
135
|
+
componentId={id}
|
|
82
136
|
nodeId={node.nodeId}
|
|
83
137
|
/>
|
|
84
138
|
{/if}
|
|
@@ -7,7 +7,12 @@ interface NodeRendererProps {
|
|
|
7
7
|
config: Config;
|
|
8
8
|
experience: RenderContext;
|
|
9
9
|
renderUnknown: RenderUnknown;
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Pre-rendered slot children keyed by slot name — one zero-arg Snippet per
|
|
12
|
+
* child node. Spread onto the customer component as named props, so a slot
|
|
13
|
+
* named `content` arrives as a `content` prop. `children` is not special.
|
|
14
|
+
*/
|
|
15
|
+
slotSnippets: Record<string, Snippet[]>;
|
|
11
16
|
}
|
|
12
17
|
declare const NodeRenderer: import("svelte").Component<NodeRendererProps, {}, "">;
|
|
13
18
|
type NodeRenderer = ReturnType<typeof NodeRenderer>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeRenderer.svelte.d.ts","sourceRoot":"","sources":["../src/NodeRenderer.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"NodeRenderer.svelte.d.ts","sourceRoot":"","sources":["../src/NodeRenderer.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAQ3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAKH,KAAK,MAAM,EACX,KAAK,aAAa,EACnB,MAAM,YAAY,CAAC;AAGpB,UAAU,iBAAiB;IACzB,IAAI,EAAE,kBAAkB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,aAAa,CAAC;IAC1B,aAAa,EAAE,aAAa,CAAC;IAC7B;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;CACzC;AA2GH,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
* Recursive renderer over PortableRenderNodes. Customer components receive
|
|
3
|
-
* the merged content / design / resolveData prop bag plus
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* the merged content / design / resolveData prop bag plus one prop per slot,
|
|
4
|
+
* each an ARRAY of Snippets — one zero-arg Snippet per pre-rendered child
|
|
5
|
+
* node. A slot named `content` arrives as `content?: Snippet[]`; the
|
|
6
|
+
* conventional default slot arrives as `children?: Snippet[]`. A component
|
|
6
7
|
* renders them all with an each-block + @render, or maps/filters/wraps the
|
|
7
|
-
* children individually. The
|
|
8
|
+
* children individually. The arrays mirror the React adapter's ReactNode[]
|
|
8
9
|
* slot contract; see the README for the consuming-a-slot example.
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* This applies to every node kind — a coded Experience Template declaring a
|
|
12
|
+
* `content` slot gets a `content` prop exactly as a component would. The raw
|
|
13
|
+
* per-slot payload nodes are also reachable via
|
|
14
|
+
* `getContentfulComponent().slots` for callers that want to render them
|
|
15
|
+
* themselves with the exported NodesRenderer.
|
|
15
16
|
*
|
|
16
17
|
* The Experience runtime context and the raw Contentful payload are read
|
|
17
18
|
* via `getExperience` / `getContentfulComponent`.
|
|
@@ -38,20 +39,42 @@
|
|
|
38
39
|
let { nodes, config, experience, renderUnknown }: NodesRendererProps = $props();
|
|
39
40
|
|
|
40
41
|
// Curry the parameterized `renderNode` snippet into a zero-arg Snippet bound
|
|
41
|
-
// to one node
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
42
|
+
// to one node: forward the anchor (the renderer, under SSR) and supply the
|
|
43
|
+
// node through a getter. This is the one spot that leans on Svelte's snippet
|
|
44
|
+
// calling convention; pinned by the slot-array tests in
|
|
45
|
+
// server-renderer.test.ts and nodes-renderer.ssr.test.ts.
|
|
45
46
|
function toChildSnippet(node: PortableRenderNode): Snippet {
|
|
46
47
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- programmatic snippet construction; see note above.
|
|
47
48
|
return ((anchor: unknown) => (renderNode as any)(anchor, () => node)) as Snippet;
|
|
48
49
|
}
|
|
50
|
+
|
|
51
|
+
// Svelte's two compiler outputs disagree on how snippet arguments arrive: the
|
|
52
|
+
// client build passes each one as a getter, the server build passes it by
|
|
53
|
+
// value. Compiler-generated `{@render}` call sites match whichever build they
|
|
54
|
+
// are in, but `toChildSnippet` writes its call by hand, so the snippet body
|
|
55
|
+
// accepts either shape and normalizes here.
|
|
56
|
+
type NodeArg = PortableRenderNode | (() => PortableRenderNode);
|
|
57
|
+
function unwrapNode(node: NodeArg): PortableRenderNode {
|
|
58
|
+
return typeof node === 'function' ? node() : node;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Build one Snippet[] per slot so every slot becomes a named prop. Snippets
|
|
62
|
+
// themselves are compile-time entities, but the *prop bag* is plain runtime
|
|
63
|
+
// data — so an unbounded set of payload-driven slot names is fine as long as
|
|
64
|
+
// each snippet comes from `toChildSnippet`.
|
|
65
|
+
function toSlotSnippets(node: PortableRenderNode): Record<string, Snippet[]> {
|
|
66
|
+
const slotSnippets: Record<string, Snippet[]> = {};
|
|
67
|
+
for (const [slotName, children] of Object.entries(node.slots)) {
|
|
68
|
+
slotSnippets[slotName] = children.map(toChildSnippet);
|
|
69
|
+
}
|
|
70
|
+
return slotSnippets;
|
|
71
|
+
}
|
|
49
72
|
</script>
|
|
50
73
|
|
|
51
|
-
{#snippet renderNode(
|
|
52
|
-
{@const
|
|
53
|
-
{@const
|
|
54
|
-
<NodeRenderer {node} {config} {experience} {renderUnknown} {
|
|
74
|
+
{#snippet renderNode(nodeArg: NodeArg)}
|
|
75
|
+
{@const node = unwrapNode(nodeArg)}
|
|
76
|
+
{@const slotSnippets = toSlotSnippets(node)}
|
|
77
|
+
<NodeRenderer {node} {config} {experience} {renderUnknown} {slotSnippets} />
|
|
55
78
|
{/snippet}
|
|
56
79
|
|
|
57
80
|
{#each nodes as node, index (node.nodeId ?? index)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodesRenderer.svelte.d.ts","sourceRoot":"","sources":["../src/NodesRenderer.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAG3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGtD,UAAU,kBAAkB;IAC1B,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,aAAa,CAAC;IAC1B,aAAa,EAAE,aAAa,CAAC;CAC9B;
|
|
1
|
+
{"version":3,"file":"NodesRenderer.svelte.d.ts","sourceRoot":"","sources":["../src/NodesRenderer.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAG3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGtD,UAAU,kBAAkB;IAC1B,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,aAAa,CAAC;IAC1B,aAAa,EAAE,aAAa,CAAC;CAC9B;AA4DH,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
import DebugExperience from './DebugExperience.svelte';
|
|
15
15
|
import MissingComponent from './MissingComponent.svelte';
|
|
16
16
|
import NodesRenderer from './NodesRenderer.svelte';
|
|
17
|
-
import WrapWithExperienceTemplate from './WrapWithExperienceTemplate.svelte';
|
|
18
17
|
import type { ServerExperienceRendererProps } from './component-props.js';
|
|
19
18
|
import { setExperience } from './context.js';
|
|
20
19
|
import type { RenderContext } from './types.js';
|
|
@@ -63,14 +62,10 @@
|
|
|
63
62
|
{#if debug}
|
|
64
63
|
<DebugExperience {experience} />
|
|
65
64
|
{/if}
|
|
66
|
-
<
|
|
67
|
-
{
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
{renderUnknown}
|
|
73
|
-
/>
|
|
74
|
-
{/snippet}
|
|
75
|
-
</WrapWithExperienceTemplate>
|
|
65
|
+
<NodesRenderer
|
|
66
|
+
nodes={experience.nodes}
|
|
67
|
+
{config}
|
|
68
|
+
experience={renderContext}
|
|
69
|
+
{renderUnknown}
|
|
70
|
+
/>
|
|
76
71
|
{/if}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServerExperienceRenderer.svelte.d.ts","sourceRoot":"","sources":["../src/ServerExperienceRenderer.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ServerExperienceRenderer.svelte.d.ts","sourceRoot":"","sources":["../src/ServerExperienceRenderer.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,sBAAsB,CAAC;AAiE1E,QAAA,MAAM,wBAAwB,mEAAwC,CAAC;AACvE,KAAK,wBAAwB,GAAG,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC5E,eAAe,wBAAwB,CAAC"}
|
package/dist/design-utils.d.ts
CHANGED
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
import type { DesignPropValue, ResolveToken, ViewportDef } from '@contentful/experiences-sdk-core';
|
|
2
|
-
export declare function selectResolvedDesign(props: {
|
|
3
|
-
design: Record<string, unknown>;
|
|
4
|
-
designRaw: Record<string, DesignPropValue>;
|
|
5
|
-
}, viewports: ViewportDef[], activeViewportIndex: number, fallbackViewportIndex: number, resolveToken: ResolveToken | undefined): {
|
|
6
|
-
props: Record<string, unknown>;
|
|
7
|
-
unresolved: string[];
|
|
8
|
-
};
|
|
9
1
|
export interface ToCssOptions {
|
|
10
2
|
/** Keys to skip, matched against the original record key. */
|
|
11
3
|
exclude?: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"design-utils.d.ts","sourceRoot":"","sources":["../src/design-utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"design-utils.d.ts","sourceRoot":"","sources":["../src/design-utils.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,KAAK,CACnB,MAAM,EAAE,MAAM,EACd,EAAE,OAAO,EAAE,OAAO,EAAE,GAAE,YAAiB,GACtC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAYjC"}
|
package/dist/design-utils.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
// Use the server-resolved `props.design` when the active viewport matches the
|
|
3
|
-
// fallback; otherwise recompute the cascade from raw `props.designRaw`.
|
|
4
|
-
export function selectResolvedDesign(props, viewports, activeViewportIndex, fallbackViewportIndex, resolveToken) {
|
|
5
|
-
if (activeViewportIndex === fallbackViewportIndex) {
|
|
6
|
-
return { props: props.design, unresolved: [] };
|
|
7
|
-
}
|
|
8
|
-
const resolvedDesign = resolveDesignProperties(props.designRaw, viewports, activeViewportIndex);
|
|
9
|
-
return applyTokenResolver(resolvedDesign, resolveToken);
|
|
10
|
-
}
|
|
1
|
+
import { isCssProperty, toCssKey } from '@contentful/experiences-design';
|
|
11
2
|
/**
|
|
12
3
|
* Convert a design record into a plain style object, keeping only keys that
|
|
13
4
|
* normalize to a known CSS property. Non-CSS keys (`variant`, `as`, …) and
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export { defineComponent, defineExperienceTemplate, normalizeComponentRegistrati
|
|
|
15
15
|
export type { ComponentConfig, Components, Config, ContentfulComponent, ContentfulExperienceTemplate, ExperienceTemplateConfig, ExperienceTemplateRegistration, ExperienceTemplates, Registration, RenderContext, ResolveToken, } from './types.js';
|
|
16
16
|
export { resolveExperience } from '@contentful/experiences-sdk-core';
|
|
17
17
|
export type { ResolverConfig, ResolveExperienceOptions } from '@contentful/experiences-sdk-core';
|
|
18
|
-
export type { ComponentNode, ComponentRef, DesignPropValue, DesignToken, ExperienceContext, ExperienceNode, ExperiencePayload, ExperienceSys, ExperienceTemplateNode, ExperienceTemplateRef, ManualDesignValue,
|
|
18
|
+
export type { ComponentNode, ComponentRef, DesignPropValue, DesignToken, ExperienceContext, ExperienceNode, ExperiencePayload, ExperienceSys, ExperienceTemplateNode, ExperienceTemplateRef, ManualDesignValue, PortableRegistration, PortableRenderNode, PortableRenderPlan, ResolveContext, ValuesByViewport, ViewportDef, } from '@contentful/experiences-sdk-core';
|
|
19
19
|
export { CSS_PROPERTIES, getValueForViewport, getViewportIndex, isCssProperty, resolveDesignProperties, toCssKey, toCssMediaQuery, } from '@contentful/experiences-design';
|
|
20
20
|
export { ContentfulViewDelivery, ContentfulViewDeliveryClient, DELIVERY_HOST, NotFoundError, PREVIEW_HOST, createClient, fetchExperience, } from '@contentful/experiences-client';
|
|
21
21
|
export type { ClientOptions, CreateClientOptions, ExperienceOptions, ResolveOptions, } from '@contentful/experiences-client';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAKtE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAElE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,YAAY,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAG/E,OAAO,EACL,aAAa,EACb,sBAAsB,EACtB,+BAA+B,GAChC,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAItD,YAAY,EACV,6BAA6B,EAC7B,6BAA6B,IAAI,uBAAuB,EACxD,oBAAoB,EACpB,qBAAqB,EACrB,aAAa,EACb,6BAA6B,GAC9B,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,8BAA8B,EAC9B,uCAAuC,GACxC,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,eAAe,EACf,UAAU,EACV,MAAM,EACN,mBAAmB,EACnB,4BAA4B,EAC5B,wBAAwB,EACxB,8BAA8B,EAC9B,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,YAAY,GACb,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,YAAY,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAGjG,YAAY,EACV,aAAa,EACb,YAAY,EACZ,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,WAAW,GACZ,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,gBAAgB,EAChB,aAAa,EACb,uBAAuB,EACvB,QAAQ,EACR,eAAe,GAChB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EACL,sBAAsB,EACtB,4BAA4B,EAC5B,aAAa,EACb,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,GACf,MAAM,gCAAgC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,8 @@ export { default as ExperienceRenderer } from './ClientExperienceRenderer.svelte
|
|
|
17
17
|
export { default as ServerExperienceRenderer } from './ServerExperienceRenderer.svelte';
|
|
18
18
|
export { default as MissingComponent } from './MissingComponent.svelte';
|
|
19
19
|
export { default as DebugExperience } from './DebugExperience.svelte';
|
|
20
|
-
//
|
|
20
|
+
// Every slot already arrives as a same-named `Snippet[]` prop; this is exposed so
|
|
21
|
+
// advanced customers can render a slot's raw nodes themselves instead:
|
|
21
22
|
// e.g. `<NodesRenderer nodes={contentful.slots.header as PortableRenderNode[]} ... />`.
|
|
22
23
|
export { default as NodesRenderer } from './NodesRenderer.svelte';
|
|
23
24
|
export { useActiveViewport } from './use-active-viewport.svelte.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodes-renderer.ssr.test.d.ts","sourceRoot":"","sources":["../src/nodes-renderer.ssr.test.ts"],"names":[],"mappings":""}
|