cms-renderer 0.2.5 → 0.2.9
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/dist/lib/block-renderer.d.ts +7 -2
- package/dist/lib/block-renderer.js +140 -89
- package/dist/lib/block-renderer.js.map +1 -1
- package/dist/lib/block-toolbar.js +9 -61
- package/dist/lib/block-toolbar.js.map +1 -1
- package/dist/lib/cms-api.d.ts +7 -5
- package/dist/lib/cms-api.js +9 -5
- package/dist/lib/cms-api.js.map +1 -1
- package/dist/lib/renderer.d.ts +4 -7
- package/dist/lib/renderer.js +167 -96
- package/dist/lib/renderer.js.map +1 -1
- package/dist/lib/schema.d.ts +6 -3
- package/dist/lib/schema.js +34 -8
- package/dist/lib/schema.js.map +1 -1
- package/dist/lib/types.d.ts +21 -1
- package/package.json +2 -5
- package/dist/lib/data-utils.d.ts +0 -218
- package/dist/lib/data-utils.js +0 -250
- package/dist/lib/data-utils.js.map +0 -1
package/dist/lib/renderer.d.ts
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { Metadata } from 'next';
|
|
3
|
+
import { CmsConfig } from './cms-api.js';
|
|
3
4
|
import { BlockComponentRegistry } from './types.js';
|
|
5
|
+
import '@repo/cms-schema/trpc';
|
|
6
|
+
import '@trpc/client';
|
|
4
7
|
import '@repo/cms-schema/blocks';
|
|
5
8
|
|
|
6
|
-
type PageProps = {
|
|
9
|
+
type PageProps = CmsConfig & {
|
|
7
10
|
params: Promise<{
|
|
8
11
|
slug: string[];
|
|
9
12
|
}>;
|
|
10
13
|
searchParams?: Promise<{
|
|
11
14
|
[key: string]: string | string[] | undefined;
|
|
12
15
|
}>;
|
|
13
|
-
/** CMS API base URL (e.g., 'http://localhost:3000') */
|
|
14
|
-
cmsUrl: string;
|
|
15
16
|
registry?: Partial<BlockComponentRegistry>;
|
|
16
|
-
/** API key for CMS API authentication */
|
|
17
|
-
apiKey?: string;
|
|
18
|
-
/** Website ID (required if not using env variables) */
|
|
19
|
-
websiteId?: string;
|
|
20
17
|
};
|
|
21
18
|
/**
|
|
22
19
|
* Force dynamic rendering to ensure routes are always fresh.
|
package/dist/lib/renderer.js
CHANGED
|
@@ -341,65 +341,66 @@ function renderToWalkableTree(node, keyPrefix = "") {
|
|
|
341
341
|
}
|
|
342
342
|
return node;
|
|
343
343
|
}
|
|
344
|
-
function BlockRenderer({
|
|
345
|
-
|
|
344
|
+
function BlockRenderer({
|
|
345
|
+
block,
|
|
346
|
+
registry,
|
|
347
|
+
disableEditable,
|
|
348
|
+
routeParams
|
|
349
|
+
}) {
|
|
350
|
+
const Component = registry[block.type];
|
|
346
351
|
if (!Component) {
|
|
347
352
|
if (process.env.NODE_ENV === "development") {
|
|
348
353
|
console.warn(`[BlockRenderer] Unknown block type: ${block.type}`);
|
|
349
354
|
}
|
|
350
355
|
return null;
|
|
351
356
|
}
|
|
352
|
-
const component = /* @__PURE__ */ jsx(Component, { content: block.content });
|
|
357
|
+
const component = /* @__PURE__ */ jsx(Component, { content: block.content, routeParams });
|
|
353
358
|
if (disableEditable) {
|
|
354
359
|
return component;
|
|
355
360
|
}
|
|
356
|
-
const renderedTree = renderToWalkableTree(component);
|
|
357
361
|
const contentValueMap = extractContentValues(block.content);
|
|
358
|
-
|
|
359
|
-
let
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
return React.cloneElement(
|
|
388
|
-
element,
|
|
389
|
-
{
|
|
390
|
-
"data-cms-block": true,
|
|
391
|
-
"data-block-id": block.id,
|
|
392
|
-
"data-block-type": block.type
|
|
393
|
-
},
|
|
394
|
-
existingChildren,
|
|
395
|
-
/* @__PURE__ */ jsx(BlockToolbar, { blockId: block.id }, "cms-toolbar")
|
|
396
|
-
);
|
|
397
|
-
}
|
|
398
|
-
return element;
|
|
362
|
+
let renderedComponent = component;
|
|
363
|
+
let needsClientSideSpans = true;
|
|
364
|
+
try {
|
|
365
|
+
const renderedTree = renderToWalkableTree(component);
|
|
366
|
+
const isWalkable = React.isValidElement(renderedTree) && typeof renderedTree.type === "string";
|
|
367
|
+
if (isWalkable) {
|
|
368
|
+
const usedPaths = /* @__PURE__ */ new Set();
|
|
369
|
+
renderedComponent = walkReactNode(renderedTree, {
|
|
370
|
+
onText: ({ value, key, path }) => {
|
|
371
|
+
const matches = contentValueMap.get(value);
|
|
372
|
+
if (!matches || matches.length === 0) return value;
|
|
373
|
+
const match = matches.find((m) => !usedPaths.has(m.contentPath)) ?? matches[0];
|
|
374
|
+
if (!match) return value;
|
|
375
|
+
usedPaths.add(match.contentPath);
|
|
376
|
+
const spanKey = key ?? `${block.id}-${match.contentPath}-${path.join("-")}`;
|
|
377
|
+
return /* @__PURE__ */ jsx(
|
|
378
|
+
"span",
|
|
379
|
+
{
|
|
380
|
+
"data-cms-editable": true,
|
|
381
|
+
"data-block-id": block.id,
|
|
382
|
+
"data-block-type": block.type,
|
|
383
|
+
"data-content-path": match.contentPath,
|
|
384
|
+
children: value
|
|
385
|
+
},
|
|
386
|
+
spanKey
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
needsClientSideSpans = false;
|
|
399
391
|
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
|
|
392
|
+
} catch {
|
|
393
|
+
}
|
|
394
|
+
const contentEntries = needsClientSideSpans ? Array.from(contentValueMap.entries()).map(([value, matches]) => ({ v: value, p: matches[0]?.contentPath })).filter((e) => !!e.p) : [];
|
|
395
|
+
return /* @__PURE__ */ jsxs(
|
|
396
|
+
"div",
|
|
397
|
+
{
|
|
398
|
+
"data-cms-block": true,
|
|
399
|
+
"data-block-id": block.id,
|
|
400
|
+
"data-block-type": block.type,
|
|
401
|
+
style: { position: "relative" },
|
|
402
|
+
children: [
|
|
403
|
+
/* @__PURE__ */ jsx("style", { children: `
|
|
403
404
|
[data-cms-block] {
|
|
404
405
|
position: relative;
|
|
405
406
|
}
|
|
@@ -469,73 +470,128 @@ function BlockRenderer({ block, registry, disableEditable }) {
|
|
|
469
470
|
height: 16px;
|
|
470
471
|
}
|
|
471
472
|
` }),
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
473
|
+
/* @__PURE__ */ jsx(
|
|
474
|
+
"script",
|
|
475
|
+
{
|
|
476
|
+
dangerouslySetInnerHTML: {
|
|
477
|
+
__html: `
|
|
477
478
|
(function() {
|
|
478
|
-
if (window.__cmsEditableInitialized)
|
|
479
|
-
|
|
479
|
+
if (!window.__cmsEditableInitialized) {
|
|
480
|
+
window.__cmsEditableInitialized = true;
|
|
480
481
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
if (e.target.closest('.cms-block-toolbar')) {
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
482
|
+
document.addEventListener('click', function(e) {
|
|
483
|
+
if (e.target.closest('.cms-block-toolbar')) return;
|
|
486
484
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
485
|
+
var editableTarget = e.target.closest('[data-cms-editable]');
|
|
486
|
+
if (editableTarget) {
|
|
487
|
+
var message = {
|
|
488
|
+
type: 'cms-editable-click',
|
|
489
|
+
blockId: editableTarget.getAttribute('data-block-id'),
|
|
490
|
+
blockType: editableTarget.getAttribute('data-block-type'),
|
|
491
|
+
contentPath: editableTarget.getAttribute('data-content-path')
|
|
492
|
+
};
|
|
493
|
+
if (window.parent && window.parent !== window) {
|
|
494
|
+
window.parent.postMessage(message, '*');
|
|
495
|
+
}
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
496
498
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
+
var blockTarget = e.target.closest('[data-cms-block]');
|
|
500
|
+
if (blockTarget) {
|
|
501
|
+
var message = {
|
|
502
|
+
type: 'cms-editable-click',
|
|
503
|
+
blockId: blockTarget.getAttribute('data-block-id'),
|
|
504
|
+
blockType: blockTarget.getAttribute('data-block-type'),
|
|
505
|
+
contentPath: null
|
|
506
|
+
};
|
|
507
|
+
if (window.parent && window.parent !== window) {
|
|
508
|
+
window.parent.postMessage(message, '*');
|
|
509
|
+
}
|
|
499
510
|
}
|
|
500
|
-
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
})();
|
|
514
|
+
${contentEntries.length > 0 ? `
|
|
515
|
+
(function() {
|
|
516
|
+
var blockId = ${JSON.stringify(block.id)};
|
|
517
|
+
var blockType = ${JSON.stringify(block.type)};
|
|
518
|
+
var entries = ${JSON.stringify(contentEntries)};
|
|
519
|
+
|
|
520
|
+
function injectSpans() {
|
|
521
|
+
var block = document.querySelector('[data-block-id="' + blockId + '"]');
|
|
522
|
+
if (!block || block.getAttribute('data-cms-spans-injected')) return;
|
|
523
|
+
block.setAttribute('data-cms-spans-injected', 'true');
|
|
524
|
+
|
|
525
|
+
var used = {};
|
|
526
|
+
var walker = document.createTreeWalker(block, NodeFilter.SHOW_TEXT, null);
|
|
527
|
+
var textNodes = [];
|
|
528
|
+
var n;
|
|
529
|
+
while (n = walker.nextNode()) {
|
|
530
|
+
if (n.parentNode && n.parentNode.closest && n.parentNode.closest('.cms-block-toolbar')) continue;
|
|
531
|
+
if (n.nodeValue && n.nodeValue.trim()) textNodes.push(n);
|
|
501
532
|
}
|
|
502
533
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
type: 'cms-editable-click',
|
|
508
|
-
blockId: blockTarget.getAttribute('data-block-id'),
|
|
509
|
-
blockType: blockTarget.getAttribute('data-block-type'),
|
|
510
|
-
contentPath: null
|
|
511
|
-
};
|
|
534
|
+
for (var i = 0; i < textNodes.length; i++) {
|
|
535
|
+
var node = textNodes[i];
|
|
536
|
+
var text = node.nodeValue;
|
|
537
|
+
if (!text) continue;
|
|
512
538
|
|
|
513
|
-
|
|
514
|
-
|
|
539
|
+
for (var j = 0; j < entries.length; j++) {
|
|
540
|
+
var e = entries[j];
|
|
541
|
+
if (used[e.p]) continue;
|
|
542
|
+
if (text.indexOf(e.v) !== -1 && text.trim() === e.v.trim()) {
|
|
543
|
+
used[e.p] = true;
|
|
544
|
+
var span = document.createElement('span');
|
|
545
|
+
span.setAttribute('data-cms-editable', '');
|
|
546
|
+
span.setAttribute('data-block-id', blockId);
|
|
547
|
+
span.setAttribute('data-block-type', blockType);
|
|
548
|
+
span.setAttribute('data-content-path', e.p);
|
|
549
|
+
node.parentNode.insertBefore(span, node);
|
|
550
|
+
span.appendChild(node);
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
515
553
|
}
|
|
516
554
|
}
|
|
517
|
-
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (document.readyState === 'complete') {
|
|
558
|
+
requestAnimationFrame(injectSpans);
|
|
559
|
+
} else {
|
|
560
|
+
window.addEventListener('load', function() {
|
|
561
|
+
requestAnimationFrame(injectSpans);
|
|
562
|
+
});
|
|
563
|
+
}
|
|
518
564
|
})();
|
|
565
|
+
` : ""}
|
|
519
566
|
`
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
),
|
|
570
|
+
renderedComponent,
|
|
571
|
+
/* @__PURE__ */ jsx(BlockToolbar, { blockId: block.id }, "cms-toolbar")
|
|
572
|
+
]
|
|
573
|
+
},
|
|
574
|
+
block.id
|
|
575
|
+
);
|
|
525
576
|
}
|
|
526
577
|
|
|
527
578
|
// lib/cms-api.ts
|
|
528
579
|
import { createTRPCClient, httpBatchLink } from "@trpc/client";
|
|
529
580
|
import superjson from "superjson";
|
|
530
581
|
function getCmsApiUrl(cmsUrl) {
|
|
531
|
-
return
|
|
582
|
+
return new URL("/api/trpc", cmsUrl).toString();
|
|
532
583
|
}
|
|
533
|
-
function createFetchWithApiKey(apiKey) {
|
|
584
|
+
function createFetchWithApiKey(apiKey, websiteId) {
|
|
534
585
|
return async (url, options) => {
|
|
535
586
|
let finalUrl = url;
|
|
587
|
+
const urlObj = new URL(url.toString());
|
|
536
588
|
if (apiKey) {
|
|
537
|
-
const urlObj = new URL(url.toString());
|
|
538
589
|
urlObj.searchParams.set("api_key", apiKey);
|
|
590
|
+
}
|
|
591
|
+
if (websiteId) {
|
|
592
|
+
urlObj.searchParams.set("website_id", websiteId);
|
|
593
|
+
}
|
|
594
|
+
if (apiKey || websiteId) {
|
|
539
595
|
finalUrl = urlObj.toString();
|
|
540
596
|
}
|
|
541
597
|
const response = await fetch(finalUrl, options);
|
|
@@ -544,13 +600,12 @@ function createFetchWithApiKey(apiKey) {
|
|
|
544
600
|
}
|
|
545
601
|
function createCmsClient(options) {
|
|
546
602
|
const url = getCmsApiUrl(options.cmsUrl);
|
|
547
|
-
console.log("[CMS API] Creating client with URL:", url);
|
|
548
603
|
return createTRPCClient({
|
|
549
604
|
links: [
|
|
550
605
|
httpBatchLink({
|
|
551
606
|
url,
|
|
552
607
|
transformer: superjson,
|
|
553
|
-
fetch: createFetchWithApiKey(options.apiKey)
|
|
608
|
+
fetch: createFetchWithApiKey(options.apiKey, options.websiteId)
|
|
554
609
|
})
|
|
555
610
|
]
|
|
556
611
|
});
|
|
@@ -606,7 +661,7 @@ async function ParametricRoutePage({
|
|
|
606
661
|
const path = normalizePath(rawPath);
|
|
607
662
|
const client = getCmsClient({ apiKey, cmsUrl });
|
|
608
663
|
try {
|
|
609
|
-
const { route } = await client.route.getByPath.query({ websiteId, path });
|
|
664
|
+
const { route, resolvedParams } = await client.route.getByPath.query({ websiteId, path });
|
|
610
665
|
if (route.state !== "Live") {
|
|
611
666
|
console.error(`Route found but not Live. Path: ${path}, State: ${route.state}`);
|
|
612
667
|
notFound();
|
|
@@ -663,12 +718,28 @@ async function ParametricRoutePage({
|
|
|
663
718
|
content
|
|
664
719
|
});
|
|
665
720
|
}
|
|
721
|
+
const routeParams = resolvedParams ? Object.fromEntries(
|
|
722
|
+
Object.entries(resolvedParams).map(([key, param]) => [
|
|
723
|
+
key,
|
|
724
|
+
{
|
|
725
|
+
value: param.value,
|
|
726
|
+
schemaName: param.schemaName,
|
|
727
|
+
document: {
|
|
728
|
+
id: param.document.id,
|
|
729
|
+
title: param.document.title,
|
|
730
|
+
content: param.document.content,
|
|
731
|
+
schema_name: param.document.schema_name
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
])
|
|
735
|
+
) : void 0;
|
|
666
736
|
return /* @__PURE__ */ jsx2("main", { children: blocks.map((block) => /* @__PURE__ */ jsx2(
|
|
667
737
|
BlockRenderer,
|
|
668
738
|
{
|
|
669
739
|
block,
|
|
670
740
|
registry: registry ?? {},
|
|
671
|
-
disableEditable: !editMode
|
|
741
|
+
disableEditable: !editMode,
|
|
742
|
+
routeParams
|
|
672
743
|
},
|
|
673
744
|
block.id
|
|
674
745
|
)) });
|