ntrn
Version:
š Professional AI-powered Next.js to React Native converter with intelligent screen creation, never-fail conversion system, comprehensive auto-fix, automatic runtime error fixing, AI assistant, Mistral AI problem-solving, intelligent Shadcn/ui conversion
842 lines (719 loc) ⢠28.9 kB
JavaScript
// š§ Smart Converter - Like Cursor AI for React Native
// Handles 95% of conversions instantly with zero AI calls
import fs from 'fs-extra';
import path from 'path';
import chalk from 'chalk';
import prompts from 'prompts';
export class SmartConverter {
constructor() {
this.nextjsPath = null;
this.outputPath = null;
this.initializeSmartPatterns();
}
async start() {
console.log(chalk.yellow('āļø NTRN Legacy Mode'));
console.log(chalk.gray('Traditional file-by-file conversion approach\n'));
// Project selection
await this.selectProjectPaths();
// Start legacy conversion process
await this.performLegacyConversion();
}
async selectProjectPaths() {
console.log(chalk.cyan('š Project Selection'));
console.log(chalk.gray('Choose your Next.js project and output location\n'));
const { nextjsPath, outputName } = await prompts([
{
type: 'text',
name: 'nextjsPath',
message: 'Enter the path to your Next.js project:',
initial: process.cwd(),
validate: async (input) => {
if (!input) return 'Path is required';
const fullPath = path.resolve(input);
if (!await fs.exists(fullPath)) return 'Path does not exist';
// Check if it's actually a Next.js project
const packageJsonPath = path.join(fullPath, 'package.json');
if (await fs.exists(packageJsonPath)) {
try {
const pkg = await fs.readJson(packageJsonPath);
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
if (!deps.next) {
console.log(chalk.yellow('ā ļø This doesn\'t appear to be a Next.js project (no "next" dependency found)'));
const proceed = await prompts({
type: 'toggle',
name: 'continue',
message: 'Continue anyway?',
initial: false,
active: 'yes',
inactive: 'no'
});
if (!proceed.continue) return 'Please provide a valid Next.js project path';
}
} catch (error) {
return 'Could not read package.json - please verify this is a valid Next.js project';
}
}
return true;
}
},
{
type: 'text',
name: 'outputName',
message: 'Enter the name for your React Native project folder:',
initial: 'converted-react-native',
validate: (name) => {
if (!name) return 'Project name is required';
if (!/^[a-zA-Z0-9-_]+$/.test(name)) {
return 'Project name can only contain letters, numbers, hyphens, and underscores';
}
return true;
}
}
]);
if (!nextjsPath || !outputName) {
console.log(chalk.red('ā Project selection cancelled.'));
process.exit(1);
}
// Resolve full paths
this.nextjsPath = path.resolve(nextjsPath);
this.outputPath = path.resolve(outputName);
// Check if output directory already exists
if (await fs.exists(this.outputPath)) {
const overwrite = await prompts({
type: 'toggle',
name: 'overwrite',
message: `Directory "${outputName}" already exists. Overwrite?`,
initial: false,
active: 'yes',
inactive: 'no'
});
if (!overwrite.overwrite) {
console.log(chalk.red('ā Project creation cancelled.'));
process.exit(1);
}
await fs.remove(this.outputPath);
}
console.log(chalk.green('\nā
Project paths configured:'));
console.log(chalk.blue(`š Input: ${this.nextjsPath}`));
console.log(chalk.blue(`š Output: ${this.outputPath}\n`));
}
async performLegacyConversion() {
console.log(chalk.cyan('š Starting legacy conversion process...\n'));
try {
// Create basic Expo project structure
await this.createBasicExpoProject();
// Import and use the legacy conversion function
const { convertPagesToScreens } = await import('../convertPagesToScreens.js');
// Convert pages and components using legacy approach
console.log(chalk.cyan('š Converting Next.js components to React Native...\n'));
const result = await convertPagesToScreens(this.nextjsPath, this.outputPath);
if (result.success) {
console.log(chalk.green('\nš Legacy conversion completed!'));
console.log(chalk.green('š± Your React Native app is ready!'));
console.log(chalk.cyan('\nš Next Steps:'));
console.log(chalk.white(`1. cd ${path.basename(this.outputPath)}`));
console.log(chalk.white('2. npm install'));
console.log(chalk.white('3. npx expo start'));
console.log(chalk.cyan('\nš” Upgrade Recommendation:'));
console.log(chalk.yellow('Try the new professional converter: ') + chalk.green('ntrn'));
console.log(chalk.gray('Better code quality, intelligent analysis, and auto-fixing'));
} else {
console.log(chalk.red('\nā Legacy conversion encountered some issues.'));
console.log(chalk.yellow('Try the new professional approach: ntrn'));
}
} catch (error) {
console.error(chalk.red('ā Legacy conversion failed:'), error.message);
console.log(chalk.yellow('Try the new professional approach: ntrn'));
}
}
async createBasicExpoProject() {
console.log(chalk.cyan('š± Creating basic Expo project structure...'));
// Create directory structure
await fs.ensureDir(this.outputPath);
// Create basic package.json for React Native Expo
const packageJson = {
"name": path.basename(this.outputPath),
"version": "1.0.0",
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web"
},
"dependencies": {
"expo": "~50.0.0",
"react": "18.2.0",
"react-native": "0.73.0",
"@react-navigation/native": "^6.1.0",
"@react-navigation/stack": "^6.3.0",
"react-native-screens": "~3.29.0",
"react-native-safe-area-context": "4.8.2",
"@react-native-async-storage/async-storage": "1.21.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@types/react": "~18.2.45",
"typescript": "^5.1.3"
}
};
await fs.writeJson(path.join(this.outputPath, 'package.json'), packageJson, { spaces: 2 });
// Create app.json
const appJson = {
"expo": {
"name": "Converted React Native App",
"slug": path.basename(this.outputPath),
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#FFFFFF"
}
},
"web": {
"favicon": "./assets/favicon.png"
}
}
};
await fs.writeJson(path.join(this.outputPath, 'app.json'), appJson, { spaces: 2 });
// Create basic App.tsx
const appTsx = `import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from './screens/HomeScreen';
const Stack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}`;
await fs.writeFile(path.join(this.outputPath, 'App.tsx'), appTsx);
// Create directories
await fs.ensureDir(path.join(this.outputPath, 'screens'));
await fs.ensureDir(path.join(this.outputPath, 'components'));
await fs.ensureDir(path.join(this.outputPath, 'assets'));
console.log(chalk.green('ā
Basic Expo project structure created'));
}
initializeSmartPatterns() {
// š§ Smart Import Patterns (Common Sense)
this.smartImports = {
// React Native Core
'View': 'react-native',
'Text': 'react-native',
'ScrollView': 'react-native',
'TouchableOpacity': 'react-native',
'TextInput': 'react-native',
'Image': 'react-native',
'StyleSheet': 'react-native',
'SafeAreaView': 'react-native-safe-area-context',
'FlatList': 'react-native',
'SectionList': 'react-native',
'Modal': 'react-native',
'Alert': 'react-native',
'ActivityIndicator': 'react-native',
'Switch': 'react-native',
'Picker': '@react-native-picker/picker',
// Navigation (Auto-detect usage)
'useNavigation': '@react-navigation/native',
'useRoute': '@react-navigation/native',
'useFocusEffect': '@react-navigation/native',
'NavigationContainer': '@react-navigation/native',
'createStackNavigator': '@react-navigation/stack',
'createBottomTabNavigator': '@react-navigation/bottom-tabs',
// Storage & APIs
'AsyncStorage': '@react-native-async-storage/async-storage',
'Clipboard': '@react-native-clipboard/clipboard',
'NetInfo': '@react-native-netinfo/netinfo',
// Common Libraries
'LinearGradient': 'react-native-linear-gradient',
'FastImage': 'react-native-fast-image'
};
// šÆ Smart Element Transformations (Instant)
this.elementTransforms = new Map([
// HTML ā React Native (Instant)
['div', { component: 'View', props: this.transformDivProps }],
['span', { component: 'Text', props: this.transformTextProps }],
['p', { component: 'Text', props: this.transformTextProps }],
['h1', { component: 'Text', props: this.transformHeadingProps }],
['h2', { component: 'Text', props: this.transformHeadingProps }],
['h3', { component: 'Text', props: this.transformHeadingProps }],
['h4', { component: 'Text', props: this.transformHeadingProps }],
['h5', { component: 'Text', props: this.transformHeadingProps }],
['h6', { component: 'Text', props: this.transformHeadingProps }],
['button', { component: 'TouchableOpacity', props: this.transformButtonProps }],
['input', { component: 'TextInput', props: this.transformInputProps }],
['img', { component: 'Image', props: this.transformImageProps }],
['a', { component: 'TouchableOpacity', props: this.transformLinkProps }],
['ul', { component: 'View', props: this.transformListProps }],
['li', { component: 'View', props: this.transformListItemProps }],
['form', { component: 'View', props: this.transformFormProps }],
['main', { component: 'ScrollView', props: this.transformScrollProps }],
['section', { component: 'View', props: this.transformSectionProps }],
['article', { component: 'View', props: this.transformSectionProps }],
['header', { component: 'View', props: this.transformSectionProps }],
['footer', { component: 'View', props: this.transformSectionProps }],
['nav', { component: 'View', props: this.transformSectionProps }]
]);
// š„ Smart Event Transformations (Instant)
this.eventTransforms = new Map([
['onClick', 'onPress'],
['onChange', 'onChangeText'],
['onSubmit', 'onPress'],
['onFocus', 'onFocus'],
['onBlur', 'onBlur'],
['onMouseEnter', 'onPressIn'],
['onMouseLeave', 'onPressOut'],
['onKeyPress', 'onKeyPress'],
['onScroll', 'onScroll']
]);
// šØ Smart Style Transformations (CSS ā StyleSheet)
this.styleTransforms = new Map([
// Layout
['display: flex', 'flexDirection: "row"'],
['flex-direction: column', 'flexDirection: "column"'],
['flex-direction: row', 'flexDirection: "row"'],
['justify-content: center', 'justifyContent: "center"'],
['justify-content: space-between', 'justifyContent: "space-between"'],
['justify-content: flex-start', 'justifyContent: "flex-start"'],
['justify-content: flex-end', 'justifyContent: "flex-end"'],
['align-items: center', 'alignItems: "center"'],
['align-items: flex-start', 'alignItems: "flex-start"'],
['align-items: flex-end', 'alignItems: "flex-end"'],
// Spacing
['margin: 0', 'margin: 0'],
['padding: 0', 'padding: 0'],
['margin-top', 'marginTop'],
['margin-bottom', 'marginBottom'],
['margin-left', 'marginLeft'],
['margin-right', 'marginRight'],
['padding-top', 'paddingTop'],
['padding-bottom', 'paddingBottom'],
['padding-left', 'paddingLeft'],
['padding-right', 'paddingRight'],
// Typography
['font-size', 'fontSize'],
['font-weight: bold', 'fontWeight: "bold"'],
['font-weight: normal', 'fontWeight: "normal"'],
['text-align: center', 'textAlign: "center"'],
['text-align: left', 'textAlign: "left"'],
['text-align: right', 'textAlign: "right"'],
['color', 'color'],
['line-height', 'lineHeight'],
// Background & Border
['background-color', 'backgroundColor'],
['border-radius', 'borderRadius'],
['border-width', 'borderWidth'],
['border-color', 'borderColor'],
// Positioning
['position: absolute', 'position: "absolute"'],
['position: relative', 'position: "relative"'],
['top', 'top'],
['bottom', 'bottom'],
['left', 'left'],
['right', 'right'],
['z-index', 'zIndex'],
// Dimensions
['width', 'width'],
['height', 'height'],
['min-width', 'minWidth'],
['min-height', 'minHeight'],
['max-width', 'maxWidth'],
['max-height', 'maxHeight']
]);
// š Smart Web API Replacements (Instant)
this.webApiReplacements = new Map([
['localStorage.setItem', 'AsyncStorage.setItem'],
['localStorage.getItem', 'AsyncStorage.getItem'],
['localStorage.removeItem', 'AsyncStorage.removeItem'],
['localStorage.clear', 'AsyncStorage.clear'],
['sessionStorage.setItem', 'AsyncStorage.setItem'],
['sessionStorage.getItem', 'AsyncStorage.getItem'],
['window.location.href', 'navigation.navigate'],
['router.push', 'navigation.navigate'],
['router.back', 'navigation.goBack'],
['router.replace', 'navigation.replace'],
['document.getElementById', '// Use ref instead'],
['document.querySelector', '// Use ref instead'],
['window.alert', 'Alert.alert'],
['console.log', 'console.log'], // Keep as is
['fetch(', 'fetch('], // Keep as is but ensure proper error handling
]);
}
// š Main Smart Conversion (Like Cursor AI)
async convertSmart(sourceCode, fileName) {
console.log(chalk.gray(` š§ Smart conversion (like Cursor AI)...`));
try {
let code = sourceCode;
const usedComponents = new Set();
const usedHooks = new Set();
const usedLibraries = new Set();
// Phase 1: Smart Element Transformation (Instant)
code = this.transformElements(code, usedComponents);
// Phase 2: Smart Event Transformation (Instant)
code = this.transformEvents(code);
// Phase 3: Smart Style Transformation (Instant)
code = this.transformStyles(code);
// Phase 4: Smart Web API Replacement (Instant)
code = this.transformWebAPIs(code, usedLibraries);
// Phase 5: Smart Hook Detection (Instant)
code = this.detectAndAddHooks(code, usedHooks);
// Phase 6: Smart Import Generation (Instant)
code = this.generateSmartImports(code, usedComponents, usedHooks, usedLibraries);
// Phase 7: Smart Component Wrapping (Instant)
code = this.wrapInReactNativeComponent(code, fileName);
// Phase 8: Smart Validation (Instant)
const validation = this.validateSmartConversion(code);
if (!validation.isValid) {
console.log(chalk.yellow(` ā ļø Smart validation failed: ${validation.errors.join(', ')}`));
return {
success: false,
reason: `Smart validation failed: ${validation.errors.join(', ')}`,
needsAI: true
};
}
console.log(chalk.green(` ā
Smart conversion successful (0 tokens, instant)`));
return {
success: true,
code,
tokensUsed: 0,
method: 'smart-deterministic',
quality: 'high',
components: Array.from(usedComponents),
hooks: Array.from(usedHooks),
libraries: Array.from(usedLibraries)
};
} catch (error) {
console.log(chalk.yellow(` ā ļø Smart conversion error: ${error.message}`));
return {
success: false,
reason: error.message,
needsAI: true
};
}
}
// šÆ Smart Element Transformation (Like v0.dev)
transformElements(code, usedComponents) {
// Transform JSX elements intelligently
for (const [htmlElement, transform] of this.elementTransforms) {
const rnComponent = transform.component;
usedComponents.add(rnComponent);
// Transform opening tags with props
const openingTagRegex = new RegExp(`<${htmlElement}(\\s[^>]*)?(?<!/)>`, 'gi');
code = code.replace(openingTagRegex, (match, props) => {
const transformedProps = transform.props ? transform.props(props || '') : props || '';
return `<${rnComponent}${transformedProps}>`;
});
// Transform closing tags
const closingTagRegex = new RegExp(`</${htmlElement}>`, 'gi');
code = code.replace(closingTagRegex, `</${rnComponent}>`);
// Transform self-closing tags
const selfClosingRegex = new RegExp(`<${htmlElement}(\\s[^>]*)?/>`, 'gi');
code = code.replace(selfClosingRegex, (match, props) => {
const transformedProps = transform.props ? transform.props(props || '') : props || '';
return `<${rnComponent}${transformedProps}/>`;
});
}
return code;
}
// š„ Smart Event Transformation
transformEvents(code) {
for (const [webEvent, rnEvent] of this.eventTransforms) {
const eventRegex = new RegExp(`${webEvent}=`, 'gi');
code = code.replace(eventRegex, `${rnEvent}=`);
}
return code;
}
// šØ Smart Style Transformation
transformStyles(code) {
// Transform className to style prop
code = code.replace(/className=/g, 'style=');
// Transform inline styles
const inlineStyleRegex = /style=\{([^}]+)\}/g;
code = code.replace(inlineStyleRegex, (match, styles) => {
let transformedStyles = styles;
for (const [cssProperty, rnProperty] of this.styleTransforms) {
const cssRegex = new RegExp(cssProperty.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
transformedStyles = transformedStyles.replace(cssRegex, rnProperty);
}
return `style={${transformedStyles}}`;
});
return code;
}
// š Smart Web API Transformation
transformWebAPIs(code, usedLibraries) {
for (const [webAPI, rnAPI] of this.webApiReplacements) {
if (code.includes(webAPI)) {
code = code.replace(new RegExp(webAPI.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), rnAPI);
// Track required libraries
if (rnAPI.includes('AsyncStorage')) {
usedLibraries.add('@react-native-async-storage/async-storage');
}
if (rnAPI.includes('Alert')) {
usedLibraries.add('react-native');
}
if (rnAPI.includes('navigation')) {
usedLibraries.add('@react-navigation/native');
}
}
}
return code;
}
// šŖ Smart Hook Detection
detectAndAddHooks(code, usedHooks) {
// Detect common patterns that need hooks
if (code.includes('useState') || code.includes('state')) {
usedHooks.add('useState');
}
if (code.includes('useEffect') || code.includes('componentDidMount') || code.includes('async')) {
usedHooks.add('useEffect');
}
if (code.includes('navigation.') || code.includes('router.')) {
usedHooks.add('useNavigation');
}
if (code.includes('route.params') || code.includes('props.route')) {
usedHooks.add('useRoute');
}
if (code.includes('useCallback') || code.includes('useMemo')) {
usedHooks.add('useCallback');
usedHooks.add('useMemo');
}
return code;
}
// š¦ Smart Import Generation (Like Cursor AI)
generateSmartImports(code, usedComponents, usedHooks, usedLibraries) {
const imports = [];
// React import with hooks
const reactImports = ['React'];
if (usedHooks.size > 0) {
reactImports.push(...Array.from(usedHooks));
}
imports.push(`import React, { ${reactImports.slice(1).join(', ')} } from 'react';`);
// React Native imports
const rnComponents = Array.from(usedComponents).filter(comp =>
this.smartImports[comp] === 'react-native'
);
if (rnComponents.length > 0) {
imports.push(`import {\n ${rnComponents.join(',\n ')}\n} from 'react-native';`);
}
// Navigation imports
const navHooks = Array.from(usedHooks).filter(hook =>
this.smartImports[hook] === '@react-navigation/native'
);
if (navHooks.length > 0) {
imports.push(`import { ${navHooks.join(', ')} } from '@react-navigation/native';`);
}
// Other library imports
for (const library of usedLibraries) {
if (library === '@react-native-async-storage/async-storage') {
imports.push(`import AsyncStorage from '${library}';`);
}
}
// SafeAreaView import (always include for screens)
if (usedComponents.has('SafeAreaView')) {
imports.push(`import { SafeAreaView } from 'react-native-safe-area-context';`);
}
return imports.join('\n') + '\n\n' + code;
}
// šļø Smart Component Wrapping
wrapInReactNativeComponent(code, fileName) {
const componentName = this.getComponentName(fileName);
const isScreen = fileName.includes('page.') || fileName.includes('/pages/') || fileName.includes('/screens/');
// Extract existing component content
const componentMatch = code.match(/export\s+default\s+function\s+\w+[^{]*\{([\s\S]*)\}/);
const returnMatch = code.match(/return\s*\(([\s\S]*?)\);?\s*\}/);
let jsxContent = '<View><Text>Component Content</Text></View>';
if (returnMatch) {
jsxContent = returnMatch[1].trim();
} else if (componentMatch) {
// Try to extract JSX from component body
const body = componentMatch[1];
const jsxMatch = body.match(/return\s*\(([\s\S]*?)\)/);
if (jsxMatch) {
jsxContent = jsxMatch[1].trim();
}
}
// Ensure JSX is wrapped properly for React Native
if (!jsxContent.includes('SafeAreaView') && isScreen) {
jsxContent = `<SafeAreaView style={styles.container}>
<ScrollView contentContainerStyle={styles.content}>
${jsxContent}
</ScrollView>
</SafeAreaView>`;
} else if (!jsxContent.includes('View')) {
jsxContent = `<View style={styles.container}>
${jsxContent}
</View>`;
}
const template = `export default function ${componentName}() {
const navigation = useNavigation();
return (
${jsxContent}
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ffffff',
},
content: {
flexGrow: 1,
padding: 16,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 16,
color: '#000000',
},
text: {
fontSize: 16,
lineHeight: 24,
color: '#333333',
},
button: {
backgroundColor: '#007AFF',
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 8,
alignItems: 'center',
marginVertical: 8,
},
buttonText: {
color: '#ffffff',
fontSize: 16,
fontWeight: '600',
},
input: {
borderWidth: 1,
borderColor: '#E1E5E9',
borderRadius: 8,
paddingHorizontal: 16,
paddingVertical: 12,
fontSize: 16,
marginVertical: 8,
backgroundColor: '#ffffff',
},
});`;
return template;
}
// ā
Smart Validation (Instant)
validateSmartConversion(code) {
const errors = [];
// Check for React Native compatibility
if (!code.includes('from \'react\'')) {
errors.push('Missing React import');
}
if (!code.includes('StyleSheet')) {
errors.push('Missing StyleSheet import');
}
if (!code.includes('export default')) {
errors.push('Missing export default');
}
// Check for common issues
if (code.includes('className=')) {
errors.push('Found className usage - should be style prop');
}
if (code.includes('onClick=')) {
errors.push('Found onClick usage - should be onPress');
}
return {
isValid: errors.length === 0,
errors
};
}
// š§ Prop Transformation Methods
transformDivProps(props) {
return props.replace(/className=/g, 'style=');
}
transformTextProps(props) {
return props.replace(/className=/g, 'style=');
}
transformHeadingProps(props) {
let transformed = props.replace(/className=/g, 'style=');
// Add default heading styles
if (!transformed.includes('style=')) {
transformed += ' style={styles.title}';
}
return transformed;
}
transformButtonProps(props) {
let transformed = props
.replace(/onClick=/g, 'onPress=')
.replace(/className=/g, 'style=');
// Add default button props
if (!transformed.includes('activeOpacity')) {
transformed += ' activeOpacity={0.7}';
}
return transformed;
}
transformInputProps(props) {
return props
.replace(/onChange=/g, 'onChangeText=')
.replace(/className=/g, 'style=')
.replace(/type="email"/g, 'keyboardType="email-address"')
.replace(/type="number"/g, 'keyboardType="numeric"')
.replace(/type="password"/g, 'secureTextEntry={true}');
}
transformImageProps(props) {
return props
.replace(/src=/g, 'source=')
.replace(/className=/g, 'style=');
}
transformLinkProps(props) {
return props
.replace(/href=/g, 'onPress={() => navigation.navigate(')
.replace(/className=/g, 'style=');
}
transformListProps(props) {
return props.replace(/className=/g, 'style=');
}
transformListItemProps(props) {
return props.replace(/className=/g, 'style=');
}
transformFormProps(props) {
return props
.replace(/onSubmit=/g, 'onPress=')
.replace(/className=/g, 'style=');
}
transformScrollProps(props) {
let transformed = props.replace(/className=/g, 'style=');
if (!transformed.includes('contentContainerStyle')) {
transformed += ' contentContainerStyle={styles.content}';
}
return transformed;
}
transformSectionProps(props) {
return props.replace(/className=/g, 'style=');
}
getComponentName(fileName) {
const baseName = path.basename(fileName, path.extname(fileName));
let componentName = baseName
.replace(/[^a-zA-Z0-9]/g, '')
.replace(/^./, str => str.toUpperCase());
if (componentName.toLowerCase() === 'page') {
componentName = 'HomeScreen';
} else if (componentName.toLowerCase() === 'index') {
componentName = 'MainScreen';
}
return componentName;
}
}
export default SmartConverter;