fix(products): extract lexical AST and JSON-LD data correctly from productTabs block
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 10s
Build & Deploy / 🧪 QA (push) Failing after 52s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 10s
Build & Deploy / 🧪 QA (push) Failing after 52s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
This commit is contained in:
@@ -17,6 +17,7 @@ import Link from 'next/link';
|
||||
import { notFound, redirect } from 'next/navigation';
|
||||
import ProductEngagementTracker from '@/components/analytics/ProductEngagementTracker';
|
||||
import MDXContent from '@/components/MDXContent';
|
||||
import { lexicalToMarkdown } from '@/lib/mdx-utils';
|
||||
|
||||
interface ProductPageProps {
|
||||
params: Promise<{
|
||||
@@ -266,18 +267,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
let technicalItems = [];
|
||||
try {
|
||||
const tabMatch =
|
||||
typeof product.content === 'string' &&
|
||||
product.content.match(/<Block type="productTabs" data={({.*?})}\s*\/>/s);
|
||||
if (tabMatch && tabMatch[1]) {
|
||||
const data = JSON.parse(tabMatch[1]);
|
||||
technicalItems = data.technicalItems || [];
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore JSON parse errors for AST
|
||||
}
|
||||
let technicalItems: any[] = [];
|
||||
|
||||
const datasheetPath = getDatasheetPath(productSlug, locale);
|
||||
const isFallback = (product.frontmatter as any).isFallback;
|
||||
@@ -296,8 +286,24 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
if (typeof product.content === 'string') {
|
||||
const tabBlockMatch = product.content.match(/<Block type="productTabs".*?\/>/s);
|
||||
if (tabBlockMatch) {
|
||||
descriptionContent = product.content.replace(tabBlockMatch[0], '');
|
||||
technicalContent = tabBlockMatch[0];
|
||||
descriptionContent = product.content.replace(tabBlockMatch[0], '');
|
||||
|
||||
const dataMatch =
|
||||
technicalContent.match(/data="({.*?})"/s) || technicalContent.match(/data={({.*?})}/s);
|
||||
if (dataMatch && dataMatch[1]) {
|
||||
try {
|
||||
const rawJsonStr = dataMatch[1].replace(/"/g, '"');
|
||||
const data = JSON.parse(rawJsonStr);
|
||||
technicalItems = data.technicalItems || [];
|
||||
|
||||
if (data.content?.root && descriptionContent.trim() === '') {
|
||||
descriptionContent = lexicalToMarkdown(data.content.root);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to parse productTabs data for page:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,3 +66,81 @@ export function fixMdxDataProps(content: string): string {
|
||||
|
||||
return fixedContent;
|
||||
}
|
||||
|
||||
export function lexicalToMarkdown(node: any, depth = 0): string {
|
||||
if (!node) return '';
|
||||
|
||||
if (typeof node === 'string') return node;
|
||||
|
||||
if (node.type === 'text') {
|
||||
let text = node.text || '';
|
||||
if (node.format === 1) text = `**${text}**`; // Bold
|
||||
if (node.format === 2) text = `*${text}*`; // Italic
|
||||
return text;
|
||||
}
|
||||
|
||||
if (node.type === 'heading') {
|
||||
const level = parseInt((node.tag || 'h1').replace('h', '')) || 1;
|
||||
const prefix = '#'.repeat(level);
|
||||
const text = (node.children || []).map((c: any) => lexicalToMarkdown(c, depth)).join('');
|
||||
return `${prefix} ${text}\n\n`;
|
||||
}
|
||||
|
||||
if (node.type === 'paragraph') {
|
||||
const text = (node.children || []).map((c: any) => lexicalToMarkdown(c, depth)).join('');
|
||||
return text.trim() ? `${text}\n\n` : '\n';
|
||||
}
|
||||
|
||||
if (node.type === 'list') {
|
||||
const isOrdered = node.tag === 'ol';
|
||||
const items = (node.children || [])
|
||||
.map((c: any, index: number) => {
|
||||
const prefix = isOrdered ? `${index + 1}.` : '-';
|
||||
return `${prefix} ${lexicalToMarkdown(c, depth + 1).trim()}`;
|
||||
})
|
||||
.join('\n');
|
||||
return `${items}\n\n`;
|
||||
}
|
||||
|
||||
if (node.type === 'listitem') {
|
||||
return (node.children || []).map((c: any) => lexicalToMarkdown(c, depth)).join('');
|
||||
}
|
||||
|
||||
if (node.type === 'quote') {
|
||||
const text = (node.children || []).map((c: any) => lexicalToMarkdown(c, depth)).join('');
|
||||
return `> ${text}\n\n`;
|
||||
}
|
||||
|
||||
if (node.type === 'link') {
|
||||
const url = node.fields?.url || node.url || '';
|
||||
const text = (node.children || []).map((c: any) => lexicalToMarkdown(c, depth)).join('');
|
||||
return `[${text}](${url})`;
|
||||
}
|
||||
|
||||
if (node.type === 'upload') {
|
||||
const url = node.value?.url || '';
|
||||
const alt = node.value?.alt || node.value?.filename || '';
|
||||
return `\n\n`;
|
||||
}
|
||||
|
||||
if (node.type === 'block') {
|
||||
const blockType = node.fields?.blockType;
|
||||
if (blockType === 'contactSection') {
|
||||
return `<ContactSection showMap={${!!node.fields?.showMap}} showForm={${!!node.fields?.showForm}} showHours={${!!node.fields?.showHours}} />\n\n`;
|
||||
}
|
||||
if (blockType === 'heroSection') {
|
||||
return `<HeroSection badge="${node.fields?.badge || ''}" title="${node.fields?.title || ''}" subtitle="${node.fields?.subtitle || ''}" alignment="${node.fields?.alignment || 'left'}" />\n\n`;
|
||||
}
|
||||
if (blockType === 'features') {
|
||||
return `<FeaturesSection layout="${node.fields?.layout || ''}" />\n\n`;
|
||||
}
|
||||
// Generic MDX block wrapper if unknown
|
||||
return `<Block type="${blockType}" data={${JSON.stringify(node.fields)}} />\n\n`;
|
||||
}
|
||||
|
||||
if (node.children && Array.isArray(node.children)) {
|
||||
return node.children.map((c: any) => lexicalToMarkdown(c, depth)).join('');
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
27
tests/lexical.test.ts
Normal file
27
tests/lexical.test.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { lexicalToMarkdown } from '../lib/mdx-utils';
|
||||
|
||||
describe('lexicalToMarkdown', () => {
|
||||
it('should convert lexical AST to markdown', () => {
|
||||
const ast = {
|
||||
type: 'root',
|
||||
children: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{ type: 'text', text: 'Hello ' },
|
||||
{ type: 'text', text: 'World', format: 1 }, // bold
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'heading',
|
||||
tag: 'h2',
|
||||
children: [{ type: 'text', text: 'Title' }],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = lexicalToMarkdown(ast);
|
||||
expect(result).toBe('Hello **World**\n\n## Title\n\n');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user