@erwininteractive/mvc 0.1.1
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 +174 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +61 -0
- package/dist/framework/App.d.ts +38 -0
- package/dist/framework/App.js +100 -0
- package/dist/framework/Auth.d.ts +27 -0
- package/dist/framework/Auth.js +67 -0
- package/dist/framework/Router.d.ts +29 -0
- package/dist/framework/Router.js +125 -0
- package/dist/framework/db.d.ts +13 -0
- package/dist/framework/db.js +41 -0
- package/dist/framework/index.d.ts +5 -0
- package/dist/framework/index.js +22 -0
- package/dist/generators/generateController.d.ts +7 -0
- package/dist/generators/generateController.js +110 -0
- package/dist/generators/generateModel.d.ts +7 -0
- package/dist/generators/generateModel.js +77 -0
- package/dist/generators/initApp.d.ts +8 -0
- package/dist/generators/initApp.js +113 -0
- package/dist/generators/paths.d.ts +17 -0
- package/dist/generators/paths.js +55 -0
- package/package.json +72 -0
- package/prisma/schema.prisma +19 -0
- package/templates/appScaffold/README.md +297 -0
- package/templates/appScaffold/package.json +23 -0
- package/templates/appScaffold/public/favicon.svg +16 -0
- package/templates/appScaffold/src/controllers/HomeController.ts +9 -0
- package/templates/appScaffold/src/middleware/auth.ts +8 -0
- package/templates/appScaffold/src/server.ts +24 -0
- package/templates/appScaffold/src/views/index.ejs +300 -0
- package/templates/appScaffold/tsconfig.json +16 -0
- package/templates/controller.ts.ejs +98 -0
- package/templates/model.prisma.ejs +7 -0
- package/templates/view.ejs.ejs +42 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerController = exports.registerControllers = exports.authenticate = exports.verifyToken = exports.signToken = exports.verifyPassword = exports.hashPassword = exports.disconnectPrisma = exports.getPrismaClient = exports.startServer = exports.createMvcApp = void 0;
|
|
4
|
+
// App factory and server
|
|
5
|
+
var App_1 = require("./App");
|
|
6
|
+
Object.defineProperty(exports, "createMvcApp", { enumerable: true, get: function () { return App_1.createMvcApp; } });
|
|
7
|
+
Object.defineProperty(exports, "startServer", { enumerable: true, get: function () { return App_1.startServer; } });
|
|
8
|
+
// Database
|
|
9
|
+
var db_1 = require("./db");
|
|
10
|
+
Object.defineProperty(exports, "getPrismaClient", { enumerable: true, get: function () { return db_1.getPrismaClient; } });
|
|
11
|
+
Object.defineProperty(exports, "disconnectPrisma", { enumerable: true, get: function () { return db_1.disconnectPrisma; } });
|
|
12
|
+
// Authentication
|
|
13
|
+
var Auth_1 = require("./Auth");
|
|
14
|
+
Object.defineProperty(exports, "hashPassword", { enumerable: true, get: function () { return Auth_1.hashPassword; } });
|
|
15
|
+
Object.defineProperty(exports, "verifyPassword", { enumerable: true, get: function () { return Auth_1.verifyPassword; } });
|
|
16
|
+
Object.defineProperty(exports, "signToken", { enumerable: true, get: function () { return Auth_1.signToken; } });
|
|
17
|
+
Object.defineProperty(exports, "verifyToken", { enumerable: true, get: function () { return Auth_1.verifyToken; } });
|
|
18
|
+
Object.defineProperty(exports, "authenticate", { enumerable: true, get: function () { return Auth_1.authenticate; } });
|
|
19
|
+
// Routing
|
|
20
|
+
var Router_1 = require("./Router");
|
|
21
|
+
Object.defineProperty(exports, "registerControllers", { enumerable: true, get: function () { return Router_1.registerControllers; } });
|
|
22
|
+
Object.defineProperty(exports, "registerController", { enumerable: true, get: function () { return Router_1.registerController; } });
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateController = generateController;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const ejs_1 = __importDefault(require("ejs"));
|
|
10
|
+
const paths_1 = require("./paths");
|
|
11
|
+
/**
|
|
12
|
+
* Generate a CRUD controller.
|
|
13
|
+
*/
|
|
14
|
+
async function generateController(name, options = {}) {
|
|
15
|
+
const { views = true } = options;
|
|
16
|
+
const modelName = capitalize(name);
|
|
17
|
+
const lowerModelName = name.toLowerCase();
|
|
18
|
+
const controllerName = `${modelName}Controller`;
|
|
19
|
+
const resourcePath = pluralize(lowerModelName);
|
|
20
|
+
console.log(`Generating controller: ${controllerName}`);
|
|
21
|
+
// Ensure controllers directory exists
|
|
22
|
+
const controllersDir = path_1.default.resolve("src/controllers");
|
|
23
|
+
if (!fs_1.default.existsSync(controllersDir)) {
|
|
24
|
+
fs_1.default.mkdirSync(controllersDir, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
// Load and render controller template
|
|
27
|
+
const templatePath = path_1.default.join((0, paths_1.getTemplatesDir)(), "controller.ts.ejs");
|
|
28
|
+
if (!fs_1.default.existsSync(templatePath)) {
|
|
29
|
+
throw new Error("Controller template not found");
|
|
30
|
+
}
|
|
31
|
+
const template = fs_1.default.readFileSync(templatePath, "utf-8");
|
|
32
|
+
const controllerContent = ejs_1.default.render(template, {
|
|
33
|
+
modelName,
|
|
34
|
+
lowerModelName,
|
|
35
|
+
controllerName,
|
|
36
|
+
resourcePath,
|
|
37
|
+
});
|
|
38
|
+
// Write controller file
|
|
39
|
+
const controllerPath = path_1.default.join(controllersDir, `${controllerName}.ts`);
|
|
40
|
+
if (fs_1.default.existsSync(controllerPath)) {
|
|
41
|
+
throw new Error(`Controller ${controllerName}.ts already exists`);
|
|
42
|
+
}
|
|
43
|
+
fs_1.default.writeFileSync(controllerPath, controllerContent);
|
|
44
|
+
console.log(`Created src/controllers/${controllerName}.ts`);
|
|
45
|
+
// Generate views if enabled
|
|
46
|
+
if (views) {
|
|
47
|
+
await generateViews(lowerModelName, modelName);
|
|
48
|
+
}
|
|
49
|
+
console.log(`
|
|
50
|
+
Controller ${controllerName} created successfully!
|
|
51
|
+
|
|
52
|
+
Routes:
|
|
53
|
+
GET /${resourcePath} -> index
|
|
54
|
+
GET /${resourcePath}/:id -> show
|
|
55
|
+
POST /${resourcePath} -> store
|
|
56
|
+
PUT /${resourcePath}/:id -> update
|
|
57
|
+
DELETE /${resourcePath}/:id -> destroy
|
|
58
|
+
`);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Generate basic views for a resource.
|
|
62
|
+
*/
|
|
63
|
+
async function generateViews(lowerName, modelName) {
|
|
64
|
+
const viewsDir = path_1.default.resolve(`src/views/${lowerName}`);
|
|
65
|
+
if (!fs_1.default.existsSync(viewsDir)) {
|
|
66
|
+
fs_1.default.mkdirSync(viewsDir, { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
// Load view template
|
|
69
|
+
const viewTemplatePath = path_1.default.join((0, paths_1.getTemplatesDir)(), "view.ejs.ejs");
|
|
70
|
+
if (!fs_1.default.existsSync(viewTemplatePath)) {
|
|
71
|
+
console.warn("View template not found, skipping view generation");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const viewTemplate = fs_1.default.readFileSync(viewTemplatePath, "utf-8");
|
|
75
|
+
// Generate index view
|
|
76
|
+
const indexContent = ejs_1.default.render(viewTemplate, {
|
|
77
|
+
title: `${modelName} List`,
|
|
78
|
+
modelName,
|
|
79
|
+
lowerName,
|
|
80
|
+
viewType: "index",
|
|
81
|
+
});
|
|
82
|
+
fs_1.default.writeFileSync(path_1.default.join(viewsDir, "index.ejs"), indexContent);
|
|
83
|
+
// Generate show view
|
|
84
|
+
const showContent = ejs_1.default.render(viewTemplate, {
|
|
85
|
+
title: `${modelName} Details`,
|
|
86
|
+
modelName,
|
|
87
|
+
lowerName,
|
|
88
|
+
viewType: "show",
|
|
89
|
+
});
|
|
90
|
+
fs_1.default.writeFileSync(path_1.default.join(viewsDir, "show.ejs"), showContent);
|
|
91
|
+
console.log(`Created views in src/views/${lowerName}/`);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Capitalize the first letter of a string.
|
|
95
|
+
*/
|
|
96
|
+
function capitalize(str) {
|
|
97
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Simple pluralization.
|
|
101
|
+
*/
|
|
102
|
+
function pluralize(str) {
|
|
103
|
+
if (str.endsWith("s")) {
|
|
104
|
+
return str;
|
|
105
|
+
}
|
|
106
|
+
if (str.endsWith("y")) {
|
|
107
|
+
return str.slice(0, -1) + "ies";
|
|
108
|
+
}
|
|
109
|
+
return str + "s";
|
|
110
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateModel = generateModel;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const ejs_1 = __importDefault(require("ejs"));
|
|
10
|
+
const child_process_1 = require("child_process");
|
|
11
|
+
const paths_1 = require("./paths");
|
|
12
|
+
/**
|
|
13
|
+
* Generate a Prisma model and append it to schema.prisma.
|
|
14
|
+
*/
|
|
15
|
+
async function generateModel(name, options = {}) {
|
|
16
|
+
const modelName = capitalize(name);
|
|
17
|
+
const tableName = pluralize(name.toLowerCase());
|
|
18
|
+
console.log(`Generating model: ${modelName}`);
|
|
19
|
+
// Find prisma schema
|
|
20
|
+
const schemaPath = path_1.default.resolve("prisma/schema.prisma");
|
|
21
|
+
if (!fs_1.default.existsSync(schemaPath)) {
|
|
22
|
+
throw new Error("prisma/schema.prisma not found. Are you in a project directory?");
|
|
23
|
+
}
|
|
24
|
+
// Load and render template
|
|
25
|
+
const templatePath = path_1.default.join((0, paths_1.getTemplatesDir)(), "model.prisma.ejs");
|
|
26
|
+
if (!fs_1.default.existsSync(templatePath)) {
|
|
27
|
+
throw new Error("Model template not found");
|
|
28
|
+
}
|
|
29
|
+
const template = fs_1.default.readFileSync(templatePath, "utf-8");
|
|
30
|
+
const modelContent = ejs_1.default.render(template, { modelName, tableName });
|
|
31
|
+
// Check if model already exists
|
|
32
|
+
const existingSchema = fs_1.default.readFileSync(schemaPath, "utf-8");
|
|
33
|
+
if (existingSchema.includes(`model ${modelName} {`)) {
|
|
34
|
+
throw new Error(`Model ${modelName} already exists in schema.prisma`);
|
|
35
|
+
}
|
|
36
|
+
// Append model to schema
|
|
37
|
+
fs_1.default.appendFileSync(schemaPath, "\n" + modelContent);
|
|
38
|
+
console.log(`Added model ${modelName} to prisma/schema.prisma`);
|
|
39
|
+
// Run Prisma migrate
|
|
40
|
+
if (!options.skipMigrate) {
|
|
41
|
+
console.log("\nRunning Prisma migrate...");
|
|
42
|
+
try {
|
|
43
|
+
(0, child_process_1.execSync)(`npx prisma migrate dev --name add-${name.toLowerCase()}`, {
|
|
44
|
+
stdio: "inherit",
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
console.error("Migration failed. You may need to run it manually.");
|
|
49
|
+
}
|
|
50
|
+
console.log("\nGenerating Prisma client...");
|
|
51
|
+
try {
|
|
52
|
+
(0, child_process_1.execSync)("npx prisma generate", { stdio: "inherit" });
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
console.error("Failed to generate Prisma client.");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
console.log(`\nModel ${modelName} created successfully!`);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Capitalize the first letter of a string.
|
|
62
|
+
*/
|
|
63
|
+
function capitalize(str) {
|
|
64
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Simple pluralization - add 's' if not already ending in 's'.
|
|
68
|
+
*/
|
|
69
|
+
function pluralize(str) {
|
|
70
|
+
if (str.endsWith("s")) {
|
|
71
|
+
return str;
|
|
72
|
+
}
|
|
73
|
+
if (str.endsWith("y")) {
|
|
74
|
+
return str.slice(0, -1) + "ies";
|
|
75
|
+
}
|
|
76
|
+
return str + "s";
|
|
77
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.initApp = initApp;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
const paths_1 = require("./paths");
|
|
11
|
+
/**
|
|
12
|
+
* Scaffold a new MVC application.
|
|
13
|
+
*/
|
|
14
|
+
async function initApp(dir, options = {}) {
|
|
15
|
+
const targetDir = path_1.default.resolve(dir);
|
|
16
|
+
const templateDir = path_1.default.join((0, paths_1.getTemplatesDir)(), "appScaffold");
|
|
17
|
+
console.log(`Creating new MVC application in ${targetDir}...`);
|
|
18
|
+
// Create target directory
|
|
19
|
+
if (!fs_1.default.existsSync(targetDir)) {
|
|
20
|
+
fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
// Copy app scaffold templates
|
|
23
|
+
copyDirRecursive(templateDir, targetDir);
|
|
24
|
+
// Copy .env.example
|
|
25
|
+
const envExample = (0, paths_1.getEnvExamplePath)();
|
|
26
|
+
if (fs_1.default.existsSync(envExample)) {
|
|
27
|
+
fs_1.default.copyFileSync(envExample, path_1.default.join(targetDir, ".env.example"));
|
|
28
|
+
}
|
|
29
|
+
// Update package.json with app name and framework path
|
|
30
|
+
const appPackageJson = path_1.default.join(targetDir, "package.json");
|
|
31
|
+
if (fs_1.default.existsSync(appPackageJson)) {
|
|
32
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(appPackageJson, "utf-8"));
|
|
33
|
+
pkg.name = path_1.default.basename(dir);
|
|
34
|
+
// Use relative path to framework for local development
|
|
35
|
+
// When published, this would be the npm package version
|
|
36
|
+
const frameworkRoot = (0, paths_1.getPackageRoot)();
|
|
37
|
+
const relativePath = path_1.default.relative(targetDir, frameworkRoot);
|
|
38
|
+
pkg.dependencies["@erwininteractive/mvc"] = `file:${relativePath}`;
|
|
39
|
+
fs_1.default.writeFileSync(appPackageJson, JSON.stringify(pkg, null, 2));
|
|
40
|
+
}
|
|
41
|
+
console.log("Application scaffolded successfully!");
|
|
42
|
+
// Install dependencies
|
|
43
|
+
if (!options.skipInstall) {
|
|
44
|
+
console.log("\nInstalling dependencies...");
|
|
45
|
+
try {
|
|
46
|
+
(0, child_process_1.execSync)("npm install", { cwd: targetDir, stdio: "inherit" });
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
console.error("Failed to install dependencies. Please run 'npm install' manually.");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Setup database if requested
|
|
53
|
+
if (options.withDatabase) {
|
|
54
|
+
setupDatabase(targetDir);
|
|
55
|
+
}
|
|
56
|
+
console.log(`
|
|
57
|
+
Next steps:
|
|
58
|
+
cd ${dir}
|
|
59
|
+
npm run dev
|
|
60
|
+
|
|
61
|
+
Your app is ready! Visit http://localhost:3000
|
|
62
|
+
${!options.withDatabase ? `
|
|
63
|
+
To add database support later:
|
|
64
|
+
npm run db:setup
|
|
65
|
+
# Edit .env with DATABASE_URL
|
|
66
|
+
npx prisma migrate dev --name init
|
|
67
|
+
` : ""}`);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Setup Prisma database support.
|
|
71
|
+
*/
|
|
72
|
+
function setupDatabase(targetDir) {
|
|
73
|
+
// Copy prisma schema
|
|
74
|
+
const prismaDir = path_1.default.join(targetDir, "prisma");
|
|
75
|
+
if (!fs_1.default.existsSync(prismaDir)) {
|
|
76
|
+
fs_1.default.mkdirSync(prismaDir, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
const frameworkPrismaSchema = path_1.default.join((0, paths_1.getPrismaDir)(), "schema.prisma");
|
|
79
|
+
if (fs_1.default.existsSync(frameworkPrismaSchema)) {
|
|
80
|
+
fs_1.default.copyFileSync(frameworkPrismaSchema, path_1.default.join(prismaDir, "schema.prisma"));
|
|
81
|
+
}
|
|
82
|
+
// Install Prisma
|
|
83
|
+
console.log("\nSetting up database...");
|
|
84
|
+
try {
|
|
85
|
+
(0, child_process_1.execSync)("npm install @prisma/client prisma", { cwd: targetDir, stdio: "inherit" });
|
|
86
|
+
(0, child_process_1.execSync)("npx prisma generate", { cwd: targetDir, stdio: "inherit" });
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
console.error("Failed to setup Prisma. Run 'npm run db:setup' manually.");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Recursively copy a directory.
|
|
94
|
+
*/
|
|
95
|
+
function copyDirRecursive(src, dest) {
|
|
96
|
+
if (!fs_1.default.existsSync(src)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (!fs_1.default.existsSync(dest)) {
|
|
100
|
+
fs_1.default.mkdirSync(dest, { recursive: true });
|
|
101
|
+
}
|
|
102
|
+
const entries = fs_1.default.readdirSync(src, { withFileTypes: true });
|
|
103
|
+
for (const entry of entries) {
|
|
104
|
+
const srcPath = path_1.default.join(src, entry.name);
|
|
105
|
+
const destPath = path_1.default.join(dest, entry.name);
|
|
106
|
+
if (entry.isDirectory()) {
|
|
107
|
+
copyDirRecursive(srcPath, destPath);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
fs_1.default.copyFileSync(srcPath, destPath);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the root directory of the framework package.
|
|
3
|
+
* Works whether running from src/ (development) or dist/ (production).
|
|
4
|
+
*/
|
|
5
|
+
export declare function getPackageRoot(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Get the templates directory path.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getTemplatesDir(): string;
|
|
10
|
+
/**
|
|
11
|
+
* Get the prisma directory path.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getPrismaDir(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Get the .env.example file path.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getEnvExamplePath(): string;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getPackageRoot = getPackageRoot;
|
|
7
|
+
exports.getTemplatesDir = getTemplatesDir;
|
|
8
|
+
exports.getPrismaDir = getPrismaDir;
|
|
9
|
+
exports.getEnvExamplePath = getEnvExamplePath;
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const fs_1 = __importDefault(require("fs"));
|
|
12
|
+
/**
|
|
13
|
+
* Get the root directory of the framework package.
|
|
14
|
+
* Works whether running from src/ (development) or dist/ (production).
|
|
15
|
+
*/
|
|
16
|
+
function getPackageRoot() {
|
|
17
|
+
// Start from this file's directory and walk up to find package.json
|
|
18
|
+
let dir = __dirname;
|
|
19
|
+
// Walk up at most 5 levels to find package.json with our package name
|
|
20
|
+
for (let i = 0; i < 5; i++) {
|
|
21
|
+
const packageJsonPath = path_1.default.join(dir, "package.json");
|
|
22
|
+
if (fs_1.default.existsSync(packageJsonPath)) {
|
|
23
|
+
try {
|
|
24
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(packageJsonPath, "utf-8"));
|
|
25
|
+
if (pkg.name === "@erwininteractive/mvc") {
|
|
26
|
+
return dir;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Continue searching
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
dir = path_1.default.dirname(dir);
|
|
34
|
+
}
|
|
35
|
+
// Fallback: assume we're in dist/generators or src/generators
|
|
36
|
+
return path_1.default.resolve(__dirname, "../..");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get the templates directory path.
|
|
40
|
+
*/
|
|
41
|
+
function getTemplatesDir() {
|
|
42
|
+
return path_1.default.join(getPackageRoot(), "templates");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get the prisma directory path.
|
|
46
|
+
*/
|
|
47
|
+
function getPrismaDir() {
|
|
48
|
+
return path_1.default.join(getPackageRoot(), "prisma");
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get the .env.example file path.
|
|
52
|
+
*/
|
|
53
|
+
function getEnvExamplePath() {
|
|
54
|
+
return path_1.default.join(getPackageRoot(), ".env.example");
|
|
55
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@erwininteractive/mvc",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "A lightweight, full-featured MVC framework for Node.js with Express, Prisma, and EJS",
|
|
5
|
+
"main": "dist/framework/index.js",
|
|
6
|
+
"types": "dist/framework/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"erwinmvc": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/**",
|
|
12
|
+
"prisma/**",
|
|
13
|
+
"templates/**",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p tsconfig.json && node scripts/build-cli.js",
|
|
18
|
+
"dev": "tsx src/cli.ts",
|
|
19
|
+
"test": "jest",
|
|
20
|
+
"prepare": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mvc",
|
|
24
|
+
"framework",
|
|
25
|
+
"express",
|
|
26
|
+
"prisma",
|
|
27
|
+
"ejs",
|
|
28
|
+
"typescript",
|
|
29
|
+
"node"
|
|
30
|
+
],
|
|
31
|
+
"author": "Erwin Interactive",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/erwininteractive/mvc.git"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=20.0.0"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@prisma/client": "^6.0.0",
|
|
42
|
+
"bcryptjs": "^2.4.3",
|
|
43
|
+
"commander": "^12.1.0",
|
|
44
|
+
"connect-redis": "^7.1.1",
|
|
45
|
+
"cors": "^2.8.5",
|
|
46
|
+
"dotenv": "^16.4.5",
|
|
47
|
+
"ejs": "^3.1.10",
|
|
48
|
+
"express": "^4.21.0",
|
|
49
|
+
"express-session": "^1.18.0",
|
|
50
|
+
"helmet": "^8.0.0",
|
|
51
|
+
"jsonwebtoken": "^9.0.2",
|
|
52
|
+
"prisma": "^6.0.0",
|
|
53
|
+
"redis": "^4.7.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/bcryptjs": "^2.4.6",
|
|
57
|
+
"@types/cors": "^2.8.17",
|
|
58
|
+
"@types/ejs": "^3.1.5",
|
|
59
|
+
"@types/express": "^5.0.0",
|
|
60
|
+
"@types/express-session": "^1.18.0",
|
|
61
|
+
"@types/jest": "^29.5.13",
|
|
62
|
+
"@types/jsonwebtoken": "^9.0.7",
|
|
63
|
+
"@types/node": "^22.7.5",
|
|
64
|
+
"@types/supertest": "^6.0.2",
|
|
65
|
+
"esbuild": "^0.24.0",
|
|
66
|
+
"jest": "^29.7.0",
|
|
67
|
+
"supertest": "^7.0.0",
|
|
68
|
+
"ts-jest": "^29.2.5",
|
|
69
|
+
"tsx": "^4.19.1",
|
|
70
|
+
"typescript": "^5.6.3"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
generator client {
|
|
2
|
+
provider = "prisma-client-js"
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
datasource db {
|
|
6
|
+
provider = "postgresql"
|
|
7
|
+
url = env("DATABASE_URL")
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
model User {
|
|
11
|
+
id Int @id @default(autoincrement())
|
|
12
|
+
email String @unique
|
|
13
|
+
hashedPassword String
|
|
14
|
+
role String @default("user")
|
|
15
|
+
createdAt DateTime @default(now())
|
|
16
|
+
updatedAt DateTime @updatedAt
|
|
17
|
+
|
|
18
|
+
@@map("users")
|
|
19
|
+
}
|