free-framework 4.6.7 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/compiler/generator.js
CHANGED
|
@@ -12,6 +12,7 @@ function generate(ast) {
|
|
|
12
12
|
"const jwt = require('jsonwebtoken');\n" +
|
|
13
13
|
"const fs = require('fs');\n" +
|
|
14
14
|
"const path = require('path');\n" +
|
|
15
|
+
"const { minify } = require('html-minifier-terser');\n" +
|
|
15
16
|
"// Enterprise Services (Global Inject)\n" +
|
|
16
17
|
"let Validator, Storage;\n" +
|
|
17
18
|
"try { Validator = require(path.join(process.cwd(), 'app/Services/Validator.js')); } catch(e) {}\n" +
|
|
@@ -199,185 +200,195 @@ if (server.namedMiddlewares['auth']) server.namedMiddlewares['AuthGuard'] = serv
|
|
|
199
200
|
" }\n" +
|
|
200
201
|
"\n" +
|
|
201
202
|
" const headerHtml = helpers.renderComponent('Header', props);\n" +
|
|
202
|
-
" const fullHTML = `<!DOCTYPE html
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
203
|
+
" const fullHTML = `<!DOCTYPE html>
|
|
204
|
+
< html lang = "en" >
|
|
205
|
+
<head>
|
|
206
|
+
<meta charset="UTF-8">
|
|
207
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
208
|
+
<title>Free Ultra | ${viewName}</title>
|
|
209
|
+
<meta name="csrf-token" content="${res.context.csrfToken || ''}">
|
|
210
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
211
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
212
|
+
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap" rel="stylesheet">
|
|
213
|
+
<link rel="stylesheet" href="/css/app.css">
|
|
214
|
+
<style>
|
|
215
|
+
:root {--primary:#00ff88; --secondary:#00bdff; --dark:#050505; }
|
|
216
|
+
body {margin:0; font-family:'Outfit', system-ui, sans-serif; background:var(--dark); color:white; overflow-x:hidden; scroll-behavior:smooth; }
|
|
217
|
+
${scopedCSS}
|
|
218
|
+
</style>
|
|
219
|
+
<script src="/free-runtime.js" defer></script>
|
|
220
|
+
</head>
|
|
221
|
+
<body>
|
|
222
|
+
${headerHtml}
|
|
223
|
+
<main id="free-app-root">
|
|
224
|
+
${pageHtml}
|
|
225
|
+
</main>
|
|
226
|
+
</body>
|
|
227
|
+
</html>`;
|
|
228
|
+
res.header('Content-Type', 'text/html').send(fullHTML);
|
|
218
229
|
" } else {\n" +
|
|
219
|
-
|
|
220
|
-
|
|
230
|
+
" // API route - return JSON\n" +
|
|
231
|
+
" res.json({success: true, result });\n" +
|
|
221
232
|
" }\n" +
|
|
222
233
|
"});\n";
|
|
223
234
|
}
|
|
224
235
|
});
|
|
225
236
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
237
|
+
output += `\nif (require.main === module) {
|
|
238
|
+
ORM.migrate(modelsRegistry).then(() => {
|
|
239
|
+
server.start(process.env.PORT || 4000);
|
|
240
|
+
}).catch(err => console.error(err));
|
|
230
241
|
}
|
|
231
242
|
|
|
232
|
-
module.exports = {
|
|
233
|
-
`;
|
|
243
|
+
module.exports = {...modelsRegistry, server, modelsRegistry, ORM};
|
|
244
|
+
`;
|
|
234
245
|
|
|
235
|
-
|
|
246
|
+
return output;
|
|
236
247
|
}
|
|
237
248
|
|
|
238
|
-
function generateUltraComponent(node) {
|
|
249
|
+
function generateUltraComponent(node) {
|
|
239
250
|
const onMount = node.body.find(n => n.type === 'onMount')?.code || '';
|
|
240
251
|
const onDestroy = node.body.find(n => n.type === 'onDestroy')?.code || '';
|
|
241
252
|
const isInteractive = node.states.length > 0 || onMount !== '' || onDestroy !== '' || hasInteractivity(node.body);
|
|
242
253
|
const stateNames = node.states.map(s => s.name);
|
|
243
254
|
|
|
244
|
-
|
|
255
|
+
let code = `\nfunction render${node.name}(props = { }, helpers = { }) {\n`;
|
|
245
256
|
code += ` const e = (str) => String(str).replace(/[&<>'"]/g, m => ({'&': '&', '<': '<', '>': '>', "'": ''', '"': '"' }[m]));\n`;
|
|
246
|
-
|
|
257
|
+
code += ` const state = {\n`;
|
|
247
258
|
node.states.forEach(s => {
|
|
248
|
-
|
|
249
|
-
|
|
259
|
+
let val = s.default;
|
|
260
|
+
const isExpression = typeof val === 'string' && (val.includes('props.') || val.includes('||') || val.includes('&&') || val === 'true' || val === 'false' || val === 'null' || val.trim().startsWith('{') || val.trim().startsWith('['));
|
|
250
261
|
const finalVal = (isExpression || typeof val === 'number') ? val : `"${val}"`;
|
|
251
|
-
|
|
262
|
+
code += ` "${s.name}": ${finalVal},\n`;
|
|
252
263
|
});
|
|
253
264
|
code += ` };\n`;
|
|
254
265
|
if (stateNames.length > 0) {
|
|
255
|
-
|
|
266
|
+
code += ` const { ${stateNames.join(', ')} } = state;\n`;
|
|
256
267
|
}
|
|
257
268
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
269
|
+
code += ` const _events = [];\n`;
|
|
270
|
+
code += ` const islandAttr = ${isInteractive} ? ' data-component="${node.name}"' : '';\n`;
|
|
271
|
+
code += ` let html = "<div class='free-component'" + islandAttr + " data-state='" + JSON.stringify(state).replace(/'/g, "'") + "'";\n`;
|
|
261
272
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
273
|
+
if (onMount) {
|
|
274
|
+
code += ` _events.push({ id: 'onMount', fn: function(state) { ${onMount} } });\n`;
|
|
275
|
+
code += ` html += " data-free-on-mount='onMount'";\n`;
|
|
265
276
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
277
|
+
if (onDestroy) {
|
|
278
|
+
code += ` _events.push({ id: 'onDestroy', fn: function(state) { ${onDestroy} } });\n`;
|
|
279
|
+
code += ` html += " data-free-on-destroy='onDestroy'";\n`;
|
|
269
280
|
}
|
|
270
281
|
|
|
271
282
|
code += ` html += ">";\n`;
|
|
272
|
-
|
|
283
|
+
code += generateComponentBody(node.body, stateNames);
|
|
273
284
|
|
|
274
|
-
|
|
275
|
-
|
|
285
|
+
if (isInteractive) {
|
|
286
|
+
code += ` html += "<script>window.__free_actions = window.__free_actions || {}; window.__free_actions['${node.name}'] = {};";\n`;
|
|
276
287
|
code += ` _events.forEach(ev => {\n`;
|
|
277
|
-
|
|
288
|
+
code += ` html += "window.__free_actions['${node.name}'][ev.id] = " + ev.fn.toString() + ";";\n`;
|
|
278
289
|
code += ` });\n`;
|
|
279
|
-
|
|
290
|
+
code += ` html += "</script>";\n`;
|
|
280
291
|
}
|
|
281
292
|
|
|
282
|
-
|
|
283
|
-
|
|
293
|
+
code += ` html += "</div>";\n return html;\n}\n`;
|
|
294
|
+
return code;
|
|
284
295
|
}
|
|
285
296
|
|
|
286
|
-
function generateComponentBody(body, stateNames) {
|
|
287
|
-
|
|
297
|
+
function generateComponentBody(body, stateNames) {
|
|
298
|
+
let code = "";
|
|
288
299
|
body.forEach(node => {
|
|
289
300
|
if (node.type === 'tag') {
|
|
290
|
-
|
|
301
|
+
code += generateTagCode(node, stateNames);
|
|
291
302
|
} else if (node.type === 'event') {
|
|
292
303
|
return;
|
|
293
304
|
} else if (node.type === 'script') {
|
|
294
305
|
const scriptCode = node.code.replace(/`/g, '\\`').replace(/\$/g, '\\$');
|
|
295
|
-
|
|
306
|
+
code += ` html += \`<script>${scriptCode}</script>\`;\n`;
|
|
296
307
|
} else if (node.type === 'loop') {
|
|
297
|
-
|
|
308
|
+
code += " if (Array.isArray(" + node.list + ")) {\n";
|
|
298
309
|
code += " " + node.list + ".forEach(" + node.item + " => {\n";
|
|
299
|
-
|
|
310
|
+
code += generateComponentBody(node.body, stateNames).replace(/^ /gm, ' ');
|
|
300
311
|
code += " });\n";
|
|
301
312
|
code += " }\n";
|
|
302
313
|
} else if (node.type === 'condition') {
|
|
303
|
-
|
|
304
|
-
|
|
314
|
+
code += " if (" + node.condition + ") {\n";
|
|
315
|
+
code += generateComponentBody(node.body, stateNames).replace(/^ /gm, ' ');
|
|
305
316
|
code += " }\n";
|
|
306
317
|
}
|
|
307
318
|
});
|
|
308
|
-
|
|
319
|
+
return code;
|
|
309
320
|
}
|
|
310
321
|
|
|
311
|
-
function hasInteractivity(nodes) {
|
|
322
|
+
function hasInteractivity(nodes) {
|
|
312
323
|
return nodes.some(n => {
|
|
313
324
|
if (n.type === 'tag') {
|
|
314
325
|
if (n.attributes && Object.keys(n.attributes).some(a => a.startsWith('on'))) return true;
|
|
315
|
-
|
|
326
|
+
if (n.children && hasInteractivity(n.children)) return true;
|
|
316
327
|
}
|
|
317
|
-
|
|
328
|
+
return false;
|
|
318
329
|
});
|
|
319
330
|
}
|
|
320
331
|
|
|
321
|
-
function generateTagCode(tag, stateNames) {
|
|
332
|
+
function generateTagCode(tag, stateNames) {
|
|
322
333
|
const isComponent = tag.name[0] === tag.name[0].toUpperCase();
|
|
323
|
-
|
|
324
|
-
|
|
334
|
+
if (isComponent) {
|
|
335
|
+
let propsStr = "{";
|
|
325
336
|
if (tag.attributes) {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
337
|
+
Object.keys(tag.attributes).forEach((a, i) => {
|
|
338
|
+
let val = tag.attributes[a].replace(/\{([^}]+)\}/g, (_, p) => "${" + p + "}");
|
|
339
|
+
propsStr += (i === 0 ? "" : ", ") + '"' + a + '": `' + val + '`';
|
|
340
|
+
});
|
|
330
341
|
}
|
|
331
342
|
propsStr += "}";
|
|
332
|
-
|
|
343
|
+
return ` html += helpers.renderComponent('${tag.name}', ${propsStr}, helpers);\n`;
|
|
333
344
|
}
|
|
334
345
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
346
|
+
let code = ` html += "<${tag.name}";\n`;
|
|
347
|
+
|
|
348
|
+
if (tag.attributes) {
|
|
349
|
+
Object.keys(tag.attributes).forEach(a => {
|
|
350
|
+
if (a.startsWith('on')) {
|
|
351
|
+
const eventName = a.substring(2).toLowerCase();
|
|
352
|
+
const uniqueId = `${eventName}_${tag.name}_${Math.random().toString(36).substring(7)}`;
|
|
353
|
+
if (tag.attributes[a].includes('++')) {
|
|
354
|
+
const key = tag.attributes[a].split('++')[0].trim();
|
|
355
|
+
code += ` _events.push({ id: '${uniqueId}', fn: function(state) { state['${key}']++; } });\n`;
|
|
356
|
+
} else {
|
|
357
|
+
code += ` _events.push({ id: '${uniqueId}', fn: function(state, event) { ${tag.attributes[a]} } });\n`;
|
|
358
|
+
}
|
|
359
|
+
code += ` html += " data-on-${eventName}='${uniqueId}'";\n`;
|
|
360
|
+
} else {
|
|
361
|
+
let val = tag.attributes[a].replace(/\{([^}]+)\}/g, (_, p) => "${e(" + p + ")}");
|
|
362
|
+
code += " html += \" \" + " + JSON.stringify(a) + " + \"='\" + `" + val + "` + \"'\";\n";
|
|
363
|
+
}
|
|
364
|
+
});
|
|
354
365
|
}
|
|
355
366
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
367
|
+
if (tag.children) {
|
|
368
|
+
tag.children.forEach(child => {
|
|
369
|
+
if (child.type === 'event') {
|
|
370
|
+
const eventName = child.event.toLowerCase();
|
|
371
|
+
const uniqueId = `${eventName}_child_${Math.random().toString(36).substring(7)}`;
|
|
372
|
+
code += ` _events.push({ id: '${uniqueId}', fn: function(state, event) { ${child.code} } });\n`;
|
|
373
|
+
code += ` html += " data-on-${eventName}='${uniqueId}'";\n`;
|
|
374
|
+
}
|
|
375
|
+
});
|
|
365
376
|
}
|
|
366
377
|
|
|
367
378
|
code += ` html += ">";\n`;
|
|
368
379
|
|
|
369
|
-
|
|
370
|
-
|
|
380
|
+
if (tag.content) {
|
|
381
|
+
let cont = tag.content.replace(/`/g, '\\`').replace(/\$/g, '\\$').replace(/\{([^}]+)\}/g, (_, p) => {
|
|
371
382
|
const parts = p.split('.');
|
|
372
|
-
|
|
373
|
-
|
|
383
|
+
if (stateNames.includes(parts[0])) return `\${e(state["${parts[0]}"]${ parts.slice(1).map(part => `?.["${part}"]`).join('') })}`;
|
|
384
|
+
return `\${e(${ p })}`;
|
|
374
385
|
});
|
|
375
|
-
|
|
386
|
+
code += ` html += \`${cont}\`;\n`;
|
|
376
387
|
}
|
|
377
388
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
389
|
+
if (tag.children) code += generateComponentBody(tag.children, stateNames);
|
|
390
|
+
code += ` html += "</${tag.name}>";\n`;
|
|
391
|
+
return code;
|
|
381
392
|
}
|
|
382
393
|
|
|
383
|
-
module.exports = {
|
|
394
|
+
module.exports = {generate};
|
package/package.json
CHANGED
|
@@ -2,6 +2,81 @@
|
|
|
2
2
|
@tailwind components;
|
|
3
3
|
@tailwind utilities;
|
|
4
4
|
|
|
5
|
+
:root {
|
|
6
|
+
--primary: #00ff88;
|
|
7
|
+
--primary-glow: rgba(0, 255, 136, 0.4);
|
|
8
|
+
--secondary: #00bdff;
|
|
9
|
+
--dark: #050505;
|
|
10
|
+
--surface: rgba(255, 255, 255, 0.03);
|
|
11
|
+
--glass-border: rgba(255, 255, 255, 0.1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
@apply bg-black text-gray-100 selection:bg-primary selection:text-black;
|
|
16
|
+
background-image:
|
|
17
|
+
radial-gradient(circle at 20% 20%, rgba(0, 255, 136, 0.05) 0%, transparent 40%),
|
|
18
|
+
radial-gradient(circle at 80% 80%, rgba(0, 189, 255, 0.05) 0%, transparent 40%);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.glass {
|
|
22
|
+
@apply backdrop-blur-xl bg-white/5 border border-white/10;
|
|
23
|
+
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.8);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.nav-link {
|
|
27
|
+
@apply relative py-2 transition-all duration-300 hover:text-primary;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.nav-link::after {
|
|
31
|
+
content: '';
|
|
32
|
+
@apply absolute bottom-0 left-0 w-0 h-0.5 bg-primary transition-all duration-300;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.nav-link:hover::after {
|
|
36
|
+
@apply w-full;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.btn-premium {
|
|
40
|
+
@apply px-8 py-4 rounded-full font-bold transition-all duration-500 relative overflow-hidden flex items-center gap-2;
|
|
41
|
+
background: linear-gradient(135deg, var(--primary), var(--secondary));
|
|
42
|
+
color: #000;
|
|
43
|
+
box-shadow: 0 0 20px var(--primary-glow);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.btn-premium:hover {
|
|
47
|
+
@apply scale-105;
|
|
48
|
+
box-shadow: 0 0 40px var(--primary-glow);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.hero-text {
|
|
52
|
+
@apply text-7xl md:text-9xl font-black tracking-tighter leading-none;
|
|
53
|
+
background: linear-gradient(180deg, #fff 0%, #888 100%);
|
|
54
|
+
-webkit-background-clip: text;
|
|
55
|
+
-webkit-text-fill-color: transparent;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@keyframes float {
|
|
59
|
+
0% {
|
|
60
|
+
transform: translateY(0px);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
50% {
|
|
64
|
+
transform: translateY(-20px);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
100% {
|
|
68
|
+
transform: translateY(0px);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.animate-float {
|
|
73
|
+
animation: float 6s ease-in-out infinite;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.feature-card {
|
|
77
|
+
@apply p-8 rounded-3xl glass transition-all duration-500 hover:-translate-y-2 hover:border-primary/30;
|
|
78
|
+
}
|
|
79
|
+
|
|
5
80
|
/* --------------------------------------------------------------------------
|
|
6
81
|
* Free Framework Enterprise Styles
|
|
7
82
|
* --------------------------------------------------------------------------
|
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
component Header {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
header class="fixed top-0 left-0 w-full z-50 transition-all duration-300 border-b border-white/5 backdrop-blur-md bg-black/30" {
|
|
3
|
+
nav class="max-w-7xl mx-auto px-6 h-20 flex items-center justify-between" {
|
|
4
|
+
div class="flex items-center gap-8" {
|
|
5
|
+
a href="/" class="flex items-center gap-2 group" {
|
|
6
|
+
div class="w-10 h-10 rounded-xl bg-gradient-to-tr from-primary to-secondary flex items-center justify-center shadow-lg shadow-primary/20 group-hover:rotate-12 transition-transform duration-500" {
|
|
7
|
+
span class="text-black font-black text-xl" { text "F" }
|
|
8
|
+
}
|
|
9
|
+
span class="text-2xl font-black tracking-tighter text-white" { text "FREE ULTRA" }
|
|
10
|
+
}
|
|
4
11
|
|
|
5
|
-
div {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
div class="hidden md:flex items-center gap-6 ml-8" {
|
|
13
|
+
a href="/" class="nav-link font-medium" { text "Home" }
|
|
14
|
+
a href="/dashboard" class="nav-link font-medium" { text "Dashboard" }
|
|
15
|
+
a href="/docs" class="nav-link font-medium" { text "Documentation" }
|
|
9
16
|
}
|
|
17
|
+
}
|
|
10
18
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
div class="flex items-center gap-4" {
|
|
20
|
+
span class="hidden sm:inline-flex items-center px-3 py-1 rounded-full bg-primary/10 border border-primary/20 text-primary text-xs font-bold tracking-widest uppercase animate-pulse" {
|
|
21
|
+
text "v4.7.0 ACTIVE"
|
|
22
|
+
}
|
|
23
|
+
a href="/login" class="px-5 py-2 rounded-full border border-white/10 hover:bg-white/5 transition-colors font-semibold" {
|
|
24
|
+
text "Sign In"
|
|
16
25
|
}
|
|
26
|
+
}
|
|
17
27
|
}
|
|
28
|
+
}
|
|
18
29
|
}
|
|
@@ -10,17 +10,66 @@ component Home {
|
|
|
10
10
|
.hero { min-height: 100vh; display: flex; flex-direction: column; justify-content: center; align-items: center; background: linear-gradient(to right, #1f2937, #111827); color: white; }
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
main class="pt-32 pb-20 px-6" {
|
|
14
|
+
// Hero Section
|
|
15
|
+
section class="relative max-w-7xl mx-auto flex flex-col items-center text-center py-20 md:py-32" {
|
|
16
|
+
div class="absolute -top-20 w-72 h-72 bg-primary/20 rounded-full blur-[120px] -z-10 animate-pulse" { }
|
|
17
|
+
div class="absolute top-40 -right-20 w-96 h-96 bg-secondary/10 rounded-full blur-[150px] -z-10" { }
|
|
18
|
+
|
|
19
|
+
h1 class="hero-text mb-8" {
|
|
20
|
+
text "Build Faster."
|
|
21
|
+
br { }
|
|
22
|
+
span class="text-primary" { text "Deploy Sharper." }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
p class="text-xl md:text-2xl text-gray-400 max-w-2xl mb-12 font-medium leading-relaxed" {
|
|
26
|
+
text "The Enterprise Node.js Ecosystem you've been waiting for. Powered by Islands Architecture, built-in ORM, and Extreme SSR Performance."
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
div class="flex flex-wrap justify-center gap-6" {
|
|
30
|
+
a href="/docs" class="btn-premium" {
|
|
31
|
+
span { text "Get Started" }
|
|
32
|
+
svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" {
|
|
33
|
+
path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="Arrow-right" { }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
a href="https://github.com/dev-omartolba" class="px-8 py-4 rounded-full glass font-bold hover:bg-white/10 transition-all flex items-center gap-2" {
|
|
37
|
+
text "View Source"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
div class="mt-20 w-full glass rounded-[40px] p-4 animate-float" {
|
|
42
|
+
div class="rounded-[32px] overflow-hidden border border-white/5 bg-black/40" {
|
|
43
|
+
div class="px-4 py-3 border-b border-white/5 flex gap-2" {
|
|
44
|
+
div class="w-3 h-3 rounded-full bg-red-500/50" { }
|
|
45
|
+
div class="w-3 h-3 rounded-full bg-yellow-500/50" { }
|
|
46
|
+
div class="w-3 h-3 rounded-full bg-green-500/50" { }
|
|
47
|
+
}
|
|
48
|
+
pre class="p-8 text-left text-primary font-mono text-lg" {
|
|
49
|
+
code { text "npx free-framework new my-app" }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
19
53
|
}
|
|
20
54
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
55
|
+
// Features Section
|
|
56
|
+
section class="max-w-7xl mx-auto py-32" {
|
|
57
|
+
h2 class="text-4xl md:text-6xl font-black mb-20 text-center" { text "Why Free Ultra?" }
|
|
58
|
+
|
|
59
|
+
div class="grid md:grid-cols-3 gap-8" {
|
|
60
|
+
div class="feature-card" {
|
|
61
|
+
h3 class="text-2xl font-bold mb-4 text-primary" { text "HyperExpress Core" }
|
|
62
|
+
p class="text-gray-400" { text "Built on top of uWebSockets.js, providing the fastest HTTP/WebSocket performance in the Node.js ecosystem." }
|
|
63
|
+
}
|
|
64
|
+
div class="feature-card" {
|
|
65
|
+
h3 class="text-2xl font-bold mb-4 text-secondary" { text "Islands Architecture" }
|
|
66
|
+
p class="text-gray-400" { text "Ships zero Javascript by default. Only hydrate the components that need interactivity for ultimate SEO and speed." }
|
|
67
|
+
}
|
|
68
|
+
div class="feature-card" {
|
|
69
|
+
h3 class="text-2xl font-bold mb-4 text-white" { text "Pro-Grade ORM" }
|
|
70
|
+
p class="text-gray-400" { text "A built-in migration system and elegant Model layer that makes database management a breeze." }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
24
73
|
}
|
|
25
74
|
}
|
|
26
75
|
}
|