react-theme-system
Version:
A comprehensive React theme management system that enforces consistency, supports dark/light mode, and eliminates hardcoded styles
204 lines (183 loc) • 5.64 kB
JavaScript
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
function generateTypesFromTheme() {
try {
const currentDir = process.cwd();
const themePath = path.join(currentDir, 'theme.config.js');
if (!fs.existsSync(themePath)) {
console.log('⚠️ No theme.config.js found, skipping type generation');
return;
}
// Read theme config - handle both require and direct file reading
let themeConfig;
try {
// Try to require the theme config
delete require.cache[require.resolve(themePath)];
themeConfig = require(themePath);
} catch (error) {
// If require fails, try to read and parse the file manually
const themeContent = fs.readFileSync(themePath, 'utf8');
// Simple regex to extract the theme object
const themeMatch = themeContent.match(/module\.exports\s*=\s*({[\s\S]*});?\s*$/);
if (themeMatch) {
try {
// Remove the module.exports wrapper and evaluate
const themeString = themeMatch[1];
themeConfig = eval(`(${themeString})`);
} catch (evalError) {
console.log('⚠️ Could not parse theme.config.js, using default structure');
themeConfig = {
light: { colors: {}, spacing: {}, typography: {}, shadows: {}, borderRadius: {}, breakpoints: {}, transitions: {}, zIndex: {} },
dark: { colors: {}, spacing: {}, typography: {}, shadows: {}, borderRadius: {}, breakpoints: {}, transitions: {}, zIndex: {} }
};
}
} else {
console.log('⚠️ Could not parse theme.config.js, using default structure');
themeConfig = {
light: { colors: {}, spacing: {}, typography: {}, shadows: {}, borderRadius: {}, breakpoints: {}, transitions: {}, zIndex: {} },
dark: { colors: {}, spacing: {}, typography: {}, shadows: {}, borderRadius: {}, breakpoints: {}, transitions: {}, zIndex: {} }
};
}
}
// Generate TypeScript definitions
const typeDefinitions = `// Auto-generated TypeScript definitions from theme.config.js
// This file is generated automatically - do not edit manually
export interface CustomTheme {
colors: {
primary: string;
secondary: string;
success: string;
warning: string;
error: string;
info: string;
background: string;
surface: string;
text: string;
textSecondary: string;
border: string;
divider: string;
};
spacing: {
xs: string;
sm: string;
md: string;
lg: string;
xl: string;
xxl: string;
};
typography: {
fontFamily: {
primary: string;
secondary: string;
mono: string;
};
fontSize: {
xs: string;
sm: string;
base: string;
lg: string;
xl: string;
'2xl': string;
'3xl': string;
};
fontWeight: {
light: number;
normal: number;
medium: number;
semibold: number;
bold: number;
};
lineHeight: {
tight: string;
normal: string;
relaxed: string;
};
};
shadows: {
sm: string;
md: string;
lg: string;
xl: string;
};
borderRadius: {
none: string;
sm: string;
md: string;
lg: string;
xl: string;
full: string;
};
breakpoints: {
sm: string;
md: string;
lg: string;
xl: string;
'2xl': string;
};
transitions: {
fast: string;
normal: string;
slow: string;
};
zIndex: {
dropdown: number;
sticky: number;
fixed: number;
modal: number;
popover: number;
tooltip: number;
};
}
export interface CustomThemeConfig {
light: CustomTheme;
dark: CustomTheme;
}
// Theme values for type checking
export const customTheme: CustomThemeConfig = ${JSON.stringify(themeConfig, null, 2)};
// Type-safe theme access helpers
export type CustomColorToken = keyof CustomTheme['colors'];
export type CustomSpacingToken = keyof CustomTheme['spacing'];
export type CustomTypographyToken = keyof CustomTheme['typography']['fontSize'];
export type CustomShadowToken = keyof CustomTheme['shadows'];
export type CustomBorderRadiusToken = keyof CustomTheme['borderRadius'];
`;
// Write type definitions
const typesPath = path.join(currentDir, 'theme.types.ts');
fs.writeFileSync(typesPath, typeDefinitions);
console.log('✨ Generated theme.types.ts with TypeScript definitions');
// Create theme provider wrapper
const providerWrapper = `import React from 'react';
import { ThemeProvider as BaseThemeProvider, ThemeConfig as BaseThemeConfig } from 'react-theme-system';
import { customTheme, CustomThemeConfig } from './theme.types';
interface CustomThemeProviderProps {
children: React.ReactNode;
defaultTheme?: 'light' | 'dark';
customThemes?: CustomThemeConfig;
}
export const CustomThemeProvider: React.FC<CustomThemeProviderProps> = ({
children,
defaultTheme = 'light',
customThemes = customTheme
}) => {
return (
<BaseThemeProvider themes={customThemes} defaultTheme={defaultTheme}>
{children}
</BaseThemeProvider>
);
};
export { useTheme } from 'react-theme-system';
export type { CustomTheme, CustomThemeConfig };
`;
const providerPath = path.join(currentDir, 'CustomThemeProvider.tsx');
fs.writeFileSync(providerPath, providerWrapper);
console.log('✨ Generated CustomThemeProvider.tsx wrapper');
} catch (error) {
console.error('❌ Error generating types:', error.message);
}
}
// Run if called directly
if (require.main === module) {
generateTypesFromTheme();
}
module.exports = { generateTypesFromTheme };