veko
Version:
Ultra-lightweight Node.js framework with ZERO dependencies - VSV components, Tailwind CSS, asset imports, SSR
336 lines (288 loc) • 9.73 kB
JavaScript
/**
* Veko.js CLI - Zero Dependencies
*/
const fs = require('fs');
const path = require('path');
const { spawn } = require('child_process');
// Colors (ANSI)
const colors = {
reset: '\x1b[0m',
cyan: '\x1b[36m',
green: '\x1b[32m',
yellow: '\x1b[33m',
red: '\x1b[31m',
gray: '\x1b[90m',
bold: '\x1b[1m'
};
const c = (color, text) => `${colors[color]}${text}${colors.reset}`;
// Get version
let version = '1.2.18';
try {
const pkg = require('../package.json');
version = pkg.version;
} catch {}
// Logo
function showLogo() {
console.log();
console.log(c('cyan', ' ██╗ ██╗███████╗██╗ ██╗ ██████╗ '));
console.log(c('cyan', ' ██║ ██║██╔════╝██║ ██╔╝██╔═══██╗'));
console.log(c('cyan', ' ██║ ██║█████╗ █████╔╝ ██║ ██║'));
console.log(c('cyan', ' ╚██╗ ██╔╝██╔══╝ ██╔═██╗ ██║ ██║'));
console.log(c('cyan', ' ╚████╔╝ ███████╗██║ ██╗╚██████╔╝'));
console.log(c('cyan', ' ╚═══╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ '));
console.log();
console.log(c('gray', ` Zero-dependency Node.js framework v${version}`));
console.log();
}
// Help
function showHelp() {
showLogo();
console.log(c('bold', 'Usage:'));
console.log(' veko <command> [options]');
console.log();
console.log(c('bold', 'Commands:'));
console.log(' init [name] Create a new Veko project');
console.log(' dev Start development server');
console.log(' start Start production server');
console.log(' build Build for production');
console.log();
console.log(c('bold', 'Options:'));
console.log(' -v, --version Show version');
console.log(' -h, --help Show this help');
console.log();
console.log(c('bold', 'Examples:'));
console.log(c('gray', ' $ veko init my-app'));
console.log(c('gray', ' $ veko dev'));
console.log(c('gray', ' $ veko start'));
console.log();
}
// Parse args
const args = process.argv.slice(2);
const command = args[0];
// Version
if (args.includes('-v') || args.includes('--version')) {
console.log(version);
process.exit(0);
}
// Help
if (!command || args.includes('-h') || args.includes('--help')) {
showHelp();
process.exit(0);
}
// Commands
switch (command) {
case 'init':
initProject(args[1]);
break;
case 'dev':
runDev();
break;
case 'start':
runStart();
break;
case 'build':
runBuild();
break;
default:
console.log(c('red', `Unknown command: ${command}`));
showHelp();
process.exit(1);
}
// Init project
function initProject(name) {
const projectName = name || 'veko-app';
const projectPath = path.join(process.cwd(), projectName);
console.log();
console.log(c('cyan', `Creating ${projectName}...`));
// Create directory
if (!fs.existsSync(projectPath)) {
fs.mkdirSync(projectPath, { recursive: true });
}
// Create package.json
const pkg = {
name: projectName,
version: '1.0.0',
type: 'module',
scripts: {
dev: 'node index.js',
start: 'NODE_ENV=production node index.js'
},
dependencies: {
veko: `^${version}`
}
};
fs.writeFileSync(path.join(projectPath, 'package.json'), JSON.stringify(pkg, null, 2));
// Create index.js
const indexJs = `const { createApp } = require('veko');
const app = createApp({ port: 3000 });
// Home route
app.get('/', (req, res) => {
res.html(\`
<!DOCTYPE html>
<html>
<head>
<title>Veko App</title>
<style>
body { font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
.container { text-align: center; color: white; }
h1 { font-size: 3rem; margin-bottom: 0.5rem; }
p { opacity: 0.8; }
code { background: rgba(255,255,255,0.2); padding: 0.5rem 1rem; border-radius: 4px; }
</style>
</head>
<body>
<div class="container">
<h1>🚀 Welcome to Veko</h1>
<p>Zero-dependency Node.js framework</p>
<p><code>Edit index.js to get started</code></p>
</div>
</body>
</html>
\`);
});
// API route example
app.get('/api/hello', (req, res) => {
res.json({ message: 'Hello from Veko!', time: new Date().toISOString() });
});
app.listen(3000, () => {
console.log('\\x1b[32m✓\\x1b[0m Server running at http://localhost:3000');
});
`;
fs.writeFileSync(path.join(projectPath, 'index.js'), indexJs);
// Create public folder
fs.mkdirSync(path.join(projectPath, 'public'), { recursive: true });
fs.writeFileSync(path.join(projectPath, 'public', '.gitkeep'), '');
// Create components folder for VSV
fs.mkdirSync(path.join(projectPath, 'components'), { recursive: true });
// Create example VSV component
const homeVsv = `export default function Home(props) {
const [count, setCount] = $state(0);
return (
<div class="container">
<h1>Welcome to Veko</h1>
<p>Count: {count()}</p>
<button @click={() => setCount(c => c + 1)}>Increment</button>
</div>
);
}
`;
fs.writeFileSync(path.join(projectPath, 'components', 'Home.jsv'), homeVsv);
console.log(c('green', '✓') + ' Created project structure');
console.log();
console.log(c('bold', 'Next steps:'));
console.log(c('gray', ` cd ${projectName}`));
console.log(c('gray', ' npm install'));
console.log(c('gray', ' npm run dev'));
console.log();
}
// Dev server
function runDev() {
console.log();
console.log(c('cyan', '▲ Veko.js ') + c('gray', version));
const entryFiles = ['index.js', 'src/index.js', 'app.js', 'server.js'];
let entry = null;
for (const file of entryFiles) {
if (fs.existsSync(path.join(process.cwd(), file))) {
entry = file;
break;
}
}
if (!entry) {
console.log(c('red', '✗ No entry file found (index.js, app.js, server.js)'));
process.exit(1);
}
console.log(c('gray', ` Starting ${entry}...`));
console.log();
const child = spawn('node', [entry], {
cwd: process.cwd(),
stdio: 'inherit',
env: { ...process.env, NODE_ENV: 'development' }
});
child.on('error', (err) => {
console.error(c('red', `Error: ${err.message}`));
});
}
// Production start
function runStart() {
console.log();
console.log(c('cyan', '▲ Veko.js ') + c('gray', version) + c('green', ' (production)'));
const entryFiles = ['index.js', 'src/index.js', 'app.js', 'server.js'];
let entry = null;
for (const file of entryFiles) {
if (fs.existsSync(path.join(process.cwd(), file))) {
entry = file;
break;
}
}
if (!entry) {
console.log(c('red', '✗ No entry file found'));
process.exit(1);
}
const child = spawn('node', [entry], {
cwd: process.cwd(),
stdio: 'inherit',
env: { ...process.env, NODE_ENV: 'production' }
});
}
// Build with page types support
async function runBuild() {
console.log();
console.log(c('cyan', '▲ Veko.js Build ') + c('gray', version));
console.log();
try {
// Load configuration
const { ConfigLoader, PageBuilder } = require('../lib/core');
const VSV = require('../lib/vsv');
const configLoader = new ConfigLoader();
const config = await configLoader.load(process.cwd());
// Validate config
const validation = configLoader.validate();
if (validation.warnings.length > 0) {
for (const warn of validation.warnings) {
console.log(c('yellow', ` ⚠ ${warn}`));
}
}
if (!validation.valid) {
for (const err of validation.errors) {
console.log(c('red', ` ✗ ${err}`));
}
process.exit(1);
}
// Create mock VSV instance for building
const vsv = {
options: config.dirs,
compiler: new (require('../lib/vsv/compiler'))({ options: config.vsv }),
renderer: new (require('../lib/vsv/renderer'))({ options: config.vsv }),
runtime: new (require('../lib/vsv/runtime'))({ options: config.vsv }),
tailwind: config.vsv.tailwind ? new VSV.VekoTailwind(config.vsv.tailwind) : null,
loadComponent: async (file) => {
// Simple component loader
delete require.cache[require.resolve(file)];
return require(file);
}
};
// Create builder
const builder = new PageBuilder(vsv, {
...config.build,
pagesDir: config.dirs.pages,
ultra: config.ultra
});
// Run build
const stats = await builder.build();
if (stats.errors.length > 0) {
console.log(c('yellow', ` Build completed with ${stats.errors.length} errors`));
process.exit(1);
}
} catch (error) {
// Fallback to simple build message if core modules not available
if (error.code === 'MODULE_NOT_FOUND') {
console.log(c('gray', ' No build step needed - Veko runs natively!'));
console.log(c('green', '✓ Ready for production'));
} else {
console.log(c('red', ` ✗ Build failed: ${error.message}`));
process.exit(1);
}
}
console.log();
}