UNPKG

v0-rails

Version:

Convert React/JSX + Tailwind UI code from v0.dev to Rails ViewComponent classes and ERB templates with automatic slot detection, icon handling, and route generation

94 lines (88 loc) 3.48 kB
const traverse = require('@babel/traverse').default; const t = require('@babel/types'); const path = require('path'); const { detectHooksUsage } = require('../utils/hook-detector'); const { detectEvents } = require('../utils/event-detector'); const { extractProps } = require('../utils/prop-extractor'); /** * Process a JSX component AST and extract relevant information * @param {Object} ast - Babel AST of the JSX file * @param {string} filePath - Path to the JSX file * @returns {Object} - Component information */ function processJsxComponent(ast, filePath) { const componentInfo = { name: path.basename(filePath, path.extname(filePath)), props: [], jsxElements: [], hasStateOrEffects: false, events: [], warnings: [], originalPath: filePath }; // Find the component function declaration traverse(ast, { FunctionDeclaration(path) { if (t.isIdentifier(path.node.id)) { componentInfo.name = path.node.id.name; componentInfo.props = extractProps(path.node.params); // Detect hooks usage const hooksInfo = detectHooksUsage(path); componentInfo.hasStateOrEffects = hooksInfo.hasStateOrEffects; if (hooksInfo.hooks.length > 0) { componentInfo.warnings.push(...hooksInfo.hooks.map(hook => `Component uses React hook: ${hook}. This may require manual conversion.`)); } } }, // Also check for functional component as variable declaration (const X = () => {}) VariableDeclaration(path) { if (path.node.declarations && path.node.declarations.length > 0) { const declaration = path.node.declarations[0]; if (t.isIdentifier(declaration.id) && (t.isArrowFunctionExpression(declaration.init) || t.isFunctionExpression(declaration.init))) { componentInfo.name = declaration.id.name; componentInfo.props = extractProps(declaration.init.params); // Detect hooks usage const hooksInfo = detectHooksUsage(path); componentInfo.hasStateOrEffects = hooksInfo.hasStateOrEffects; if (hooksInfo.hooks.length > 0) { componentInfo.warnings.push(...hooksInfo.hooks.map(hook => `Component uses React hook: ${hook}. This may require manual conversion.`)); } } } }, // Extract JSX elements JSXElement(path) { componentInfo.jsxElements.push(path.node); // Detect events const events = detectEvents(path.node); if (events.length > 0) { componentInfo.events.push(...events); } }, // Check for imports that might indicate complex React features ImportDeclaration(path) { if (t.isStringLiteral(path.node.source) && path.node.source.value === 'react' && path.node.specifiers) { const complexFeatures = ['useContext', 'createContext', 'Suspense', 'lazy', 'memo', 'forwardRef']; path.node.specifiers.forEach(specifier => { if (t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported) && complexFeatures.includes(specifier.imported.name)) { componentInfo.warnings.push(`Component uses advanced React feature: ${specifier.imported.name}. Manual conversion may be required.`); } }); } } }); // Validate component information if (!componentInfo.name) { throw new Error(`Could not determine component name from file: ${filePath}`); } return componentInfo; } module.exports = { processJsxComponent };