UNPKG

create-nova-expo-template

Version:

A template for creating a new React Native app using Expo and TypeScript, with a focus on performance and best practices.

171 lines (141 loc) 5.7 kB
/* eslint-disable */ /* ------------------------------------------------------------------------------ generate-svg.js ------------------------------------------------------------------------------ This script automates the process of converting SVG files into React Native icon components using SVGR, and updates the icon list for easy imports. WHEN TO USE: - Run this script whenever you add new SVG files to ./assets/svgs. - It will generate corresponding .tsx icon components in ./assets/icons. - It will also update ./components/atoms/Icon/list.ts to include the new icons. HOW TO USE: 1. Place your SVG files in the ./assets/svgs directory. 2. In your terminal, run: node template/scripts/generate-svg.js 3. The script will: - Convert each SVG to a React Native component using SVGR. - Format and clean up the generated component. - Update the icon list for easy usage in your project. NOTES: - The script overwrites existing icon components and updates the icon list. - If you want to keep original SVGs, comment/uncomment the unlinkSync line (line 99). - Make sure your SVG filenames are unique and use kebab-case (e.g., my-icon.svg). - The generated components follow the IconProps interface for consistency. ------------------------------------------------------------------------------ */ const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); const svgFolder = './assets/svgs'; const outputFolder = './assets/icons'; const iconListFile = './components/atoms/Icon/list.ts'; // Create output folder if it doesn't exist if (!fs.existsSync(outputFolder)) { fs.mkdirSync(outputFolder, { recursive: true }); } const newComponents = []; const modifyComponent = (filePath, componentName) => { let content = fs.readFileSync(filePath, 'utf8'); // 1. Fix the import/export naming consistency const pascalName = componentName .split('-') .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join(''); // 2. Clean up and format the content const paths = content.match(/<Path[^>]*\/>/g)?.join('\n ') || ''; content = `import { IconProps } from '@/components/atoms/Icon/types'; import { COLORS } from '@/constants/Colors'; import { Appearance } from 'react-native'; import Svg, { Path } from 'react-native-svg'; const ${pascalName} = ({ size = 24, color = 'primary', ...props }: IconProps) => { const theme = Appearance.getColorScheme(); const targetTheme = (theme === 'unspecified' || !theme) ? 'light' : theme; const targetColor = COLORS[targetTheme][color] return ( <Svg width={size} height={size} viewBox="0 0 24 24" fill="none" color={targetColor} {...props}> ${paths .replace(/stroke="(?!none)[^"]+"/g, 'stroke={targetColor}') .replace(/fill="(?!none)[^"]+"/g, 'fill={targetColor}')} </Svg> ) }; export default ${pascalName}; `; fs.writeFileSync(filePath, content); }; const generatePascalCase = (name) => { return name .split('-') .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join(''); }; // Process SVG files fs.readdirSync(svgFolder).forEach((file) => { if (path.extname(file) === '.svg') { const componentName = path .basename(file, '.svg') .toLowerCase() .replace(/^\w/, (c) => c.toUpperCase()); const outputFile = path.join(outputFolder, `${componentName}.tsx`); try { // 1. Generate component execSync( `npx @svgr/cli --native --icon --no-dimensions --no-svgo ${path.join( svgFolder, file )} > ${outputFile}`, { stdio: 'pipe' } ); // 2. Modify component modifyComponent(outputFile, componentName); // 3. Delete original SVG after successful conversion // fs.unlinkSync(svgFilePath); console.log(`✅ Generated ${componentName}`); newComponents.push(componentName); } catch (error) { console.error(`❌ Failed to process ${file}:`, error?.message); } } }); // Rest of your original file handling the iconList updates... if (newComponents.length > 0) { try { let content = fs.readFileSync(iconListFile, 'utf8'); const newImports = newComponents .filter( (name) => !content.includes(`import ${generatePascalCase(name)} from '@/assets/icons/${name}';`) ) .map((name) => `import ${generatePascalCase(name)} from '@/assets/icons/${name}';`) .join('\n'); const lastBraceIndex = content.lastIndexOf('}'); if (lastBraceIndex === -1) { throw new Error('Could not find iconsList object'); } const newEntries = newComponents .filter( (name) => !content.includes( `${generatePascalCase(name).charAt(0).toLowerCase() + generatePascalCase(name).slice(1)}: ${generatePascalCase(name)},` ) ) .map( (name) => ` ${generatePascalCase(name).charAt(0).toLowerCase() + generatePascalCase(name).slice(1)}: ${generatePascalCase(name)},` ) .join('\n'); const updatedContent = newImports + '\n' + content.slice(0, lastBraceIndex).trimEnd() + '\n' + newEntries + '\n' + content.slice(lastBraceIndex); fs.writeFileSync(iconListFile, updatedContent); console.log('✅ Updated iconsList with new component'); } catch (error) { console.error('❌ Failed to update iconsList:', error?.message); } }