free-framework 4.4.1 β 4.5.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/README.md +7 -1
- package/bin/free.js +51 -0
- package/cli/commands/bench.js +45 -0
- package/cli/commands/db_seed.js +39 -0
- package/cli/commands/make_crud.js +87 -0
- package/cli/commands/route_list.js +51 -0
- package/cli/commands/shell.js +43 -0
- package/cli/commands/update.js +34 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,7 +110,13 @@ error 404 {
|
|
|
110
110
|
| `free serve` | Start the development server |
|
|
111
111
|
| `free build` | Build for production (minified) |
|
|
112
112
|
| `free devtools` | Launch the DevTools Dashboard |
|
|
113
|
-
| `free
|
|
113
|
+
| `free doctor` | Run project health diagnostics |
|
|
114
|
+
| `free update` | Update Free Framework to the latest version |
|
|
115
|
+
| `free route:list` | List all registered routes |
|
|
116
|
+
| `free db:seed` | Run database seeders |
|
|
117
|
+
| `free make:crud <name>` | Scaffold full CRUD for a model |
|
|
118
|
+
| `free shell` | Enter interactive Free Shell |
|
|
119
|
+
| `free bench` | Benchmark current app performance |
|
|
114
120
|
| `free install <pkg>` | Install ecosystem plugins |
|
|
115
121
|
| `free make:model <name>` | Scaffold a new model |
|
|
116
122
|
|
package/bin/free.js
CHANGED
|
@@ -117,4 +117,55 @@ program
|
|
|
117
117
|
require("../cli/commands/doctor")();
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
+
program
|
|
121
|
+
.command("update")
|
|
122
|
+
.description("Update Free Framework to the latest version")
|
|
123
|
+
.action(() => {
|
|
124
|
+
require("../cli/commands/update")();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
program
|
|
128
|
+
.command("route:list")
|
|
129
|
+
.description("List all registered routes")
|
|
130
|
+
.action(() => {
|
|
131
|
+
require("../cli/commands/route_list")();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
program
|
|
135
|
+
.command("db:seed")
|
|
136
|
+
.description("Run database seeders")
|
|
137
|
+
.action(() => {
|
|
138
|
+
require("../cli/commands/db_seed")();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
program
|
|
142
|
+
.command("make:crud <name>")
|
|
143
|
+
.description("Scaffold full CRUD for a model")
|
|
144
|
+
.action((name) => {
|
|
145
|
+
require("../cli/commands/make_crud")(name);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
program
|
|
149
|
+
.command("shell")
|
|
150
|
+
.description("Enter interactive Free Shell")
|
|
151
|
+
.action(() => {
|
|
152
|
+
require("../cli/commands/shell")();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
program
|
|
156
|
+
.command("bench")
|
|
157
|
+
.description("Benchmark current app performance")
|
|
158
|
+
.option("-u, --url <url>", "URL to benchmark", "http://localhost:3000")
|
|
159
|
+
.option("-d, --duration <duration>", "Duration in seconds", 5)
|
|
160
|
+
.action((options) => {
|
|
161
|
+
require("../cli/commands/bench")(options);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
program
|
|
165
|
+
.command("update")
|
|
166
|
+
.description("Update Free Framework to the latest version")
|
|
167
|
+
.action(() => {
|
|
168
|
+
require("../cli/commands/update")();
|
|
169
|
+
});
|
|
170
|
+
|
|
120
171
|
program.parse(process.argv);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/bench.js
|
|
3
|
+
* Simple performance benchmarking tool.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const http = require("http");
|
|
7
|
+
|
|
8
|
+
module.exports = function (options) {
|
|
9
|
+
const url = options.url || "http://localhost:3000";
|
|
10
|
+
const duration = options.duration || 5; // seconds
|
|
11
|
+
console.log(`\nπ Benchmarking ${url} for ${duration}s...\n`);
|
|
12
|
+
|
|
13
|
+
let requests = 0;
|
|
14
|
+
let errors = 0;
|
|
15
|
+
const startTime = Date.now();
|
|
16
|
+
const endTime = startTime + (duration * 1000);
|
|
17
|
+
|
|
18
|
+
const runBench = () => {
|
|
19
|
+
if (Date.now() >= endTime) {
|
|
20
|
+
const totalTime = (Date.now() - startTime) / 1000;
|
|
21
|
+
const rps = (requests / totalTime).toFixed(2);
|
|
22
|
+
console.log(`\nπ Results:`);
|
|
23
|
+
console.log(` Total Requests: ${requests}`);
|
|
24
|
+
console.log(` Total Errors: ${errors}`);
|
|
25
|
+
console.log(` RPS: ${rps} req/s\n`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const req = http.get(url, (res) => {
|
|
30
|
+
requests++;
|
|
31
|
+
res.on('data', () => { });
|
|
32
|
+
res.on('end', runBench);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
req.on('error', (e) => {
|
|
36
|
+
errors++;
|
|
37
|
+
runBench();
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Parallelize with multiple concurrent workers
|
|
42
|
+
for (let i = 0; i < 10; i++) {
|
|
43
|
+
runBench();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/db_seed.js
|
|
3
|
+
* Runs database seeders to populate the environment.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require("fs-extra");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
|
|
9
|
+
module.exports = async function () {
|
|
10
|
+
console.log("\nπ± Free Framework - Seeding Database...\n");
|
|
11
|
+
|
|
12
|
+
const seederDir = path.join(process.cwd(), "database/seeders");
|
|
13
|
+
if (!fs.existsSync(seederDir)) {
|
|
14
|
+
console.log("β No database/seeders directory found.");
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const files = fs.readdirSync(seederDir).filter(f => f.endsWith(".js"));
|
|
19
|
+
if (files.length === 0) {
|
|
20
|
+
console.log("βΉοΈ No seeder files found.");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const { ORM } = require("../../database/orm");
|
|
26
|
+
const knex = new ORM().knex;
|
|
27
|
+
|
|
28
|
+
for (const file of files) {
|
|
29
|
+
console.log(` πΈ Running seeder: ${file}...`);
|
|
30
|
+
const seeder = require(path.join(seederDir, file));
|
|
31
|
+
await seeder.run(knex);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log("\nβ
Database seeding completed successfully.");
|
|
35
|
+
} catch (err) {
|
|
36
|
+
console.error("\nβ Seeding failed:", err.message);
|
|
37
|
+
}
|
|
38
|
+
console.log("");
|
|
39
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/make_crud.js
|
|
3
|
+
* Scaffolds Model, Controller, Views, and Routes for a resource.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require("fs-extra");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
|
|
9
|
+
module.exports = function (name) {
|
|
10
|
+
const plural = name.toLowerCase() + "s";
|
|
11
|
+
const singular = name.toLowerCase();
|
|
12
|
+
const className = name.charAt(0) + name.slice(1);
|
|
13
|
+
|
|
14
|
+
console.log(`\nποΈ Scaffolding CRUD for resource: ${className}\n`);
|
|
15
|
+
|
|
16
|
+
// 1. Create Model
|
|
17
|
+
const modelPath = path.join(process.cwd(), `app/models/${className}.free`);
|
|
18
|
+
if (!fs.existsSync(modelPath)) {
|
|
19
|
+
const modelContent = `model ${className} {
|
|
20
|
+
title string
|
|
21
|
+
content text
|
|
22
|
+
status string
|
|
23
|
+
}\n`;
|
|
24
|
+
fs.ensureDirSync(path.dirname(modelPath));
|
|
25
|
+
fs.writeFileSync(modelPath, modelContent);
|
|
26
|
+
console.log(` β
Created Model: app/models/${className}.free`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 2. Create Controller
|
|
30
|
+
const controllerPath = path.join(process.cwd(), `app/Http/Controllers/${className}Controller.free`);
|
|
31
|
+
const controllerContent = `// ${className} Controller
|
|
32
|
+
action list${plural} {
|
|
33
|
+
const items = await ${className}.all();
|
|
34
|
+
return items;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
action get${className} {
|
|
38
|
+
const item = await ${className}.find(params.id);
|
|
39
|
+
return item;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
action create${className} {
|
|
43
|
+
const item = await ${className}.create(body);
|
|
44
|
+
return item;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
action delete${className} {
|
|
48
|
+
await ${className}.delete(params.id);
|
|
49
|
+
return { success: true };
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
fs.ensureDirSync(path.dirname(controllerPath));
|
|
53
|
+
fs.writeFileSync(controllerPath, controllerContent);
|
|
54
|
+
console.log(` β
Created Controller: app/Http/Controllers/${className}Controller.free`);
|
|
55
|
+
|
|
56
|
+
// 3. Create Basic View
|
|
57
|
+
const viewPath = path.join(process.cwd(), `resources/views/${plural}.free`);
|
|
58
|
+
const viewContent = `page ${plural} {
|
|
59
|
+
h1 { text "Manage ${plural}" }
|
|
60
|
+
|
|
61
|
+
component ${className}List {
|
|
62
|
+
onMount {
|
|
63
|
+
state.items = await actions.list${plural}()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
loop item in items {
|
|
67
|
+
div {
|
|
68
|
+
h3 { text item.title }
|
|
69
|
+
p { text item.content }
|
|
70
|
+
button {
|
|
71
|
+
text "Delete"
|
|
72
|
+
on-click {
|
|
73
|
+
await actions.delete${className}(item.id)
|
|
74
|
+
state.items = await actions.list${plural}()
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
82
|
+
fs.ensureDirSync(path.dirname(viewPath));
|
|
83
|
+
fs.writeFileSync(viewPath, viewContent);
|
|
84
|
+
console.log(` β
Created View: resources/views/${plural}.free`);
|
|
85
|
+
|
|
86
|
+
console.log(`\nπ CRUD scaffolded! Don't forget to register the routes in app/routes.free\n`);
|
|
87
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/route_list.js
|
|
3
|
+
* Displays a table of all registered routes in the project.
|
|
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
|
+
|
|
11
|
+
module.exports = function () {
|
|
12
|
+
console.log("\nπΊοΈ Free Framework - Route Registry\n");
|
|
13
|
+
|
|
14
|
+
const baseDir = process.cwd();
|
|
15
|
+
const routesFile = path.join(baseDir, "app/routes.free");
|
|
16
|
+
|
|
17
|
+
// Check multiple possible locations for routes
|
|
18
|
+
const paths = [
|
|
19
|
+
path.join(baseDir, "app/routes.free"),
|
|
20
|
+
path.join(baseDir, "routes/web.free"),
|
|
21
|
+
path.join(baseDir, "routes/api.free")
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const allRoutes = [];
|
|
25
|
+
|
|
26
|
+
paths.forEach(p => {
|
|
27
|
+
if (fs.existsSync(p)) {
|
|
28
|
+
try {
|
|
29
|
+
const code = fs.readFileSync(p, "utf8");
|
|
30
|
+
const tokens = tokenize(code, path.basename(p));
|
|
31
|
+
const ast = parse(tokens, path.basename(p), code);
|
|
32
|
+
|
|
33
|
+
ast.filter(n => n.type === 'route').forEach(r => {
|
|
34
|
+
allRoutes.push({
|
|
35
|
+
method: r.method.toUpperCase(),
|
|
36
|
+
path: r.path,
|
|
37
|
+
handler: r.body.some(b => b.type === 'view') ? `View: ${r.body.find(b => b.type === 'view').viewName}` : 'Action/Logic',
|
|
38
|
+
file: path.basename(p)
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
} catch (e) { }
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (allRoutes.length === 0) {
|
|
46
|
+
console.log("βΉοΈ No routes found in project.");
|
|
47
|
+
} else {
|
|
48
|
+
console.table(allRoutes);
|
|
49
|
+
}
|
|
50
|
+
console.log("");
|
|
51
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/shell.js
|
|
3
|
+
* Interactive REPL for Free Framework.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const repl = require("repl");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const fs = require("fs");
|
|
9
|
+
|
|
10
|
+
module.exports = function () {
|
|
11
|
+
console.log("\nπ Free Framework Interactive Shell");
|
|
12
|
+
console.log("Type '.exit' to leave. Global 'Model' and project models are available.\n");
|
|
13
|
+
|
|
14
|
+
const r = repl.start({
|
|
15
|
+
prompt: "free > ",
|
|
16
|
+
useGlobal: true
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Auto-load project context
|
|
20
|
+
try {
|
|
21
|
+
const { ORM } = require("../../database/orm");
|
|
22
|
+
const { Model } = require("../../database/model");
|
|
23
|
+
|
|
24
|
+
r.context.ORM = ORM;
|
|
25
|
+
r.context.Model = Model;
|
|
26
|
+
r.context.db = new ORM().knex;
|
|
27
|
+
|
|
28
|
+
// Try to load models from app/models
|
|
29
|
+
const modelDir = path.join(process.cwd(), "app/models");
|
|
30
|
+
if (fs.existsSync(modelDir)) {
|
|
31
|
+
fs.readdirSync(modelDir).forEach(file => {
|
|
32
|
+
if (file.endsWith(".free")) {
|
|
33
|
+
const modelName = path.basename(file, ".free");
|
|
34
|
+
// In a real scenario, we'd need to require the compiled JS version
|
|
35
|
+
// For now we expose the base Model class Proxy logic
|
|
36
|
+
r.context[modelName] = Model.extend(modelName);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
} catch (e) {
|
|
41
|
+
console.log("β οΈ Context partial load: " + e.message);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/commands/update.js
|
|
3
|
+
* Implementation of 'free update' command to keep the framework current.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { execSync } = require("child_process");
|
|
7
|
+
const pkg = require("../../package.json");
|
|
8
|
+
|
|
9
|
+
module.exports = function () {
|
|
10
|
+
console.log("\nπ Checking for Free Framework updates...\n");
|
|
11
|
+
console.log(`Current version: ${pkg.version}`);
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
console.log("π‘ Fetching latest version from NPM...");
|
|
15
|
+
// Use npm view to get the latest version
|
|
16
|
+
const latest = execSync("npm view free-framework version").toString().trim();
|
|
17
|
+
|
|
18
|
+
if (latest === pkg.version) {
|
|
19
|
+
console.log("β
You are already using the latest version.");
|
|
20
|
+
} else {
|
|
21
|
+
console.log(`π New version available: ${latest}`);
|
|
22
|
+
console.log("π οΈ Updating global Free Framework CLI...");
|
|
23
|
+
|
|
24
|
+
// Run global install
|
|
25
|
+
execSync("npm install -g free-framework@latest", { stdio: "inherit" });
|
|
26
|
+
|
|
27
|
+
console.log("\nβ
Update complete! Enjoy the new features.");
|
|
28
|
+
}
|
|
29
|
+
} catch (err) {
|
|
30
|
+
console.error("\nβ Update failed. Please ensure you have an internet connection and proper permissions.");
|
|
31
|
+
console.log("π‘ Try running: sudo npm install -g free-framework@latest");
|
|
32
|
+
}
|
|
33
|
+
console.log("");
|
|
34
|
+
};
|