rynex
Version:
A minimalist TypeScript framework for building reactive web applications with no virtual DOM
717 lines (670 loc) • 16.3 kB
JavaScript
/**
* Rynex Project Templates
* Template generators for different project types
*/
const ext = (config) => config.useTypeScript ? 'ts' : 'js';
/**
* Package.json template
*/
export const packageJson = (config) => `{
"name": "${config.projectName}",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "rynex dev",
"build": "rynex build"
},
"dependencies": {
"rynex": "^0.1.3"
}${config.useTypeScript ? `,
"devDependencies": {
"typescript": "^5.3.0"
}` : ''}
}`;
/**
* Rynex config
*/
export const rynexConfig = (config) => `export default {
entry: 'src/index.${ext(config)}',
output: 'public/bundle.js',
minify: false,
sourceMaps: true,
port: 3000,
hotReload: true
};`;
/**
* TypeScript config
*/
export const tsConfig = () => `{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM"],
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "public"]
}`;
/**
* README template
*/
export const readme = (config) => `# ${config.projectName}
A Rynex application${config.template !== 'empty' ? ` built with the ${config.template} template` : ''}.
## Getting Started
\`\`\`bash
# Install dependencies
npm install
# Start development server
npm run dev
# Build for production
npm run build
\`\`\`
## Project Structure
\`\`\`
${config.projectName}/
├── src/
│ ├── index.${ext(config)} # Entry point
│ ├── App.${ext(config)} # Root component${config.template === 'routed' ? `
│ ├── components/ # Reusable components
│ └── pages/ # Route pages` : ''}
├── public/
│ ├── index.html # HTML shell
│ └── styles.css # Global styles
└── rynex.config.js # Configuration
\`\`\`
## Learn More
Visit [Rynex Documentation](https://github.com/razen-core/rynex) to learn more.
`;
/**
* HTML template
*/
export const html = (config) => `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>${config.projectName}</title>
${config.template !== 'empty' ? `
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700;800&family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
` : ''}
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="root"></div>
<script type="module" src="bundle.js"></script>
</body>
</html>`;
/**
* CSS template
*/
export const css = (config) => {
if (config.template === 'empty') {
return `/* Global styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}`;
}
return `/* Global styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #000000;
color: #ffffff;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Montserrat', sans-serif;
}
#root {
min-height: 100vh;
}
/* Navigation styles */
.nav-link {
transition: all 0.2s ease-in-out;
}
.nav-link:hover {
background: rgba(255, 255, 255, 0.1);
}
.nav-link.active {
background: rgba(0, 255, 136, 0.2);
color: #00ff88 !important;
font-weight: 600;
}`;
};
/**
* Entry point template
*/
export const indexFile = (config) => `import { render } from 'rynex';
import App from './App.${ext(config) === 'ts' ? 'js' : ext(config)}';
const root = document.getElementById('root');
if (root) {
render(App, root);
}`;
/**
* Empty App template
*/
export const emptyApp = (config) => `import { div, text } from 'rynex';
export default function App() {
return div({
style: {
padding: '2rem',
textAlign: 'center'
}
}, [
text('Hello, Rynex!')
]);
}`;
/**
* Minimal App template (single page with styling)
*/
export const minimalApp = (config) => `import { state, vbox, hbox, container, h1, text, button, svg } from 'rynex';
export default function App() {
const appState = state({
count: 0
});
return vbox({
style: {
minHeight: '100vh',
background: '#000000',
color: '#ffffff',
alignItems: 'center',
justifyContent: 'center',
padding: '2rem',
gap: '2rem'
}
}, [
// Logo Badge
container({
style: {
padding: '0.5rem 1.25rem',
background: '#0a0a0a',
border: '1px solid #333333',
borderRadius: '9999px',
boxShadow: '0 4px 6px -1px rgba(0, 255, 136, 0.1)'
}
}, [
text({
style: {
fontSize: '0.875rem',
color: '#00ff88',
fontWeight: '600',
letterSpacing: '0.05em'
}
}, 'Rynex')
]),
// Title
h1({
style: {
fontSize: '3rem',
fontWeight: '800',
margin: '0',
background: 'linear-gradient(135deg, #ffffff 0%, #b0b0b0 100%)',
webkitBackgroundClip: 'text',
webkitTextFillColor: 'transparent',
backgroundClip: 'text'
}
}, 'Welcome to Rynex'),
// Counter
vbox({
style: {
gap: '1rem',
alignItems: 'center',
padding: '2rem',
background: '#0a0a0a',
border: '1px solid #333333',
borderRadius: '1rem',
minWidth: '300px'
}
}, [
text({
style: {
fontSize: '3rem',
fontWeight: 'bold',
color: '#00ff88'
}
}, () => \`\${appState.count}\`),
hbox({
style: { gap: '1rem' }
}, [
button({
onClick: () => appState.count--,
style: {
padding: '0.75rem 1.5rem',
background: '#333333',
color: '#ffffff',
border: 'none',
borderRadius: '0.5rem',
fontSize: '1rem',
cursor: 'pointer',
transition: 'all 0.2s'
},
onHover: {
background: '#444444'
}
}, '-'),
button({
onClick: () => appState.count++,
style: {
padding: '0.75rem 1.5rem',
background: '#00ff88',
color: '#000000',
border: 'none',
borderRadius: '0.5rem',
fontSize: '1rem',
fontWeight: '600',
cursor: 'pointer',
transition: 'all 0.2s'
},
onHover: {
background: '#00cc6a'
}
}, '+')
])
]),
// Footer
text({
style: {
fontSize: '0.875rem',
color: '#666666',
marginTop: '2rem'
}
}, 'Edit src/App.${ext(config)} to get started')
]);
}`;
/**
* Routed App template
*/
export const routedApp = (config) => `import { state, createRouter, vbox, nav, hbox, h2, NavLink, RouterOutlet, NotFound } from 'rynex';
import HomePage from './pages/home/page.${ext(config) === 'ts' ? 'js' : ext(config)}';
import AboutPage from './pages/about/page.${ext(config) === 'ts' ? 'js' : ext(config)}';
import BlogPage from './pages/blog/page.${ext(config) === 'ts' ? 'js' : ext(config)}';
export default function App() {
// Create router
const router = createRouter([
{
path: '/',
component: () => HomePage(),
name: 'home'
},
{
path: '/about',
component: () => AboutPage(),
name: 'about'
},
{
path: '/blog',
component: () => BlogPage(),
name: 'blog'
}
]);
// 404 handler
router.setNotFound((ctx) => {
return NotFound({
title: '404',
message: \`Page "\${ctx.path}" not found\`,
homeLink: true
});
});
// Navigation component
const Navigation = () => {
return nav({
style: {
padding: '1rem 2rem',
background: '#0a0a0a',
borderBottom: '1px solid #333333',
display: 'flex',
gap: '2rem',
alignItems: 'center'
}
}, [
h2({
style: {
color: '#00ff88',
margin: 0,
fontSize: '1.5rem',
fontWeight: '800'
}
}, 'Rynex'),
hbox({
style: {
gap: '1rem',
flex: 1
}
}, [
NavLink({
href: '/',
activeClass: 'active',
class: 'nav-link',
style: {
color: '#ffffff',
textDecoration: 'none',
padding: '0.5rem 1rem',
borderRadius: '0.5rem'
}
}, 'Home'),
NavLink({
href: '/about',
activeClass: 'active',
class: 'nav-link',
style: {
color: '#ffffff',
textDecoration: 'none',
padding: '0.5rem 1rem',
borderRadius: '0.5rem'
}
}, 'About'),
NavLink({
href: '/blog',
activeClass: 'active',
class: 'nav-link',
style: {
color: '#ffffff',
textDecoration: 'none',
padding: '0.5rem 1rem',
borderRadius: '0.5rem'
}
}, 'Blog')
])
]);
};
// Main layout
return vbox({
style: {
minHeight: '100vh',
background: '#000000'
}
}, [
Navigation(),
RouterOutlet(router)
]);
}`;
/**
* Home page template
*/
export const homePage = (config) => `import { vbox, h1, text, hbox, button } from 'rynex';
export default function HomePage() {
return vbox({
style: {
padding: '3rem 2rem',
maxWidth: '800px',
margin: '0 auto',
gap: '2rem'
}
}, [
vbox({
style: {
textAlign: 'center',
gap: '1rem'
}
}, [
h1({
style: {
fontSize: '3rem',
fontWeight: '800',
background: 'linear-gradient(135deg, #ffffff 0%, #b0b0b0 100%)',
webkitBackgroundClip: 'text',
webkitTextFillColor: 'transparent',
backgroundClip: 'text',
margin: 0
}
}, 'Welcome Home'),
text({
style: {
fontSize: '1.25rem',
color: '#b0b0b0',
lineHeight: '1.6'
}
}, 'A modern, reactive web framework with elegant syntax and powerful features.')
]),
hbox({
style: {
gap: '1rem',
justifyContent: 'center',
marginTop: '2rem'
}
}, [
button({
onClick: () => window.open('https://github.com/razen-core/rynex', '_blank'),
style: {
padding: '0.875rem 2rem',
background: '#00ff88',
color: '#000000',
border: 'none',
borderRadius: '0.5rem',
fontSize: '1rem',
fontWeight: '600',
cursor: 'pointer'
}
}, 'Get Started'),
button({
onClick: () => window.location.href = '/about',
style: {
padding: '0.875rem 2rem',
background: '#333333',
color: '#ffffff',
border: 'none',
borderRadius: '0.5rem',
fontSize: '1rem',
cursor: 'pointer'
}
}, 'Learn More')
])
]);
}`;
/**
* About page template
*/
export const aboutPage = (config) => `import { vbox, h1, h2, text } from 'rynex';
export default function AboutPage() {
return vbox({
style: {
padding: '3rem 2rem',
maxWidth: '800px',
margin: '0 auto',
gap: '2rem'
}
}, [
h1({
style: {
fontSize: '2.5rem',
fontWeight: '800',
color: '#00ff88',
margin: 0
}
}, 'About Rynex'),
vbox({
style: { gap: '1rem' }
}, [
text({
style: {
fontSize: '1.125rem',
color: '#b0b0b0',
lineHeight: '1.8'
}
}, 'Rynex is a minimalist TypeScript framework for building reactive web applications with no Virtual DOM.'),
text({
style: {
fontSize: '1.125rem',
color: '#b0b0b0',
lineHeight: '1.8'
}
}, 'Built with modern web standards, Rynex provides a clean and intuitive API for creating fast, reactive user interfaces.')
]),
vbox({
style: {
gap: '1.5rem',
marginTop: '2rem'
}
}, [
vbox({
style: {
padding: '1.5rem',
background: '#0a0a0a',
border: '1px solid #333333',
borderRadius: '0.5rem',
gap: '0.5rem'
}
}, [
h2({
style: {
fontSize: '1.25rem',
color: '#00ff88',
margin: 0
}
}, 'No Virtual DOM'),
text({
style: {
color: '#b0b0b0'
}
}, 'Direct DOM manipulation for maximum performance')
]),
vbox({
style: {
padding: '1.5rem',
background: '#0a0a0a',
border: '1px solid #333333',
borderRadius: '0.5rem',
gap: '0.5rem'
}
}, [
h2({
style: {
fontSize: '1.25rem',
color: '#00ff88',
margin: 0
}
}, 'Reactive State'),
text({
style: {
color: '#b0b0b0'
}
}, 'Proxy-based reactivity with automatic UI updates')
]),
vbox({
style: {
padding: '1.5rem',
background: '#0a0a0a',
border: '1px solid #333333',
borderRadius: '0.5rem',
gap: '0.5rem'
}
}, [
h2({
style: {
fontSize: '1.25rem',
color: '#00ff88',
margin: 0
}
}, 'TypeScript First'),
text({
style: {
color: '#b0b0b0'
}
}, 'Full type safety out of the box')
])
])
]);
}`;
/**
* Blog page template
*/
export const blogPage = (config) => `import { state, vbox, h1, h2, text, hbox } from 'rynex';
export default function BlogPage() {
const blogState = state({
posts: [
{
id: 1,
title: 'Getting Started with Rynex',
excerpt: 'Learn how to build your first Rynex application',
date: '2024-10-21'
},
{
id: 2,
title: 'Understanding Reactive State',
excerpt: 'Deep dive into Rynex reactive state management',
date: '2024-10-20'
},
{
id: 3,
title: 'Building with Components',
excerpt: 'Best practices for component architecture',
date: '2024-10-19'
}
]
});
return vbox({
style: {
padding: '3rem 2rem',
maxWidth: '800px',
margin: '0 auto',
gap: '2rem'
}
}, [
h1({
style: {
fontSize: '2.5rem',
fontWeight: '800',
color: '#00ff88',
margin: 0
}
}, 'Blog'),
vbox({
style: { gap: '1.5rem' }
}, blogState.posts.map(post =>
vbox({
style: {
padding: '1.5rem',
background: '#0a0a0a',
border: '1px solid #333333',
borderRadius: '0.5rem',
gap: '0.75rem',
cursor: 'pointer',
transition: 'all 0.2s'
},
onHover: {
borderColor: '#00ff88',
transform: 'translateY(-2px)'
}
}, [
h2({
style: {
fontSize: '1.5rem',
color: '#ffffff',
margin: 0
}
}, post.title),
text({
style: {
color: '#b0b0b0',
lineHeight: '1.6'
}
}, post.excerpt),
text({
style: {
fontSize: '0.875rem',
color: '#666666'
}
}, post.date)
])
))
]);
}`;
//# sourceMappingURL=templates.js.map