UNPKG

known-ui

Version:

A CLI tool for integrating Known UI components into your Next.js projects.

165 lines (140 loc) 4.72 kB
import { transform } from "@babel/core"; import * as parser from "@babel/parser"; import traverse from "@babel/traverse"; import generate from "@babel/generator"; import * as t from "@babel/types"; async function convertTsxToJsx(content) { try { // First pass: Use Babel to handle basic TypeScript transformations const result = await transform(content, { plugins: [ "@babel/plugin-transform-typescript", ["@babel/plugin-syntax-typescript", { isTSX: true }], ], presets: ["@babel/preset-react"], retainLines: true, }); if (!result?.code) { throw new Error("Babel transformation failed"); } // Parse the transformed code const ast = parser.parse(result.code, { sourceType: "module", plugins: ["jsx", "react"], }); // Second pass: Custom AST transformations traverse(ast, { // Clean up React.forwardRef type annotations CallExpression(path) { if ( t.isMemberExpression(path.node.callee) && t.isIdentifier(path.node.callee.object, { name: "React" }) && t.isIdentifier(path.node.callee.property, { name: "forwardRef" }) ) { path.node.typeParameters = null; } }, // Remove remaining type imports ImportDeclaration(path) { if (path.node.importKind === "type") { path.remove(); } }, // Clean up function parameters Function(path) { path.node.params.forEach((param) => { if (t.isIdentifier(param)) { param.typeAnnotation = null; } else if (t.isObjectPattern(param)) { param.typeAnnotation = null; param.properties.forEach((prop) => { if (t.isObjectProperty(prop)) { prop.typeAnnotation = null; } }); } }); }, // Remove type assertions TSAsExpression(path) { path.replaceWith(path.node.expression); }, // Clean up variable declarations VariableDeclarator(path) { path.node.id.typeAnnotation = null; }, }); // Generate the final code const output = generate(ast, { retainLines: true, quotes: "single", compact: false, }); // Post-processing cleanup let finalCode = output.code // Remove any remaining type annotations .replace(/:\s*[A-Za-z][\w\s<>[\],{}|&.]*(?=[,)])/g, "") // Clean up empty lines .replace(/\n\s*\n\s*\n/g, "\n\n") // Ensure proper component displayName .replace(/(\w+)\.displayName\s*=\s*(['"])\1\2/g, '$1.displayName = "$1"') // Fix import statements .replace(/import\s*{\s*(\w+)\s*}\s*from\s*['"]@\/types.*?['"]/g, "") // Remove empty interfaces .replace(/interface\s+\w+\s*\{\s*\}\s*/g, ""); // Format the code with proper indentation finalCode = formatCode(finalCode); return finalCode; } catch (error) { console.error("Error converting TSX to JSX:", error); // Fallback to the original content if conversion fails return content; } } // Improved code formatting function function formatCode(code) { const lines = code.split("\n"); let indent = 0; const formattedLines = []; let inJSX = false; let inImports = false; for (let i = 0; i < lines.length; i++) { let line = lines[i].trim(); if (!line) { // Preserve empty lines between logical blocks if ( formattedLines.length && formattedLines[formattedLines.length - 1] !== "" ) { formattedLines.push(""); } continue; } // Handle imports grouping if (line.startsWith("import")) { inImports = true; } else if (inImports) { inImports = false; formattedLines.push(""); } // Track JSX and brackets for proper indentation if (line.includes("=>") && line.includes("(") && !line.includes(")")) { inJSX = true; } // Adjust indent based on brackets const openBrackets = (line.match(/[{(]/g) || []).length; const closeBrackets = (line.match(/[})]/g) || []).length; // Calculate proper indentation const currentIndent = " ".repeat(Math.max(0, indent)); // Add the formatted line formattedLines.push(currentIndent + line); // Update indent for next line indent += openBrackets - closeBrackets; // Handle JSX closing if (inJSX && line.includes(")")) { inJSX = false; } } return formattedLines.join("\n"); } export { convertTsxToJsx };