fix(mdx): robust parsing for payload data and ignore sentry pipe noise
This commit is contained in:
@@ -65,11 +65,8 @@ export async function getPostBySlug(slug: string, locale: string): Promise<PostD
|
|||||||
const fileContent = await fs.readFile(filePath, 'utf-8');
|
const fileContent = await fs.readFile(filePath, 'utf-8');
|
||||||
const { data, content } = matter(fileContent);
|
const { data, content } = matter(fileContent);
|
||||||
|
|
||||||
// Fix MDX data props dropped by next-mdx-remote
|
const { fixMdxDataProps } = await import('./mdx-utils');
|
||||||
// Payload serializes as data={{"items":...}} which Acorn treats as a Block statement.
|
const fixedContent = fixMdxDataProps(content);
|
||||||
const fixedContent = content.replace(/data=\{\{([\s\S]*?)\}\}/g, (match, p1) => {
|
|
||||||
return 'data="{' + p1.replace(/"/g, '"') + '}"';
|
|
||||||
});
|
|
||||||
|
|
||||||
let parsedContent = fixedContent;
|
let parsedContent = fixedContent;
|
||||||
try {
|
try {
|
||||||
|
|||||||
46
lib/mdx-utils.ts
Normal file
46
lib/mdx-utils.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
export function fixMdxDataProps(content: string): string {
|
||||||
|
let fixedContent = content;
|
||||||
|
let dataIndex = fixedContent.indexOf('data={{');
|
||||||
|
|
||||||
|
while (dataIndex !== -1) {
|
||||||
|
let openCount = 0;
|
||||||
|
let endIndex = -1;
|
||||||
|
const startObj = dataIndex + 6; // index of the first '{' in 'data={{'
|
||||||
|
|
||||||
|
for (let i = startObj; i < fixedContent.length; i++) {
|
||||||
|
if (fixedContent[i] === '{') {
|
||||||
|
openCount++;
|
||||||
|
} else if (fixedContent[i] === '}') {
|
||||||
|
openCount--;
|
||||||
|
if (openCount === 0) {
|
||||||
|
endIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endIndex !== -1) {
|
||||||
|
// jsonStr is the content INSIDE the outer curly braces
|
||||||
|
const jsonStr = fixedContent.substring(startObj + 1, endIndex);
|
||||||
|
const safeJsonStr = jsonStr.replace(/"/g, '"');
|
||||||
|
const replacement = `data="{${safeJsonStr}}"`;
|
||||||
|
|
||||||
|
// We also need to consume the closing `}` of the `data={{`
|
||||||
|
const nextCharIndex = endIndex + 1;
|
||||||
|
const skipChars =
|
||||||
|
nextCharIndex < fixedContent.length && fixedContent[nextCharIndex] === '}' ? 2 : 1;
|
||||||
|
|
||||||
|
fixedContent =
|
||||||
|
fixedContent.substring(0, dataIndex) +
|
||||||
|
replacement +
|
||||||
|
fixedContent.substring(endIndex + skipChars);
|
||||||
|
|
||||||
|
dataIndex = fixedContent.indexOf('data={{', dataIndex + replacement.length);
|
||||||
|
} else {
|
||||||
|
// Malformed braces, prevent infinite loop
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fixedContent;
|
||||||
|
}
|
||||||
@@ -53,11 +53,8 @@ export async function getPageBySlug(slug: string, locale: string): Promise<PageD
|
|||||||
const fileContent = await fs.readFile(filePath, 'utf-8');
|
const fileContent = await fs.readFile(filePath, 'utf-8');
|
||||||
const { data, content } = matter(fileContent);
|
const { data, content } = matter(fileContent);
|
||||||
|
|
||||||
// Fix MDX data props dropped by next-mdx-remote
|
const { fixMdxDataProps } = await import('./mdx-utils');
|
||||||
// Payload serializes as data={{"items":...}} which Acorn treats as a Block statement.
|
const fixedContent = fixMdxDataProps(content);
|
||||||
const fixedContent = content.replace(/data=\{\{([\s\S]*?)\}\}/g, (match, p1) => {
|
|
||||||
return 'data="{' + p1.replace(/"/g, '"') + '}"';
|
|
||||||
});
|
|
||||||
|
|
||||||
let parsedContent = fixedContent;
|
let parsedContent = fixedContent;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -39,11 +39,8 @@ export async function getProductBySlug(slug: string, locale: string): Promise<Pr
|
|||||||
const fileContent = await fs.readFile(filePath, 'utf-8');
|
const fileContent = await fs.readFile(filePath, 'utf-8');
|
||||||
const { data, content } = matter(fileContent);
|
const { data, content } = matter(fileContent);
|
||||||
|
|
||||||
// Fix MDX data props dropped by next-mdx-remote
|
const { fixMdxDataProps } = await import('./mdx-utils');
|
||||||
// Payload serializes as data={{"items":...}} which Acorn treats as a Block statement.
|
const fixedContent = fixMdxDataProps(content);
|
||||||
const fixedContent = content.replace(/data=\{\{([\s\S]*?)\}\}/g, (match, p1) => {
|
|
||||||
return 'data="{' + p1.replace(/"/g, '"') + '}"';
|
|
||||||
});
|
|
||||||
|
|
||||||
let parsedContent = fixedContent;
|
let parsedContent = fixedContent;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -12,4 +12,5 @@ Sentry.init({
|
|||||||
|
|
||||||
// Setting this option to true will print useful information to the console while you're setting up Sentry.
|
// Setting this option to true will print useful information to the console while you're setting up Sentry.
|
||||||
debug: false,
|
debug: false,
|
||||||
|
ignoreErrors: ['failed to pipe response'],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,4 +12,5 @@ Sentry.init({
|
|||||||
|
|
||||||
// Setting this option to true will print useful information to the console while you're setting up Sentry.
|
// Setting this option to true will print useful information to the console while you're setting up Sentry.
|
||||||
debug: false,
|
debug: false,
|
||||||
|
ignoreErrors: ['failed to pipe response'],
|
||||||
});
|
});
|
||||||
|
|||||||
17
tests/mdx-regex.test.ts
Normal file
17
tests/mdx-regex.test.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { fixMdxDataProps } from '../lib/mdx-utils';
|
||||||
|
|
||||||
|
describe('MDX Data Props Fixer', () => {
|
||||||
|
it('should correctly parse MDX with nested JSON containing }}', () => {
|
||||||
|
// This string simulates the exact structure that causes the bug in n2x2y.mdx
|
||||||
|
const mdxInput = `<Block type="productTabs" data={{"content":{"root":{"children":[]}},"id":"123"}} />`;
|
||||||
|
|
||||||
|
const result = fixMdxDataProps(mdxInput);
|
||||||
|
|
||||||
|
// The expected output should have the entire JSON object enclosed in data="{...}"
|
||||||
|
// and NO trailing characters left over from the regex truncating early.
|
||||||
|
const expected = `<Block type="productTabs" data="{"content":{"root":{"children":[]}},"id":"123"}" />`;
|
||||||
|
|
||||||
|
expect(result).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user