#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); const args = process.argv.slice(2); const projectName = args[0]; const templateArg = args.find(a => a.startsWith('--template=')); const template = templateArg ? templateArg.split('=')[1] : 'pokemon'; if (!projectName || projectName === '--help' || projectName === '-h') { console.log(` create-strata-compile - Create a new Strata project Usage: npx create-strata-compile [options] Options: --template= Use a specific template (default: pokemon) Available: pokemon, minimal Examples: npx create-strata-compile my-app npx create-strata-compile my-app --template=minimal CLI Generator Commands (after project creation): strata-compile g component Create component with all files strata-compile g page Create page with all files strata-compile g service Create service file strata-compile g api Create API contract file strata-compile g util Create utility file strata-compile g store Create store definition File Structure: .strata → Template (HTML structure only) .compiler.sts → Build-time execution (variable definitions) .service.sts → Logic layer (API calls, business logic) .api.sts → API contracts (declarative endpoints) .sts → Pure logic (utilities, helpers) .scss → Scoped styles Template Syntax: { variable } → Variable interpolation { s-for item in items } → Loop (with { /s-for } closing) { s-for item, i in arr } → Loop with index { s-if condition } → Conditional { s-elif condition } → Else-if branch { s-else } → Else branch { /s-if } → Close conditional { s-imp "@path/file" } → Import component `); process.exit(0); } const projectPath = path.resolve(process.cwd(), projectName); if (fs.existsSync(projectPath)) { console.error(`Error: Directory "${projectName}" already exists.`); process.exit(1); } console.log(` Creating Strata project: ${projectName} Template: ${template} `); // Create project structure const dirs = [ '', 'src', 'src/components', 'src/pages', 'src/pages/index', // Index page directory 'src/pages/pokemon', // Pokemon example page 'src/services', 'src/api', 'src/stores', 'src/utils', 'src/assets', 'src/assets/styles', 'public', '.vscode', ]; for (const dir of dirs) { fs.mkdirSync(path.join(projectPath, dir), { recursive: true }); } // Create package.json const packageJson = { name: projectName, version: '0.1.0', private: true, scripts: { dev: '~/.strata/bin/strata-compile dev', build: '~/.strata/bin/strata-compile build', preview: '~/.strata/bin/strata-compile preview', }, devDependencies: { typescript: '^5.3.0', }, }; fs.writeFileSync( path.join(projectPath, 'package.json'), JSON.stringify(packageJson, null, 2) ); // Create strataconfig.ts const strataConfig = `import { defineConfig } from 'strata'; export default defineConfig({ app: { title: '${projectName}', description: 'A Strata application with Pokemon API example', }, // API configuration api: { baseUrl: process.env.API_URL || 'https://pokeapi.co/api/v2', cache: { enabled: true, ttl: '5m', // 5 minute cache }, }, // State management state: { encrypt: false, // Encrypt sensitive state fields persist: true, // Persist state to localStorage }, // Build options build: { static: true, // Static-first compilation minify: true, sourceMaps: false, }, }); `; fs.writeFileSync(path.join(projectPath, 'strataconfig.ts'), strataConfig); // ============================================================ // INDEX PAGE - Welcome page with all syntax examples // ============================================================ const indexStrata = ` `; const indexCompiler = `// index.compiler.sts - Build-time execution layer // All values resolved at compile time → pure static HTML output // Static values - resolved during build export const title = '${projectName}'; export const subtitle = 'Static Template Rendering Architecture'; export const version = '1.0.0'; // Environment flags - evaluated at build time export const isProduction = false; export const isDevelopment = true; // Feature list - demonstrates s-for loop export const features = [ { icon: '⚡', name: 'Static Compilation', description: 'Templates compiled to pure HTML at build time' }, { icon: '🔄', name: 'Build-time Data Fetching', description: 'API calls executed during compilation' }, { icon: '📦', name: 'Zero Runtime Overhead', description: 'No framework JavaScript in production' }, { icon: '🎯', name: 'Type-Safe Templates', description: 'Full TypeScript support for data and logic' }, { icon: '🔒', name: 'Encrypted State', description: 'Optional encryption for sensitive client state' }, { icon: '🌐', name: 'Cross-Tab Sync', description: 'Shared Worker for tab coordination' } ]; // Statistics with index example export const stats = [ { value: '< 5KB', label: 'Runtime Size' }, { value: '100ms', label: 'Build Time' }, { value: '0', label: 'Runtime Dependencies' }, { value: '∞', label: 'Possibilities' } ]; export default { title, subtitle, version, isProduction, isDevelopment, features, stats }; `; const indexService = `// index.service.sts - Logic layer // This file is for runtime interactions (if needed) // For static pages, most logic happens in .compiler.sts export default {}; `; const indexScss = `.index-page { max-width: 1200px; margin: 0 auto; padding: 2rem; } .hero { text-align: center; padding: 4rem 2rem; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 1rem; color: white; margin-bottom: 3rem; h1 { font-size: 3rem; margin-bottom: 1rem; } .subtitle { font-size: 1.25rem; opacity: 0.9; } } .features { margin-bottom: 3rem; h2 { text-align: center; margin-bottom: 2rem; } } .feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; } .feature-card { background: white; border: 1px solid #e5e7eb; border-radius: 0.75rem; padding: 1.5rem; transition: transform 0.2s, box-shadow 0.2s; &:hover { transform: translateY(-2px); box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1); } .icon { font-size: 2rem; display: block; margin-bottom: 0.75rem; } h3 { margin-bottom: 0.5rem; color: #1f2937; } p { color: #6b7280; font-size: 0.875rem; } } .status { text-align: center; margin-bottom: 3rem; .badge { display: inline-block; padding: 0.5rem 1rem; border-radius: 9999px; font-size: 0.875rem; font-weight: 500; &.production { background: #dcfce7; color: #166534; } &.development { background: #fef3c7; color: #92400e; } &.unknown { background: #f3f4f6; color: #374151; } } } .stats { margin-bottom: 3rem; h2 { text-align: center; margin-bottom: 2rem; } } .stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; @media (max-width: 768px) { grid-template-columns: repeat(2, 1fr); } } .stat-card { background: #f9fafb; border-radius: 0.75rem; padding: 1.5rem; text-align: center; display: flex; flex-direction: column; gap: 0.25rem; .stat-index { font-size: 0.75rem; color: #9ca3af; } .stat-value { font-size: 2rem; font-weight: 700; color: #667eea; } .stat-label { font-size: 0.875rem; color: #6b7280; } } .cta { text-align: center; padding: 3rem; background: #f9fafb; border-radius: 1rem; margin-bottom: 3rem; h2 { margin-bottom: 0.5rem; } p { color: #6b7280; margin-bottom: 1.5rem; } .btn { display: inline-block; background: #667eea; color: white; padding: 0.75rem 2rem; border-radius: 0.5rem; text-decoration: none; font-weight: 500; transition: background 0.2s; &:hover { background: #5a67d8; } } } footer { text-align: center; padding: 2rem; color: #9ca3af; font-size: 0.875rem; .version { margin-top: 0.5rem; font-family: monospace; } } `; // Write index page files fs.writeFileSync(path.join(projectPath, 'src/pages/index/index.strata'), indexStrata); fs.writeFileSync(path.join(projectPath, 'src/pages/index/index.compiler.sts'), indexCompiler); fs.writeFileSync(path.join(projectPath, 'src/pages/index/index.service.sts'), indexService); fs.writeFileSync(path.join(projectPath, 'src/pages/index/index.scss'), indexScss); // ============================================================ // POKEMON PAGE - API fetching at build time example // ============================================================ const pokemonStrata = ` `; const pokemonCompiler = `// pokemon.compiler.sts - Build-time data fetching // This file runs during compilation to fetch Pokemon data // Page metadata export const title = 'Pokemon Gallery'; export const description = 'Pokemon data fetched at build time from PokeAPI'; // Fetch Pokemon list at build time // The static compiler will execute this during build export const pokemons = await fetchPokemonList(); // Computed values export const totalCount = pokemons.length; export const hasPokemons = pokemons.length > 0; // Build-time fetch function async function fetchPokemonList() { // This runs at compile time, not in browser // The compiler has a built-in fetch capability // For demonstration, return static data // In real build, the compiler fetches from: https://pokeapi.co/api/v2/pokemon?limit=20 return [ { id: 1, name: 'Bulbasaur', sprite: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png', types: ['grass', 'poison'], stats: { hp: 45, attack: 49, defense: 49 } }, { id: 4, name: 'Charmander', sprite: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/4.png', types: ['fire'], stats: { hp: 39, attack: 52, defense: 43 } }, { id: 7, name: 'Squirtle', sprite: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/7.png', types: ['water'], stats: { hp: 44, attack: 48, defense: 65 } }, { id: 25, name: 'Pikachu', sprite: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/25.png', types: ['electric'], stats: { hp: 35, attack: 55, defense: 40 } }, { id: 39, name: 'Jigglypuff', sprite: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/39.png', types: ['normal', 'fairy'], stats: { hp: 115, attack: 45, defense: 20 } }, { id: 52, name: 'Meowth', sprite: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/52.png', types: ['normal'], stats: { hp: 40, attack: 45, defense: 35 } }, { id: 133, name: 'Eevee', sprite: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/133.png', types: ['normal'], stats: { hp: 55, attack: 55, defense: 50 } }, { id: 143, name: 'Snorlax', sprite: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/143.png', types: ['normal'], stats: { hp: 160, attack: 110, defense: 65 } } ]; } export default { title, description, pokemons, totalCount, hasPokemons }; `; const pokemonService = `// pokemon.service.sts - Runtime logic (if needed) // For static pages, this file is typically empty // Use for runtime interactions like search filtering export default {}; `; const pokemonScss = `.pokemon-page { max-width: 1200px; margin: 0 auto; padding: 2rem; } header { text-align: center; margin-bottom: 2rem; h1 { font-size: 2.5rem; margin-bottom: 0.5rem; color: #1f2937; } p { color: #6b7280; } .meta { font-family: monospace; background: #f3f4f6; padding: 0.5rem 1rem; border-radius: 0.5rem; display: inline-block; margin-top: 1rem; } } .search-info { background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 0.75rem; padding: 1rem 1.5rem; margin-bottom: 2rem; text-align: center; p { color: #1e40af; margin: 0.25rem 0; } } .pokemon-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1.5rem; margin-bottom: 2rem; } .pokemon-card { background: white; border: 1px solid #e5e7eb; border-radius: 1rem; padding: 1.5rem; text-align: center; transition: transform 0.2s, box-shadow 0.2s; &:hover { transform: translateY(-4px); box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15); } .pokemon-number { font-size: 0.75rem; color: #9ca3af; font-family: monospace; } img { width: 96px; height: 96px; margin: 0.5rem 0; image-rendering: pixelated; } h3 { text-transform: capitalize; margin-bottom: 0.5rem; color: #1f2937; } .pokemon-types { display: flex; gap: 0.25rem; justify-content: center; flex-wrap: wrap; margin-bottom: 0.75rem; } .type-badge { padding: 0.25rem 0.75rem; border-radius: 9999px; font-size: 0.75rem; text-transform: capitalize; color: white; &.grass { background: #22c55e; } &.poison { background: #a855f7; } &.fire { background: #f97316; } &.water { background: #3b82f6; } &.electric { background: #eab308; color: #1f2937; } &.normal { background: #a8a29e; } &.fairy { background: #ec4899; } } .pokemon-stats { display: flex; justify-content: space-around; padding-top: 0.75rem; border-top: 1px solid #e5e7eb; .stat { display: flex; flex-direction: column; font-size: 0.75rem; span:first-child { color: #9ca3af; font-weight: 500; } span:last-child { font-weight: 700; color: #1f2937; } } } } .no-results { text-align: center; padding: 3rem; background: #fef2f2; border-radius: 0.75rem; color: #991b1b; } .back-nav { text-align: center; padding: 2rem; a { color: #667eea; text-decoration: none; font-weight: 500; &:hover { text-decoration: underline; } } } `; // Write Pokemon page files fs.writeFileSync(path.join(projectPath, 'src/pages/pokemon/pokemon.strata'), pokemonStrata); fs.writeFileSync(path.join(projectPath, 'src/pages/pokemon/pokemon.compiler.sts'), pokemonCompiler); fs.writeFileSync(path.join(projectPath, 'src/pages/pokemon/pokemon.service.sts'), pokemonService); fs.writeFileSync(path.join(projectPath, 'src/pages/pokemon/pokemon.scss'), pokemonScss); // ============================================================ // API CONTRACT EXAMPLE // ============================================================ const pokemonApi = `// pokemon.api.sts - API contracts (declarative, dual-mode) // Describe API endpoints once, executable in compiler and runtime mode import { defineApi } from 'strata'; /** * Get Pokemon list */ export const getPokemonListApi = defineApi({ path: 'https://pokeapi.co/api/v2/pokemon', method: 'GET', cache: '1h', // Cache for 1 hour params: { limit: 20, offset: 0, }, }); /** * Get single Pokemon by ID or name */ export const getPokemonApi = defineApi({ path: 'https://pokeapi.co/api/v2/pokemon/:id', method: 'GET', cache: '1h', }); /** * Get Pokemon species (for evolution data) */ export const getPokemonSpeciesApi = defineApi({ path: 'https://pokeapi.co/api/v2/pokemon-species/:id', method: 'GET', cache: '1h', }); export default { getPokemonListApi, getPokemonApi, getPokemonSpeciesApi, }; `; fs.writeFileSync(path.join(projectPath, 'src/api/pokemon.api.sts'), pokemonApi); // ============================================================ // GLOBAL STYLES // ============================================================ const globalStyles = `// Global styles @import 'variables'; * { box-sizing: border-box; margin: 0; padding: 0; } html { font-size: 16px; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: var(--text-primary); line-height: 1.6; background: var(--bg-primary); min-height: 100vh; } #app { min-height: 100vh; } a { color: var(--primary); } img { max-width: 100%; height: auto; } `; const variables = `// Design tokens :root { // Colors --primary: #667eea; --primary-dark: #5a67d8; --secondary: #764ba2; // Text --text-primary: #1f2937; --text-secondary: #6b7280; --text-muted: #9ca3af; // Backgrounds --bg-primary: #ffffff; --bg-secondary: #f9fafb; --bg-tertiary: #f3f4f6; // Borders --border-color: #e5e7eb; // Shadows --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); --shadow-lg: 0 10px 40px rgba(0, 0, 0, 0.1); } `; fs.writeFileSync(path.join(projectPath, 'src/assets/styles/global.scss'), globalStyles); fs.writeFileSync(path.join(projectPath, 'src/assets/styles/_variables.scss'), variables); // ============================================================ // UTILITY EXAMPLE // ============================================================ const formatUtil = `// format.sts - Pure utility functions // No side effects. Deterministic output. /** * Capitalize first letter of a string */ export function capitalize(str) { if (!str) return ''; return str.charAt(0).toUpperCase() + str.slice(1); } /** * Format number with commas */ export function formatNumber(num) { return num.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ','); } /** * Truncate string with ellipsis */ export function truncate(str, length = 50) { if (!str || str.length <= length) return str; return str.slice(0, length) + '...'; } export default { capitalize, formatNumber, truncate }; `; fs.writeFileSync(path.join(projectPath, 'src/utils/format.sts'), formatUtil); // ============================================================ // CONFIGURATION FILES // ============================================================ const gitignore = `node_modules dist .strata *.log .DS_Store .env .env.local `; const vscodeSettings = { "files.associations": { "*.strata": "html", "*.sts": "typescript", "*.compiler.sts": "typescript", "*.service.sts": "typescript", "*.api.sts": "typescript" }, "emmet.includeLanguages": { "strata": "html" }, "editor.defaultFormatter": "esbenp.prettier-vscode", "typescript.tsdk": "node_modules/typescript/lib" }; const nvimConfig = `-- Strata file type detection for NeoVim vim.filetype.add({ extension = { strata = "html", sts = "typescript", }, pattern = { [".*%.compiler%.sts"] = "typescript", [".*%.service%.sts"] = "typescript", [".*%.api%.sts"] = "typescript", }, }) `; fs.writeFileSync(path.join(projectPath, '.gitignore'), gitignore); fs.writeFileSync(path.join(projectPath, '.vscode/settings.json'), JSON.stringify(vscodeSettings, null, 2)); fs.writeFileSync(path.join(projectPath, '.nvim.lua'), nvimConfig); // ============================================================ // README // ============================================================ const readme = `# ${projectName} A Strata application with Pokemon API example. ## Getting Started \`\`\`bash npm install npm run dev \`\`\` ## Project Structure \`\`\` src/ ├── pages/ │ ├── index/ # Home page │ │ ├── index.strata # Template │ │ ├── index.compiler.sts # Build-time data │ │ ├── index.service.sts # Runtime logic │ │ └── index.scss # Styles │ └── pokemon/ # Pokemon example │ ├── pokemon.strata │ ├── pokemon.compiler.sts │ ├── pokemon.service.sts │ └── pokemon.scss ├── api/ # API contracts ├── services/ # Business logic ├── utils/ # Pure utilities └── assets/ └── styles/ \`\`\` ## CLI Commands ### Generate Components \`\`\`bash strata-compile g component UserCard # Create component strata-compile g page users # Create page strata-compile g service user # Create service strata-compile g api user # Create API contract strata-compile g util format # Create utility strata-compile g store user # Create store \`\`\` ## Template Syntax | Syntax | Description | |--------|-------------| | \`{ variable }\` | Variable interpolation | | \`{ s-for item in items }\` | Loop | | \`{ s-for item, i in arr }\` | Loop with index | | \`{ s-if condition }\` | Conditional | | \`{ s-elif condition }\` | Else-if | | \`{ s-else }\` | Else | | \`{ /s-if }\`, \`{ /s-for }\` | Close block | ## File Types | Extension | Purpose | |-----------|---------| | \`.strata\` | Template (HTML structure) | | \`.compiler.sts\` | Build-time data & logic | | \`.service.sts\` | Runtime logic | | \`.api.sts\` | API contracts | | \`.sts\` | Pure utilities | | \`.scss\` | Scoped styles | ## Learn More - [Strata Documentation](https://strata.dev) `; fs.writeFileSync(path.join(projectPath, 'README.md'), readme); // Initialize git repository try { execSync('git init', { cwd: projectPath, stdio: 'ignore' }); execSync('git add .', { cwd: projectPath, stdio: 'ignore' }); execSync('git commit -m "Initial commit - Strata project with Pokemon example"', { cwd: projectPath, stdio: 'ignore', env: { ...process.env, GIT_AUTHOR_NAME: 'Strata', GIT_AUTHOR_EMAIL: 'strata@localhost', GIT_COMMITTER_NAME: 'Strata', GIT_COMMITTER_EMAIL: 'strata@localhost' } }); console.log(' ✓ Git repository initialized'); } catch (err) { console.log(' ⚠ Could not initialize git repository'); } console.log(` ✓ Project created successfully! Project structure: src/pages/index/ - Home page with feature examples src/pages/pokemon/ - Pokemon API example (build-time fetch) src/api/ - API contracts src/utils/ - Pure utility functions Template syntax examples included: { variable } - Variable interpolation { s-for } - Loop with items { s-for item, i } - Loop with index { s-if/s-elif/s-else } - Conditionals Next steps: cd ${projectName} npm install npm run dev CLI generator commands: strata-compile g component - Create component strata-compile g page - Create page strata-compile g service - Create service strata-compile g api - Create API contract strata-compile g util - Create utility Happy coding with Strata! `);