free-framework 4.4.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/LICENSE +21 -0
- package/README.md +198 -0
- package/bin/free.js +118 -0
- package/cli/commands/build.js +124 -0
- package/cli/commands/deploy.js +143 -0
- package/cli/commands/devtools.js +210 -0
- package/cli/commands/doctor.js +72 -0
- package/cli/commands/install.js +28 -0
- package/cli/commands/make.js +74 -0
- package/cli/commands/migrate.js +67 -0
- package/cli/commands/new.js +54 -0
- package/cli/commands/serve.js +73 -0
- package/cli/commands/test.js +57 -0
- package/compiler/analyzer.js +102 -0
- package/compiler/generator.js +386 -0
- package/compiler/lexer.js +166 -0
- package/compiler/parser.js +410 -0
- package/database/model.js +6 -0
- package/database/orm.js +379 -0
- package/database/query-builder.js +179 -0
- package/index.js +51 -0
- package/package.json +80 -0
- package/plugins/auth.js +212 -0
- package/plugins/cache.js +85 -0
- package/plugins/chat.js +32 -0
- package/plugins/mail.js +53 -0
- package/plugins/metrics.js +126 -0
- package/plugins/payments.js +59 -0
- package/plugins/queue.js +139 -0
- package/plugins/search.js +51 -0
- package/plugins/storage.js +123 -0
- package/plugins/upload.js +62 -0
- package/router/router.js +57 -0
- package/runtime/app.js +14 -0
- package/runtime/client.js +254 -0
- package/runtime/cluster.js +35 -0
- package/runtime/edge.js +62 -0
- package/runtime/middleware/maintenance.js +54 -0
- package/runtime/middleware/security.js +30 -0
- package/runtime/server.js +130 -0
- package/runtime/validator.js +102 -0
- package/runtime/views/error.free +104 -0
- package/runtime/views/maintenance.free +0 -0
- package/template-engine/renderer.js +24 -0
- package/templates/app-template/.env +23 -0
- package/templates/app-template/app/Exceptions/Handler.js +65 -0
- package/templates/app-template/app/Http/Controllers/AuthController.free +91 -0
- package/templates/app-template/app/Http/Middleware/AuthGuard.js +46 -0
- package/templates/app-template/app/Services/Storage.js +75 -0
- package/templates/app-template/app/Services/Validator.js +91 -0
- package/templates/app-template/app/controllers/AuthController.free +42 -0
- package/templates/app-template/app/middleware/auth.js +25 -0
- package/templates/app-template/app/models/User.free +32 -0
- package/templates/app-template/app/routes.free +12 -0
- package/templates/app-template/app/styles.css +23 -0
- package/templates/app-template/app/views/counter.free +23 -0
- package/templates/app-template/app/views/header.free +13 -0
- package/templates/app-template/config/app.js +32 -0
- package/templates/app-template/config/auth.js +39 -0
- package/templates/app-template/config/database.js +54 -0
- package/templates/app-template/package.json +28 -0
- package/templates/app-template/resources/css/app.css +11 -0
- package/templates/app-template/resources/views/dashboard.free +25 -0
- package/templates/app-template/resources/views/home.free +26 -0
- package/templates/app-template/routes/api.free +22 -0
- package/templates/app-template/routes/web.free +25 -0
- package/templates/app-template/tailwind.config.js +21 -0
- package/templates/app-template/views/about.ejs +47 -0
- package/templates/app-template/views/home.ejs +52 -0
- package/templates/auth/login.html +144 -0
- package/templates/auth/register.html +146 -0
- package/utils/logger.js +20 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dev Omar Tolba
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Free Framework
|
|
2
|
+
|
|
3
|
+
> The blazing-fast, full-stack web framework with its own `.free` language syntax.
|
|
4
|
+
> Build modern production apps in a fraction of the time ā with **zero boilerplate**.
|
|
5
|
+
|
|
6
|
+
[](https://www.npmjs.com/package/free-framework)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](https://nodejs.org)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## ā” Why Free Framework?
|
|
13
|
+
|
|
14
|
+
| Feature | Free | Next.js | Laravel |
|
|
15
|
+
|---|---|---|---|
|
|
16
|
+
| Custom DSL Syntax | ā
`.free` | ā JSX | ā Blade |
|
|
17
|
+
| Performance Engine | ā
uWebSockets | ā ļø Node HTTP | ā ļø PHP-FPM |
|
|
18
|
+
| Built-in ORM | ā
Yes | ā No | ā
Eloquent |
|
|
19
|
+
| Server Actions | ā
`action {}` | ā
Yes | ā No |
|
|
20
|
+
| Real-Time Sockets | ā
Built-in | ā Manual | ā Manual |
|
|
21
|
+
| Edge Runtime | ā
Yes | ā
Yes | ā No |
|
|
22
|
+
| Islands Architecture | ā
Native | ā ļø Extra | ā No |
|
|
23
|
+
| CLI DevTools | ā
`free devtools` | ā No | ā ļø Artisan |
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## š Quick Start
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx free-framework new my-app
|
|
31
|
+
cd my-app
|
|
32
|
+
free serve
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## š§© The `.free` Syntax
|
|
38
|
+
|
|
39
|
+
**Routing:**
|
|
40
|
+
```
|
|
41
|
+
route GET "/" {
|
|
42
|
+
view "Home"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
route GET "/dashboard" {
|
|
46
|
+
middleware auth
|
|
47
|
+
view "Dashboard"
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Components with Reactive State:**
|
|
52
|
+
```
|
|
53
|
+
component Counter {
|
|
54
|
+
state count = 0
|
|
55
|
+
|
|
56
|
+
button {
|
|
57
|
+
text "Clicked {count} times!"
|
|
58
|
+
on-click { count++ }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Server Actions (no API routes needed):**
|
|
64
|
+
```
|
|
65
|
+
action createPost {
|
|
66
|
+
const post = await Post.create({ title: body.title, content: body.content });
|
|
67
|
+
return post;
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Database Models:**
|
|
72
|
+
```
|
|
73
|
+
model Post {
|
|
74
|
+
title string
|
|
75
|
+
content string
|
|
76
|
+
likes number
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Authentication:**
|
|
81
|
+
```
|
|
82
|
+
auth {
|
|
83
|
+
login "/login"
|
|
84
|
+
register "/register"
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**File Uploads:**
|
|
89
|
+
```
|
|
90
|
+
upload avatar {
|
|
91
|
+
path "public/uploads"
|
|
92
|
+
maxSize "10mb"
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Error Pages:**
|
|
97
|
+
```
|
|
98
|
+
error 404 {
|
|
99
|
+
view "NotFound"
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## š ļø CLI Commands
|
|
106
|
+
|
|
107
|
+
| Command | Description |
|
|
108
|
+
|---|---|
|
|
109
|
+
| `free new <name>` | Create a new Free project |
|
|
110
|
+
| `free serve` | Start the development server |
|
|
111
|
+
| `free build` | Build for production (minified) |
|
|
112
|
+
| `free devtools` | Launch the DevTools Dashboard |
|
|
113
|
+
| `free test` | Run the test suite with Vitest |
|
|
114
|
+
| `free install <pkg>` | Install ecosystem plugins |
|
|
115
|
+
| `free make:model <name>` | Scaffold a new model |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## šļø Architecture
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
my-app/
|
|
123
|
+
āāā app/
|
|
124
|
+
ā āāā routes.free # Route definitions
|
|
125
|
+
ā āāā config.free # Auth, Uploads, Error pages
|
|
126
|
+
ā āāā models.free # ORM Models
|
|
127
|
+
ā āāā actions.free # Server Actions
|
|
128
|
+
ā āāā *.free # Pages & Components
|
|
129
|
+
āāā public/ # Static assets
|
|
130
|
+
āāā views/ # EJS fallback views
|
|
131
|
+
āāā .env # Environment config
|
|
132
|
+
āāā .free/ # Compiled output
|
|
133
|
+
āāā app.js
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## ā” Performance
|
|
139
|
+
|
|
140
|
+
Powered by **HyperExpress (uWebSockets.js)** ā the fastest HTTP/WebSocket server for Node.js:
|
|
141
|
+
|
|
142
|
+
- **Multi-core clustering** via built-in `ClusterManager`
|
|
143
|
+
- **LRU page cache** for instant SSR delivery
|
|
144
|
+
- **Zero-copy streaming** for file uploads
|
|
145
|
+
- **Native pub/sub** WebSocket rooms
|
|
146
|
+
- **Minified HTML/CSS/JS** output at build time
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## š Built-in Plugins
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
const { AuthPlugin, UploadPlugin, ChatPlugin } = require('free-framework');
|
|
154
|
+
|
|
155
|
+
server.use(AuthPlugin({ login: '/login', register: '/register' }));
|
|
156
|
+
server.use(UploadPlugin('avatar', { path: 'uploads', maxSize: '10mb' }));
|
|
157
|
+
server.use(ChatPlugin());
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## š Security Built-in
|
|
163
|
+
|
|
164
|
+
- Zero-config **security headers** (CSP, X-Frame-Options, HSTS)
|
|
165
|
+
- **DDoS & Rate limiting** (1000 req/min per IP by default)
|
|
166
|
+
- **CSRF token injection** on every page
|
|
167
|
+
- **bcrypt** password hashing in `AuthPlugin`
|
|
168
|
+
- **XSS-safe** template interpolation (auto-escaped)
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## š Edge Runtime
|
|
173
|
+
|
|
174
|
+
Deploy anywhere ā Cloudflare Workers, Bun, or standard Node.js:
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
const { EdgeRuntime } = require('free-framework');
|
|
178
|
+
const edge = new EdgeRuntime(myFreeApp);
|
|
179
|
+
|
|
180
|
+
// Cloudflare Workers / Bun compatible
|
|
181
|
+
export default { fetch: edge.fetch.bind(edge) };
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## š¦ Ecosystem
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
free install free-auth
|
|
190
|
+
free install free-mail
|
|
191
|
+
free install free-payments
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
MIT Ā© [Dev Omar Tolba](https://github.com/dev-omartolba)
|
package/bin/free.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* bin/free.js
|
|
5
|
+
* CLI entry point for the Free framework.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { program } = require("commander");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
|
|
11
|
+
// Load Environment variables globally
|
|
12
|
+
require('dotenv').config();
|
|
13
|
+
|
|
14
|
+
program
|
|
15
|
+
.version("1.0.0")
|
|
16
|
+
.description("Free Framework CLI");
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.command("new <name>")
|
|
20
|
+
.description("Create a new Free project")
|
|
21
|
+
.action((name) => {
|
|
22
|
+
require("../cli/commands/new")(name);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
program
|
|
26
|
+
.command("serve")
|
|
27
|
+
.description("Start the development server")
|
|
28
|
+
.option("-p, --port <port>", "Port to run on", "3000")
|
|
29
|
+
.option("--cluster", "Run in Enterprise Cluster Mode")
|
|
30
|
+
.action((options) => {
|
|
31
|
+
require("../cli/commands/serve")(options);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
program
|
|
35
|
+
.command("build")
|
|
36
|
+
.description("Build the project for production")
|
|
37
|
+
.action(() => {
|
|
38
|
+
require("../cli/commands/build")();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
program
|
|
42
|
+
.command("make:model <name>")
|
|
43
|
+
.description("Scaffold a new model")
|
|
44
|
+
.action((name) => { require("../cli/commands/make").model(name); });
|
|
45
|
+
|
|
46
|
+
program
|
|
47
|
+
.command("make:controller <name>")
|
|
48
|
+
.description("Scaffold route file for a controller")
|
|
49
|
+
.action((name) => { require("../cli/commands/make").controller(name); });
|
|
50
|
+
|
|
51
|
+
program
|
|
52
|
+
.command("make:page <name>")
|
|
53
|
+
.description("Scaffold a new .free page")
|
|
54
|
+
.action((name) => { require("../cli/commands/make").page(name); });
|
|
55
|
+
|
|
56
|
+
program
|
|
57
|
+
.command("make:component <name>")
|
|
58
|
+
.description("Scaffold a new .free component")
|
|
59
|
+
.action((name) => { require("../cli/commands/make").component(name); });
|
|
60
|
+
|
|
61
|
+
program
|
|
62
|
+
.command("make:middleware <name>")
|
|
63
|
+
.description("Scaffold a new middleware file")
|
|
64
|
+
.action((name) => { require("../cli/commands/make").middleware(name); });
|
|
65
|
+
|
|
66
|
+
program
|
|
67
|
+
.command("make:plugin <name>")
|
|
68
|
+
.description("Scaffold a new plugin file")
|
|
69
|
+
.action((name) => { require("../cli/commands/make").plugin(name); });
|
|
70
|
+
|
|
71
|
+
program
|
|
72
|
+
.command("make:migration <name>")
|
|
73
|
+
.description("Scaffold a new database migration")
|
|
74
|
+
.action((name) => { require("../cli/commands/make").migration(name); });
|
|
75
|
+
|
|
76
|
+
program
|
|
77
|
+
.command("migrate")
|
|
78
|
+
.description("Run pending database migrations")
|
|
79
|
+
.action(() => {
|
|
80
|
+
require("../cli/commands/migrate")();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
program
|
|
84
|
+
.command("devtools")
|
|
85
|
+
.description("Open Free DevTools Dashboard")
|
|
86
|
+
.action(() => {
|
|
87
|
+
require("../cli/commands/devtools")();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
program
|
|
91
|
+
.command("test")
|
|
92
|
+
.description("Run Vitest Testing Suite")
|
|
93
|
+
.action(() => {
|
|
94
|
+
require("../cli/commands/test")();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
program
|
|
98
|
+
.command("install [packages...]")
|
|
99
|
+
.description("Install ecosystem NPM plugins")
|
|
100
|
+
.action((packages) => {
|
|
101
|
+
require("../cli/commands/install")(packages);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
program
|
|
105
|
+
.command("deploy")
|
|
106
|
+
.description("Generate Docker + PM2 deployment files")
|
|
107
|
+
.action(() => {
|
|
108
|
+
require("../cli/commands/deploy")();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
program
|
|
112
|
+
.command("doctor")
|
|
113
|
+
.description("Run project health diagnostics")
|
|
114
|
+
.action(() => {
|
|
115
|
+
require("../cli/commands/doctor")();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/build.js
|
|
3
|
+
* Implementation of 'free build' command.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require("fs-extra");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const { tokenize } = require("../../compiler/lexer");
|
|
9
|
+
const { parse } = require("../../compiler/parser");
|
|
10
|
+
const { generate } = require("../../compiler/generator");
|
|
11
|
+
const { analyze, printAnalysis } = require("../../compiler/analyzer");
|
|
12
|
+
|
|
13
|
+
module.exports = function () {
|
|
14
|
+
const baseDir = process.cwd();
|
|
15
|
+
const outputDir = path.join(baseDir, ".free");
|
|
16
|
+
|
|
17
|
+
if (!fs.existsSync(path.join(baseDir, "app"))) {
|
|
18
|
+
console.error("Error: 'app' directory not found. Are you in a Free Enterprise project?");
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(outputDir)) {
|
|
23
|
+
fs.mkdirSync(outputDir);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log("šØ Building Enterprise Project...");
|
|
27
|
+
|
|
28
|
+
const freeFiles = [];
|
|
29
|
+
function scanDir(dir) {
|
|
30
|
+
if (!fs.existsSync(dir)) return;
|
|
31
|
+
const files = fs.readdirSync(dir);
|
|
32
|
+
files.forEach(file => {
|
|
33
|
+
const filePath = path.join(dir, file);
|
|
34
|
+
if (fs.statSync(filePath).isDirectory()) {
|
|
35
|
+
scanDir(filePath);
|
|
36
|
+
} else if (file.endsWith(".free")) {
|
|
37
|
+
freeFiles.push(filePath);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Scan all enterprise directories for .free files
|
|
43
|
+
scanDir(path.join(baseDir, "app"));
|
|
44
|
+
scanDir(path.join(baseDir, "routes"));
|
|
45
|
+
scanDir(path.join(baseDir, "resources", "views"));
|
|
46
|
+
let combinedAST = [];
|
|
47
|
+
|
|
48
|
+
console.log(`š Found ${freeFiles.length} files to process.`);
|
|
49
|
+
freeFiles.forEach(filePath => {
|
|
50
|
+
const file = path.basename(filePath);
|
|
51
|
+
console.log(` š Processing ${file}...`);
|
|
52
|
+
const code = fs.readFileSync(filePath, "utf-8");
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const tokens = tokenize(code, file);
|
|
56
|
+
const ast = parse(tokens, file, code);
|
|
57
|
+
console.log(` ā
Parsed ${ast.length} nodes from ${file}`);
|
|
58
|
+
combinedAST = combinedAST.concat(ast);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
if (err.name === 'FreeSyntaxError') {
|
|
61
|
+
console.error(err.toString());
|
|
62
|
+
} else {
|
|
63
|
+
console.error(`Error in ${filePath}: ${err.message}`);
|
|
64
|
+
}
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// āā Static Analysis āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
70
|
+
console.log("š Running static analysis...");
|
|
71
|
+
const analysis = analyze(combinedAST, 'app/');
|
|
72
|
+
printAnalysis(analysis, 'app/');
|
|
73
|
+
if (!analysis.clean) {
|
|
74
|
+
console.error("ā Build aborted due to errors.");
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const jsCode = generate(combinedAST);
|
|
79
|
+
const outputPath = path.join(outputDir, "app.js");
|
|
80
|
+
|
|
81
|
+
fs.writeFileSync(outputPath, jsCode);
|
|
82
|
+
|
|
83
|
+
// Bundle client runtime
|
|
84
|
+
console.log("š¦ Bundling client assets...");
|
|
85
|
+
const { buildSync } = require("esbuild");
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const publicDir = path.join(process.cwd(), "public");
|
|
89
|
+
fs.ensureDirSync(publicDir);
|
|
90
|
+
|
|
91
|
+
buildSync({
|
|
92
|
+
entryPoints: [path.join(__dirname, "../../runtime/client.js")],
|
|
93
|
+
bundle: true,
|
|
94
|
+
minify: true,
|
|
95
|
+
outfile: path.join(publicDir, "free-runtime.js"),
|
|
96
|
+
});
|
|
97
|
+
console.log("ā
Client runtime bundled to public/free-runtime.js");
|
|
98
|
+
} catch (err) {
|
|
99
|
+
console.error("Bundling failed:", err.message || err);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// āā CSS Build (Tailwind) āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
103
|
+
console.log("šØ Compiling Tailwind CSS...");
|
|
104
|
+
const { execSync } = require("child_process");
|
|
105
|
+
try {
|
|
106
|
+
const publicCssDir = path.join(process.cwd(), "public/css");
|
|
107
|
+
fs.ensureDirSync(publicCssDir);
|
|
108
|
+
|
|
109
|
+
let tailwindInput = "./resources/css/app.css";
|
|
110
|
+
// Fallback for older projects
|
|
111
|
+
if (!fs.existsSync(path.join(process.cwd(), "resources/css/app.css"))) {
|
|
112
|
+
if (fs.existsSync(path.join(process.cwd(), "app/styles.css"))) {
|
|
113
|
+
tailwindInput = "./app/styles.css";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
execSync(`npx tailwindcss -i ${tailwindInput} -o ./public/css/app.css --minify`, { stdio: "inherit" });
|
|
118
|
+
console.log("ā
Tailwind CSS compiled to public/css/app.css");
|
|
119
|
+
} catch (err) {
|
|
120
|
+
console.error("Tailwind compilation failed:", err);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
console.log(`ā
Build complete! Compiled to ${outputPath}`);
|
|
124
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/deploy.js
|
|
3
|
+
* Free Framework Deployment Generator.
|
|
4
|
+
* Generates: Dockerfile, docker-compose.yml, .dockerignore, deploy.sh
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require("fs-extra");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
|
|
10
|
+
module.exports = function deploy(options = {}) {
|
|
11
|
+
const cwd = process.cwd();
|
|
12
|
+
const appName = require(path.join(cwd, "package.json")).name || "free-app";
|
|
13
|
+
const port = process.env.PORT || 3000;
|
|
14
|
+
|
|
15
|
+
console.log(`\nš Generating deployment files for "${appName}"...\n`);
|
|
16
|
+
|
|
17
|
+
// āā Dockerfile āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
18
|
+
fs.writeFileSync(path.join(cwd, "Dockerfile"), `# Free Framework ā Production Dockerfile
|
|
19
|
+
FROM node:20-alpine AS base
|
|
20
|
+
WORKDIR /app
|
|
21
|
+
|
|
22
|
+
# Install dependencies
|
|
23
|
+
COPY package*.json ./
|
|
24
|
+
RUN npm ci --only=production
|
|
25
|
+
|
|
26
|
+
# Copy source
|
|
27
|
+
COPY . .
|
|
28
|
+
|
|
29
|
+
# Build
|
|
30
|
+
RUN node bin/free.js build 2>/dev/null || true
|
|
31
|
+
|
|
32
|
+
EXPOSE ${port}
|
|
33
|
+
|
|
34
|
+
# Use clustered production start
|
|
35
|
+
CMD ["node", ".free/app.js"]
|
|
36
|
+
`);
|
|
37
|
+
|
|
38
|
+
// āā docker-compose.yml āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
39
|
+
fs.writeFileSync(path.join(cwd, "docker-compose.yml"), `version: "3.9"
|
|
40
|
+
|
|
41
|
+
services:
|
|
42
|
+
app:
|
|
43
|
+
build: .
|
|
44
|
+
container_name: ${appName}
|
|
45
|
+
restart: unless-stopped
|
|
46
|
+
ports:
|
|
47
|
+
- "${port}:${port}"
|
|
48
|
+
environment:
|
|
49
|
+
NODE_ENV: production
|
|
50
|
+
PORT: ${port}
|
|
51
|
+
# Add your .env values here or use --env-file .env
|
|
52
|
+
volumes:
|
|
53
|
+
- ./storage:/app/storage # persist file uploads
|
|
54
|
+
- ./database.sqlite:/app/database.sqlite # persist SQLite DB
|
|
55
|
+
|
|
56
|
+
# Uncomment for Redis cache support
|
|
57
|
+
# redis:
|
|
58
|
+
# image: redis:7-alpine
|
|
59
|
+
# restart: unless-stopped
|
|
60
|
+
# ports:
|
|
61
|
+
# - "6379:6379"
|
|
62
|
+
|
|
63
|
+
# Uncomment for PostgreSQL support
|
|
64
|
+
# postgres:
|
|
65
|
+
# image: postgres:16-alpine
|
|
66
|
+
# restart: unless-stopped
|
|
67
|
+
# environment:
|
|
68
|
+
# POSTGRES_DB: free_db
|
|
69
|
+
# POSTGRES_USER: postgres
|
|
70
|
+
# POSTGRES_PASSWORD: changeme
|
|
71
|
+
# ports:
|
|
72
|
+
# - "5432:5432"
|
|
73
|
+
# volumes:
|
|
74
|
+
# - pgdata:/var/lib/postgresql/data
|
|
75
|
+
|
|
76
|
+
# volumes:
|
|
77
|
+
# pgdata:
|
|
78
|
+
`);
|
|
79
|
+
|
|
80
|
+
// āā .dockerignore āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
81
|
+
fs.writeFileSync(path.join(cwd, ".dockerignore"), `node_modules
|
|
82
|
+
.free
|
|
83
|
+
*.log
|
|
84
|
+
.env
|
|
85
|
+
.git
|
|
86
|
+
coverage
|
|
87
|
+
demo-app
|
|
88
|
+
free-social-app
|
|
89
|
+
`);
|
|
90
|
+
|
|
91
|
+
// āā deploy.sh (VPS quick deploy) āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
92
|
+
fs.writeFileSync(path.join(cwd, "deploy.sh"), `#!/bin/bash
|
|
93
|
+
# Quick VPS deployment script for ${appName}
|
|
94
|
+
set -e
|
|
95
|
+
|
|
96
|
+
echo "š¦ Pulling latest changes..."
|
|
97
|
+
git pull origin main
|
|
98
|
+
|
|
99
|
+
echo "š„ Installing dependencies..."
|
|
100
|
+
npm ci --only=production
|
|
101
|
+
|
|
102
|
+
echo "šØ Building..."
|
|
103
|
+
node bin/free.js build
|
|
104
|
+
|
|
105
|
+
echo "š Restarting with PM2..."
|
|
106
|
+
pm2 restart ${appName} || pm2 start .free/app.js --name ${appName} --instances max
|
|
107
|
+
|
|
108
|
+
echo "ā
Deployed successfully!"
|
|
109
|
+
`);
|
|
110
|
+
fs.chmodSync(path.join(cwd, "deploy.sh"), "755");
|
|
111
|
+
|
|
112
|
+
// āā PM2 ecosystem config āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
113
|
+
fs.writeFileSync(path.join(cwd, "ecosystem.config.js"), `// PM2 Ecosystem Config for ${appName}
|
|
114
|
+
module.exports = {
|
|
115
|
+
apps: [{
|
|
116
|
+
name: "${appName}",
|
|
117
|
+
script: ".free/app.js",
|
|
118
|
+
instances: "max", // Use all CPU cores
|
|
119
|
+
exec_mode: "cluster",
|
|
120
|
+
env_production: {
|
|
121
|
+
NODE_ENV: "production",
|
|
122
|
+
PORT: ${port},
|
|
123
|
+
},
|
|
124
|
+
max_memory_restart: "500M",
|
|
125
|
+
error_file: "logs/err.log",
|
|
126
|
+
out_file: "logs/out.log",
|
|
127
|
+
}],
|
|
128
|
+
};
|
|
129
|
+
`);
|
|
130
|
+
|
|
131
|
+
console.log("ā
Deployment files generated:");
|
|
132
|
+
console.log(" š Dockerfile");
|
|
133
|
+
console.log(" š docker-compose.yml");
|
|
134
|
+
console.log(" š .dockerignore");
|
|
135
|
+
console.log(" š deploy.sh");
|
|
136
|
+
console.log(" š ecosystem.config.js (PM2)\n");
|
|
137
|
+
console.log("š³ Docker:");
|
|
138
|
+
console.log(" docker build -t " + appName + " .");
|
|
139
|
+
console.log(" docker-compose up -d\n");
|
|
140
|
+
console.log("š„ļø VPS (with PM2):");
|
|
141
|
+
console.log(" npm install -g pm2");
|
|
142
|
+
console.log(" bash deploy.sh\n");
|
|
143
|
+
};
|