This solution is generated by ChatGPT, correctness is not guaranteed.
AI solution for JavaScript Coding Question on BFE.dev
143. Virtual DOM IV - JSX 1
const h = (tag, props, ...children) => ({
tag,
props: props || {},
children
});
// helper function to extract JSX text content
const extractJSXText = (JSXNode) => {
const jsxChild = JSXNode.children[0];
if (jsxChild && jsxChild.type === 'JSXText') {
return jsxChild.value;
}
return null;
};
const parse = (code) => {
// remove spaces and newline for simplicity
const cleanedCode = code.replace(/\s+/g, '');
let currentIndex = 0;
const parseOpeningElement = () => {
if (cleanedCode[currentIndex] !== '<') {
throw new Error('Invalid JSX Element');
}
currentIndex++;
const tagName = [];
const attributes = {};
while (cleanedCode[currentIndex] !== '>' && cleanedCode[currentIndex] !== ' ') {
tagName.push(cleanedCode[currentIndex]);
currentIndex++;
}
while (cleanedCode[currentIndex] !== '>') {
currentIndex++;
if (cleanedCode[currentIndex] === '/') {
return {
type: 'JSXElement',
openingElement: {
type: 'JSXOpeningElement',
tagName: tagName.join(''),
attributes
},
children: []
};
}
let attrName = [];
let attrValue = [];
let isParsingAttrName = true;
while (cleanedCode[currentIndex] !== '>' && cleanedCode[currentIndex] !== ' ') {
if (isParsingAttrName) {
attrName.push(cleanedCode[currentIndex]);
} else {
attrValue.push(cleanedCode[currentIndex]);
}
currentIndex++;
if (cleanedCode[currentIndex] === '=') {
isParsingAttrName = false;
currentIndex++;
}
}
attributes[attrName.join('')] = attrValue.join('');
}
currentIndex++;
return {
type: 'JSXElement',
openingElement: {
type: 'JSXOpeningElement',
tagName: tagName.join(''),
attributes,
},
children: []
};
};
const parseClosingElement = () => {
if (cleanedCode[currentIndex] !== '<') {
throw new Error('Invalid JSX Element');
}
currentIndex++;
const tagName = [];
while (cleanedCode[currentIndex] !== '>') {
tagName.push(cleanedCode[currentIndex]);
currentIndex++;
}
currentIndex++;
return tagName.join('');
};
const parseJSXText = () => {
const text = extractJSXText(parseOpeningElement());
if (cleanedCode[currentIndex] === '<' && cleanedCode[currentIndex + 1] === '/' && cleanedCode[currentIndex + 2] === text[0] && cleanedCode[currentIndex + 3] === '>') {
currentIndex += 4;
} else {
throw new Error('Invalid JSX Element');
}
return {
type: 'JSXText',
value: text
};
};
const parseChild = () => {
if (cleanedCode[currentIndex] === '<') {
if (cleanedCode[currentIndex + 1] === '/') {
throw new Error('Invalid JSX Element');
}
const jsxNode = parseOpeningElement();
let jsxText = extractJSXText(jsxNode);
if (cleanedCode[currentIndex] === '<') {
const closingTagName = parseClosingElement();
if (closingTagName !== jsxNode.openingElement.tagName) {
throw new Error('Invalid JSX Element');
}
jsxNode.children.push({type: 'JSXText', value: jsxText});
return jsxNode;
} else {
throw new Error('Invalid JSX Element');
}
} else if (cleanedCode[currentIndex] !== '<') {
return parseJSXText();
} else {
throw new Error('Invalid JSX Element');
}
};
const parseJSXChildren = () => {
const jsxChildren = [];
while (currentIndex < cleanedCode.length && cleanedCode[currentIndex] !== '<' && cleanedCode[currentIndex - 1] && cleanedCode[currentIndex - 1] !== '>') {
currentIndex++;
}
while (currentIndex < cleanedCode.length) {
jsxChildren.push(parseChild());
while (currentIndex < cleanedCode.length && cleanedCode[currentIndex] !== '<' && cleanedCode[currentIndex - 1] && cleanedCode[currentIndex - 1] !== '>') {
currentIndex++;
}
if (cleanedCode[currentIndex] === '<' && cleanedCode[currentIndex + 1] === '/') {
break;
}
}
return jsxChildren;
};
const jsxElement = parseOpeningElement();
jsxElement.children = parseJSXChildren();
if (cleanedCode[currentIndex] === '<' && cleanedCode[currentIndex + 2] === '/') {
const closingTagName = parseClosingElement();
if (closingTagName !== jsxElement.openingElement.tagName) {
throw new Error('Invalid JSX Element');
}
}
return jsxElement;
};
const generate = (ast) => {
if (ast.type === 'JSXText') {
return JSON.stringify(ast.value);
}
const children = ast.children.map(child => generate(child)).join('');
return `h('${ast.openingElement.tagName}',${JSON.stringify(ast.openingElement.attributes)}, ${children})`;
};