zura-stack-native
Version:
A comprehensive React Native CLI project generator with production-ready setup
249 lines (218 loc) • 6.82 kB
JavaScript
const fs = require('fs-extra');
const path = require('path');
const chalk = require('chalk');
class ConfigGenerator {
constructor(config) {
this.config = config;
}
async generateAll() {
try {
await this.generateBabelConfig();
await this.generateTypeScriptConfig();
await this.generateTailwindConfig();
await this.generateMetroConfig();
await this.generateESLintConfig();
if (this.config.testing) {
await this.generateJestConfig();
await this.generateJestSetup();
}
} catch (error) {
throw new Error(`Configuration generation failed: ${error.message}`);
}
}
async generateBabelConfig() {
const content = `module.exports = {
presets: ['@react-native/babel-preset'],
plugins: [
'react-native-reanimated/plugin',
[
'module-resolver',
{
root: ['./src'],
extensions: ['.ios.js', '.android.js', '.js', '.ts', '.tsx', '.json'],
alias: {
'@': './src',
'@components': './src/components',
'@screens': './src/screens',
'@utils': './src/utils',
'@store': './src/store',
'@hooks': './src/hooks',
},
},
],
],
};`;
await fs.writeFile(path.join(this.config.projectPath, 'babel.config.js'), content);
}
async generateTypeScriptConfig() {
const content = `{
"extends": "@react-native/typescript-config/tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@screens/*": ["src/screens/*"],
"@utils/*": ["src/utils/*"],
"@store/*": ["src/store/*"],
"@hooks/*": ["src/hooks/*"]
},
"types": ["jest", "node"]
},
"include": ["src/**/*", "__tests__/**/*", "e2e/**/*"],
"exclude": ["node_modules", "babel.config.js", "metro.config.js", "jest.config.js"]
}`;
await fs.writeFile(path.join(this.config.projectPath, 'tsconfig.json'), content);
}
async generateTailwindConfig() {
const content = `/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./App.{js,jsx,ts,tsx}", "./src/**/*.{js,jsx,ts,tsx}"],
theme: {
extend: {},
},
plugins: [],
}`;
await fs.writeFile(path.join(this.config.projectPath, 'tailwind.config.js'), content);
}
async generateMetroConfig() {
const content = `const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
const config = {};
module.exports = mergeConfig(getDefaultConfig(__dirname), config);`;
await fs.writeFile(path.join(this.config.projectPath, 'metro.config.js'), content);
}
async generateESLintConfig() {
const content = `module.exports = {
root: true,
extends: '@react-native',
rules: {
'prettier/prettier': 'off',
},
};`;
await fs.writeFile(path.join(this.config.projectPath, '.eslintrc.js'), content);
}
async generateJestConfig() {
const content = `module.exports = {
preset: 'react-native',
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
transformIgnorePatterns: [
'node_modules/(?!(react-native|@react-native|@react-navigation|react-native-reanimated|react-native-safe-area-context|react-native-screens|@react-native-community|react-native-vector-icons|react-native-svg|react-native-linear-gradient|@react-native-masked-view|react-native-responsive-screen|react-native-responsive-fontsize|lucide-react-native)/)',
],
testEnvironment: 'jsdom',
testMatch: [
'<rootDir>/src/**/__tests__/**/*.(ts|tsx|js)',
'<rootDir>/src/**/?(*.)(spec|test).(ts|tsx|js)',
'<rootDir>/__tests__/**/*.(ts|tsx|js)',
],
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
'!src/**/index.ts',
'!src/**/*.stories.{ts,tsx}',
],
coverageThreshold: {
global: {
branches: 70,
functions: 70,
lines: 70,
statements: 70,
},
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'^@/components/(.*)$': '<rootDir>/src/components/$1',
'^@/screens/(.*)$': '<rootDir>/src/screens/$1',
'^@/utils/(.*)$': '<rootDir>/src/utils/$1',
'^@/store/(.*)$': '<rootDir>/src/store/$1',
'^@/hooks/(.*)$': '<rootDir>/src/hooks/$1',
},
};`;
await fs.writeFile(path.join(this.config.projectPath, 'jest.config.js'), content);
}
async generateJestSetup() {
const content = `import '@testing-library/jest-native/extend-expect';
// Mock react-native-reanimated
jest.mock('react-native-reanimated', () => {
const Reanimated = require('react-native-reanimated/mock');
Reanimated.default.call = () => {};
return Reanimated;
});
// Mock react-native-safe-area-context
jest.mock('react-native-safe-area-context', () => ({
SafeAreaProvider: ({ children }) => children,
SafeAreaView: ({ children }) => children,
useSafeAreaInsets: () => ({
top: 0,
right: 0,
bottom: 0,
left: 0,
}),
}));
// Mock react-native-responsive-screen
jest.mock('react-native-responsive-screen', () => ({
widthPercentageToDP: jest.fn((width) => width),
heightPercentageToDP: jest.fn((height) => height),
}));
// Mock react-native-responsive-fontsize
jest.mock('react-native-responsive-fontsize', () => jest.fn((size) => size));
// Mock lucide-react-native
jest.mock('lucide-react-native', () => ({
Home: 'Home',
User: 'User',
Settings: 'Settings',
Bell: 'Bell',
Plus: 'Plus',
Minus: 'Minus',
RotateCcw: 'RotateCcw',
LogOut: 'LogOut',
Mail: 'Mail',
Hash: 'Hash',
Moon: 'Moon',
Sun: 'Sun',
Globe: 'Globe',
Smartphone: 'Smartphone',
MessageCircle: 'MessageCircle',
}));
// Mock Dimensions
jest.mock('react-native/Libraries/Utilities/Dimensions', () => ({
get: jest.fn().mockReturnValue({
width: 375,
height: 812,
}),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
}));
// Mock AsyncStorage
jest.mock('@react-native-async-storage/async-storage', () =>
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
);
// Mock NativeWind
jest.mock('nativewind', () => ({
styled: jest.fn((component) => component),
useColorScheme: jest.fn(() => 'light'),
}));
// Mock react-native-css-interop
jest.mock('react-native-css-interop', () => ({
vars: jest.fn(() => ({})),
}));
// Global test utilities
global.console = {
...console,
// Uncomment to ignore a specific log level
// log: jest.fn(),
// debug: jest.fn(),
// info: jest.fn(),
// warn: jest.fn(),
// error: jest.fn(),
};`;
await fs.writeFile(path.join(this.config.projectPath, 'jest.setup.js'), content);
}
}
module.exports = { ConfigGenerator };