free-framework 4.8.2 → 4.8.4

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.
@@ -166,14 +166,22 @@ if (server.namedMiddlewares['auth']) server.namedMiddlewares['AuthGuard'] = serv
166
166
  handlerCode = node.handler || "";
167
167
  }
168
168
 
169
+ const stylesRegistry = {};
170
+ ast.filter(n => n.type === 'component').forEach(comp => {
171
+ const styleNode = comp.body.find(b => b.type === 'style');
172
+ stylesRegistry[comp.name] = styleNode ? styleNode.content.trim() : "";
173
+ });
174
+
169
175
  output += `\nserver.route("${node.method.toLowerCase()}", "${node.path}", ${JSON.stringify(middlewares)}, async (req, res) => {
170
176
  const user = (res.context && res.context.user) ? res.context.user : null;
171
177
  let body = {};
172
178
  try {
173
- if (req.headers['content-type'] && req.headers['content-type'].includes('application/json')) {
174
- body = await req.json().catch(() => ({}));
175
- } else {
176
- body = await req.urlencoded().catch(() => ({}));
179
+ if (req.headers['content-length'] > 0) {
180
+ if (req.headers['content-type'] && req.headers['content-type'].includes('application/json')) {
181
+ body = await req.json().catch(() => ({}));
182
+ } else {
183
+ body = await req.urlencoded().catch(() => ({}));
184
+ }
177
185
  }
178
186
  } catch(e) {}
179
187
 
@@ -184,18 +192,23 @@ if (server.namedMiddlewares['auth']) server.namedMiddlewares['AuthGuard'] = serv
184
192
  "\n" +
185
193
  " // If it's a PAGE route (has a view), render HTML\n" +
186
194
  " if (" + (node.view ? 'true' : 'false') + ") {\n" +
195
+ " const stylesRegistry = " + JSON.stringify(stylesRegistry) + ";\n" +
187
196
  " const helpers = {\n" +
188
197
  " renderComponent: (name, props = {}) => {\n" +
189
198
  " server.renderRegistry = server.renderRegistry || {\n" +
190
199
  " " + Object.keys(components).map(name => `${name}: render${name}`).join(',\n ') + "\n" +
191
200
  " };\n" +
192
201
  " return server.renderRegistry[name] ? server.renderRegistry[name](props, helpers) : '<!-- Component ' + name + ' not found -->';\n" +
202
+ " },\n" +
203
+ " getScopedCSS: (used = []) => {\n" +
204
+ " return used.map(name => stylesRegistry[name] || '').join('\\n');\n" +
193
205
  " }\n" +
194
206
  " };\n" +
195
207
  " const props = result || (res.context && res.context.props ? res.context.props : {});\n" +
196
208
  " const pageHtml = helpers.renderComponent('" + viewName + "', props);\n" +
209
+ " const scopedCSS = helpers.getScopedCSS(['" + viewName + "', 'Header']);\n" +
197
210
  " if (req.headers['x-free-partial']) {\n" +
198
- " return res.json({ title: 'Free Ultra | " + viewName + "', content: pageHtml, url: req.url });\n" +
211
+ " return res.json({ title: 'Free Ultra | " + viewName + "', content: pageHtml, url: req.url, css: scopedCSS });\n" +
199
212
  " }\n" +
200
213
  " const headerHtml = helpers.renderComponent('Header', props);\n" +
201
214
  " const fullHTML = `<!DOCTYPE html>\n" +
@@ -210,7 +223,7 @@ if (server.namedMiddlewares['auth']) server.namedMiddlewares['AuthGuard'] = serv
210
223
  " <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700;900&display=swap\" rel=\"stylesheet\">\n" +
211
224
  " <link rel=\"stylesheet\" href=\"/css/app.css\">\n" +
212
225
  " <style>\n" +
213
- " :root { --primary:#fff; --bg:#000; --border:#222; }\n" +
226
+ " :root { --primary:#fff; --bg:#000; --border:#27272a; }\n" +
214
227
  " body { margin:0; font-family:'Inter', system-ui, sans-serif; background:var(--bg); color:#fff; -webkit-font-smoothing:antialiased; }\n" +
215
228
  " ${scopedCSS}\n" +
216
229
  " </style>\n" +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-framework",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Professional Node.js engine for the .free language. Blazing-fast SSR, Islands Architecture, and built-in ORM.",
5
5
  "main": "index.js",
