in src/ast.ts [177:279]
export function parseCode(
code: string,
elementName: string,
parseProvider?: (ast: any) => void,
) {
const propValues: { [key: string]: string } = {};
const stateValues: { [key: string]: string } = {};
let parsedProvider: any = undefined;
try {
const ast = parse(code) as any;
traverse(ast, {
JSXElement(path) {
if (
Object.keys(propValues).length === 0 && // process just the first element
path.node.openingElement.type === "JSXOpeningElement" &&
//@ts-ignore
path.node.openingElement.name.name === elementName
) {
path.node.openingElement.attributes.forEach((attr: any) => {
const name = attr.name.name;
let value = null;
if (attr.value === null) {
//boolean prop without value
value = true;
} else {
if (attr.value.type === "StringLiteral") {
value = attr.value.value;
} else if (attr.value.type === "JSXExpressionContainer") {
if (attr.value.expression.type === "BooleanLiteral") {
value = attr.value.expression.value;
} else {
value = formatAstAndPrint(
//@ts-ignore
t.program([t.expressionStatement(attr.value.expression)]),
30,
);
if (attr.value.expression.type === "ObjectExpression") {
// the generated code is ({ .... }), this removes the brackets to
// keep the input more readable
value = value.slice(1, -1);
}
if (
attr.value.expression.type === "MemberExpression" &&
attr.value.expression.computed
) {
// turn a['hello-world'] into a.hello-world so we don't have to deal with two
// variants in the enum knob UI
value = `${attr.value.expression.object.name}.${attr.value.expression.property.value}`;
}
}
}
}
propValues[name] = value;
});
propValues["children"] = formatAstAndPrint(
getAstJsxElement("ViewRoot", [], path.node.children as any) as any,
30,
)
.replace(/\n /g, "\n")
.replace(/^<ViewRoot>\n?/, "")
.replace(/<\/ViewRoot>$/, "")
.replace(/\s*<ViewRoot \/>\s*/, "");
}
},
VariableDeclarator(path) {
// looking for React.useState()
const node = path.node as any;
if (
node.id.type === "ArrayPattern" &&
node.init.type === "CallExpression" &&
node.init.callee.property.name === "useState"
) {
const name = node.id.elements[0].name;
const valueNode = node.init.arguments[0];
if (
valueNode.type === "StringLiteral" ||
valueNode.type === "BooleanLiteral"
) {
stateValues[name] = valueNode.value;
} else {
stateValues[name] = generate(valueNode).code;
}
}
},
});
if (parseProvider) {
parsedProvider = parseProvider(ast);
}
} catch (e) {
throw new Error("Code is not valid and can't be parsed.");
}
// override props by local state (React hooks)
Object.keys(stateValues).forEach((stateValueKey) => {
Object.keys(propValues).forEach((propValueKey) => {
if (propValues[propValueKey] === stateValueKey) {
propValues[propValueKey] = stateValues[stateValueKey];
}
});
});
return { parsedProps: propValues, parsedProvider };
}