Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3334937b67 | |||
| 246e6540d4 | |||
| aa299b285d | |||
| 2be0d1ea92 | |||
| 0554460153 | |||
| 4ae45b1aa7 | |||
| 76cbdaa37e | |||
| 5b1a2eff7f |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -28,3 +28,4 @@ backups/
|
||||
|
||||
# Local data / backups
|
||||
.data/
|
||||
scratch/
|
||||
|
||||
@@ -266,18 +266,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 +285,20 @@ 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 || [];
|
||||
} catch (e) {
|
||||
console.error('Failed to parse productTabs data for page:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import VideoSection from './home/VideoSection';
|
||||
import CTA from './home/CTA';
|
||||
import { AgbHistoryBlock } from './AgbHistoryBlock';
|
||||
import { PDFDownloadBlock } from './PDFDownloadBlock';
|
||||
import ProductTechnicalData from './ProductTechnicalData';
|
||||
function ContactSection(props: any) {
|
||||
return (
|
||||
<div className="p-8 border-2 border-dashed border-primary my-8 text-center text-primary font-bold">
|
||||
@@ -100,6 +101,8 @@ async function Block(props: any) {
|
||||
return <AgbHistoryBlock {...data} />;
|
||||
case 'pdfDownload':
|
||||
return <PDFDownloadBlock {...data} />;
|
||||
case 'productTabs':
|
||||
return <ProductTechnicalData data={data} />;
|
||||
|
||||
default:
|
||||
return (
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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 '';
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
"prepare": "husky",
|
||||
"preinstall": "npx only-allow pnpm"
|
||||
},
|
||||
"version": "2.3.32",
|
||||
"version": "2.3.35",
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"@parcel/watcher",
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
14
tests/mdx-content-mapping.test.tsx
Normal file
14
tests/mdx-content-mapping.test.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
describe('MDXContent Mapping', () => {
|
||||
it('should have productTabs in the switch statement to prevent Unknown Block Type errors', () => {
|
||||
const filePath = path.join(process.cwd(), 'components/MDXContent.tsx');
|
||||
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
||||
|
||||
// Check if the switch case for productTabs exists
|
||||
expect(fileContent).toContain("case 'productTabs':");
|
||||
expect(fileContent).toContain('<ProductTechnicalData');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user