6
6
  "bin": {
package/runtime/server.js CHANGED
@@ -15,7 +15,7 @@ const { LRUCache } = require('lru-cache');
15
15
 
16
16
  class FreeServer {
17
17
  constructor() {
18
- console.log(`[Free Engine] ⚙️ Runtime Core v4.6.5 initializing...`);
18
+ console.log(`[Free Engine] ⚙️ Runtime Core v4.8.3 initializing...`);
19
19
  this.app = new HyperExpress.Server();
20
20
  this.viewsPath = process.env.VIEWS_PATH || nodePath.join(process.cwd(), 'views');
21
21
  this.namedMiddlewares = {};
@@ -43,22 +43,32 @@ class FreeServer {
43
43
  this.handleError(req, res, error);
44
44
  });
45
45
 
46
- // 3. Static Asset Serving (Simplified & Safe)
47
46
  const publicPath = nodePath.join(process.cwd(), 'public');
48
47
  this.app.use((req, res, next) => {
49
- if (req.path === '/' || !req.path.includes('.')) {
50
- return typeof next === 'function' ? next() : null;
51
- }
52
-
53
- const lookupPath = req.path.startsWith('/') ? req.path.substring(1) : req.path;
54
- const fullPath = nodePath.join(publicPath, lookupPath);
48
+ // If it looks like a file request (has a dot)
49
+ if (req.path.includes('.')) {
50
+ const lookupPath = req.path.startsWith('/') ? req.path.substring(1) : req.path;
51
+ const fullPath = nodePath.join(publicPath, lookupPath);
52
+
53
+ if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
54
+ const ext = nodePath.extname(fullPath).toLowerCase();
55
+ const mimes = {
56
+ '.js': 'application/javascript',
57
+ '.css': 'text/css',
58
+ '.png': 'image/png',
59
+ '.jpg': 'image/jpeg',
60
+ '.jpeg': 'image/jpeg',
61
+ '.svg': 'image/svg+xml',
62
+ '.ico': 'image/x-icon',
63
+ '.json': 'application/json'
64
+ };
65
+
66
+ res.setHeader('Content-Type', mimes[ext] || 'text/plain');
67
+ return res.send(fs.readFileSync(fullPath));
68
+ }
55
69
 
56
- if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
57
- console.log(`[Free Engine] 📄 Serving static: ${lookupPath}`);
58
- const ext = nodePath.extname(fullPath);
59
- const mimes = { '.js': 'application/javascript', '.css': 'text/css', '.png': 'image/png', '.jpg': 'image/jpeg', '.svg': 'image/svg+xml' };
60
- res.header('Content-Type', mimes[ext] || 'text/plain');
61
- return res.send(fs.readFileSync(fullPath));
70
+ // If file not found but has extension, 404 immediately
71
+ return res.status(404).send('Not Found');
62
72
  }
63
73
 
64
74
  if (typeof next === 'function') next();
@@ -38,11 +38,11 @@ body {
38
38
  }
39
39
 
40
40
  .btn-outline {
41
- @apply px-6 py-2.5 rounded-lg border border-border font-semibold text-zinc-300 transition-all hover:bg-zinc-900 hover:text-white;
41
+ @apply px-6 py-2.5 rounded-lg border border-zinc-800 font-semibold text-zinc-300 transition-all hover:bg-zinc-900 hover:text-white;
42
42
  }
43
43
 
44
44
  .card-pro {
45
- @apply p-8 rounded-2xl border border-border bg-[#09090b]/50 backdrop-blur-sm transition-all duration-300 hover:border-zinc-700 hover:bg-[#09090b];
45
+ @apply p-8 rounded-2xl border border-zinc-800 bg-[#09090b]/50 backdrop-blur-sm transition-all duration-300 hover:border-zinc-700 hover:bg-[#09090b];
46
46
  }
47
47
 
48
48
  .hero-title {
@@ -54,9 +54,13 @@ body {
54
54
  }
55
55
 
56
56
  .badge-pro {
57
- @apply px-2.5 py-1 rounded-full border border-border bg-zinc-900/50 text-[10px] font-bold tracking-tighter uppercase text-zinc-400;
57
+ @apply px-2.5 py-1 rounded-full border border-zinc-800 bg-zinc-900/50 text-[10px] font-bold tracking-tighter uppercase text-zinc-400;
58
58
  }
59
59
 
60
60
  pre {
61
- @apply bg-zinc-950 border border-border rounded-xl p-6 font-mono text-sm overflow-x-auto text-zinc-300;
61
+ @apply bg-zinc-950 border border-zinc-800 rounded-xl p-6 font-mono text-sm overflow-x-auto text-zinc-300;
62
+ }
63
+
64
+ code {
65
+ @apply font-mono;
62
66
  }
@@ -15,7 +15,7 @@ component Header {
15
15
 
16
16
  div class="flex items-center gap-4" {
17
17
  span class="badge-pro" {
18
- text "v4.8.0-STABLE"
18
+ text "v4.8.4"
19
19
  }
20
20
  a href="/login" class="text-sm font-medium text-white hover:opacity-80 transition-opacity" {
21
21
  text "Sign In"
@@ -2,18 +2,20 @@
2
2
  module.exports = {
3
3
  content: [
4
4
  "./app/**/*.free",
5
- "./views/**/*.html",
6
- "./public/**/*.js"
5
+ "./resources/views/**/*.free",
6
+ "./routes/**/*.free",
7
+ "../resources/views/debug.html"
7
8
  ],
8
9
  theme: {
9
10
  extend: {
10
11
  colors: {
11
- primary: '#00ff88',
12
- secondary: '#00bdff',
13
- dark: '#0a0a0a',
12
+ primary: '#fff',
13
+ secondary: '#a1a1aa',
14
+ dark: '#000',
15
+ border: '#27272a',
14
16
  },
15
17
  fontFamily: {
16
- sans: ['Outfit', 'sans-serif'],
18
+ sans: ['Inter', 'system-ui', 'sans-serif'],
17
19
  },
18
20
  },
19
21
  },