db-model-router 1.0.3 → 1.0.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.
- package/README.md +178 -14
- package/docs/SKILL.md +154 -109
- package/package.json +10 -2
- package/src/cli/commands/help.js +180 -0
- package/src/cli/commands/init.js +42 -14
- package/src/cli/commands/inspect.js +20 -3
- package/src/cli/generate-model.js +5 -4
- package/src/cli/generate-route.js +28 -22
- package/src/cli/init/dependencies.js +14 -5
- package/src/cli/init/generators.js +1073 -64
- package/src/cli/init/prompt.js +37 -5
- package/src/cli/init.js +148 -25
- package/src/cli/main.js +90 -10
- package/src/index.js +2 -0
- package/src/schema/schema-printer.js +1 -5
- package/src/schema/schema-to-meta.js +4 -0
- package/src/schema/schema-validator.js +3 -1
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Per-command detailed help text.
|
|
5
|
+
* Each key matches a subcommand name from main.js.
|
|
6
|
+
*/
|
|
7
|
+
const COMMAND_HELP = {
|
|
8
|
+
init: `Usage: db-model-router init [options]
|
|
9
|
+
|
|
10
|
+
Scaffold a new project from a schema file or interactively.
|
|
11
|
+
Creates app.js, .env, commons/, route/, middleware/, and migrations/.
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
--from <path> Read adapter, framework, and options from a schema file
|
|
15
|
+
--framework <name> Express framework: express, ultimate-express
|
|
16
|
+
--database <name> Database adapter: mysql, mariadb, postgres, sqlite3, mongodb,
|
|
17
|
+
mssql, cockroachdb, oracle, redis, dynamodb
|
|
18
|
+
--db <name> Alias for --database
|
|
19
|
+
--session <type> Session store: memory, redis, database
|
|
20
|
+
--output <dir> Directory for backend source files (relative to cwd).
|
|
21
|
+
package.json and app.js stay in root; commons/, route/,
|
|
22
|
+
middleware/, and migrations/ go inside this folder.
|
|
23
|
+
--rateLimiting Enable rate limiting (default: yes)
|
|
24
|
+
--helmet Enable Helmet security headers (default: yes)
|
|
25
|
+
--logger Enable Winston + Loki logger for Grafana (default: yes)
|
|
26
|
+
--yes Accept all defaults without prompting
|
|
27
|
+
--json Output machine-readable JSON
|
|
28
|
+
--dry-run Preview planned files without writing
|
|
29
|
+
--no-install Skip npm install after scaffolding
|
|
30
|
+
--help Show this help message
|
|
31
|
+
|
|
32
|
+
Generated files:
|
|
33
|
+
app.js Express app entry point
|
|
34
|
+
.env / .env.example Environment configuration
|
|
35
|
+
.gitignore Git ignore rules
|
|
36
|
+
<output>/commons/db.js Database init, connect, and global.db
|
|
37
|
+
<output>/commons/session.js Session configuration
|
|
38
|
+
<output>/commons/migrate.js Migration runner (also runs as script)
|
|
39
|
+
<output>/commons/add_migration.js Migration creation helper (also runs as script)
|
|
40
|
+
<output>/commons/security.js Helmet, rate limiting, custom headers
|
|
41
|
+
<output>/middleware/logger.js Winston + Loki request logger
|
|
42
|
+
<output>/route/index.js Central route mounting
|
|
43
|
+
<output>/route/health.js GET /health endpoint
|
|
44
|
+
<output>/migrations/ Initial migration files
|
|
45
|
+
|
|
46
|
+
Examples:
|
|
47
|
+
db-model-router init --from dbmr.schema.json --yes --no-install
|
|
48
|
+
db-model-router init --framework express --database postgres --output backend --yes
|
|
49
|
+
db-model-router init --database mysql --session redis --helmet --rateLimiting
|
|
50
|
+
db-model-router init --dry-run`,
|
|
51
|
+
|
|
52
|
+
inspect: `Usage: db-model-router inspect [options]
|
|
53
|
+
|
|
54
|
+
Introspect a live database and produce a dbmr.schema.json file.
|
|
55
|
+
Connects to the database, reads table structures, and outputs a schema.
|
|
56
|
+
|
|
57
|
+
Options:
|
|
58
|
+
--type <adapter> Database adapter (required): mysql, postgres, sqlite3,
|
|
59
|
+
mssql, oracle, cockroachdb
|
|
60
|
+
--env <path> Path to .env file for connection parameters
|
|
61
|
+
--out <path> Output file path (default: dbmr.schema.json)
|
|
62
|
+
--tables <list> Comma-separated list of tables to include (omit for all)
|
|
63
|
+
--yes Accept all defaults without prompting
|
|
64
|
+
--json Output schema as JSON to stdout (no file write)
|
|
65
|
+
--dry-run Output schema to stdout without writing file
|
|
66
|
+
--help Show this help message
|
|
67
|
+
|
|
68
|
+
Examples:
|
|
69
|
+
db-model-router inspect --type postgres --env .env
|
|
70
|
+
db-model-router inspect --type sqlite3 --out schema.json --tables users,posts
|
|
71
|
+
db-model-router inspect --type mysql --json`,
|
|
72
|
+
|
|
73
|
+
generate: `Usage: db-model-router generate [options]
|
|
74
|
+
|
|
75
|
+
Generate models, routes, tests, OpenAPI spec, and LLM docs from a schema file.
|
|
76
|
+
When no artifact flags are provided, all artifact types are generated.
|
|
77
|
+
|
|
78
|
+
Options:
|
|
79
|
+
--from <path> Path to schema file (default: dbmr.schema.json)
|
|
80
|
+
--models Generate only model files
|
|
81
|
+
--routes Generate only route files (including child routes and index)
|
|
82
|
+
--openapi Generate only OpenAPI spec
|
|
83
|
+
--tests Generate only test files
|
|
84
|
+
--llm-docs Generate only LLM documentation (llms.txt + docs/llm.md)
|
|
85
|
+
--yes Accept all defaults without prompting
|
|
86
|
+
--json Output machine-readable JSON
|
|
87
|
+
--dry-run Report planned files without writing
|
|
88
|
+
--help Show this help message
|
|
89
|
+
|
|
90
|
+
Generated files:
|
|
91
|
+
models/<table>.js Model with CRUD operations
|
|
92
|
+
routes/<table>.js Express route handlers
|
|
93
|
+
routes/<child>_child_of_<parent>.js Child route (scoped by FK)
|
|
94
|
+
routes/index.js Route mounting index
|
|
95
|
+
test/<table>.test.js CRUD endpoint tests
|
|
96
|
+
openapi.json OpenAPI 3.0 spec
|
|
97
|
+
llms.txt LLM quick reference
|
|
98
|
+
docs/llm.md Full LLM reference
|
|
99
|
+
|
|
100
|
+
Examples:
|
|
101
|
+
db-model-router generate --from dbmr.schema.json
|
|
102
|
+
db-model-router generate --models --dry-run
|
|
103
|
+
db-model-router generate --routes --tests
|
|
104
|
+
db-model-router generate --from dbmr.schema.json --json`,
|
|
105
|
+
|
|
106
|
+
doctor: `Usage: db-model-router doctor [options]
|
|
107
|
+
|
|
108
|
+
Validate schema, check adapter driver dependencies, and verify generated
|
|
109
|
+
files are in sync with the schema.
|
|
110
|
+
|
|
111
|
+
Options:
|
|
112
|
+
--from <path> Path to schema file (default: dbmr.schema.json)
|
|
113
|
+
--yes Accept all defaults without prompting
|
|
114
|
+
--json Output machine-readable JSON
|
|
115
|
+
--help Show this help message
|
|
116
|
+
|
|
117
|
+
Checks performed:
|
|
118
|
+
1. Schema validation Syntax and structure of dbmr.schema.json
|
|
119
|
+
2. Dependency check Adapter driver present in package.json
|
|
120
|
+
3. Sync check Generated files match what the schema would produce
|
|
121
|
+
|
|
122
|
+
Examples:
|
|
123
|
+
db-model-router doctor --from dbmr.schema.json
|
|
124
|
+
db-model-router doctor --json`,
|
|
125
|
+
|
|
126
|
+
diff: `Usage: db-model-router diff [options]
|
|
127
|
+
|
|
128
|
+
Preview changes between the current generated files and what the schema
|
|
129
|
+
would produce. Read-only — does not modify any files on disk.
|
|
130
|
+
|
|
131
|
+
Options:
|
|
132
|
+
--from <path> Path to schema file (default: dbmr.schema.json)
|
|
133
|
+
--yes Accept all defaults without prompting
|
|
134
|
+
--json Output machine-readable JSON
|
|
135
|
+
--help Show this help message
|
|
136
|
+
|
|
137
|
+
Output shows:
|
|
138
|
+
+ Added New files that would be created
|
|
139
|
+
~ Modified Files with changes (includes line diffs)
|
|
140
|
+
- Deleted Extra files that would be removed
|
|
141
|
+
|
|
142
|
+
Examples:
|
|
143
|
+
db-model-router diff --from dbmr.schema.json
|
|
144
|
+
db-model-router diff --json`,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Help command handler.
|
|
149
|
+
*
|
|
150
|
+
* When called with a command name in args (e.g. `help init`), prints
|
|
151
|
+
* detailed help for that command. Otherwise prints the general overview.
|
|
152
|
+
*
|
|
153
|
+
* @param {object} args - Parsed key-value args
|
|
154
|
+
* @param {object} flags - Universal flags
|
|
155
|
+
* @param {import('../flags').OutputContext} ctx - Output context
|
|
156
|
+
* @param {object} options - Injected dependencies
|
|
157
|
+
* @param {Function} options.printHelp - General help printer from main.js
|
|
158
|
+
*/
|
|
159
|
+
async function help(args, flags, ctx, options) {
|
|
160
|
+
// The command to get help for is the first positional arg captured
|
|
161
|
+
// by parseFlags as a key-value. We also check args._command which
|
|
162
|
+
// main.js will inject.
|
|
163
|
+
const topic = args._command;
|
|
164
|
+
|
|
165
|
+
if (topic && COMMAND_HELP[topic]) {
|
|
166
|
+
ctx.log(COMMAND_HELP[topic]);
|
|
167
|
+
} else if (topic) {
|
|
168
|
+
ctx.log(`Unknown command: ${topic}\n`);
|
|
169
|
+
ctx.log(`Available commands: ${Object.keys(COMMAND_HELP).join(", ")}\n`);
|
|
170
|
+
ctx.log(`Run "db-model-router help <command>" for detailed help.`);
|
|
171
|
+
} else {
|
|
172
|
+
// No topic — print general help
|
|
173
|
+
if (options && options.printHelp) {
|
|
174
|
+
options.printHelp();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
module.exports = help;
|
|
180
|
+
module.exports.COMMAND_HELP = COMMAND_HELP;
|
package/src/cli/commands/init.js
CHANGED
|
@@ -19,9 +19,10 @@ const DEFAULT_ANSWERS = {
|
|
|
19
19
|
framework: "express",
|
|
20
20
|
database: "postgres",
|
|
21
21
|
session: "memory",
|
|
22
|
-
rateLimiting:
|
|
23
|
-
helmet:
|
|
24
|
-
logger:
|
|
22
|
+
rateLimiting: true,
|
|
23
|
+
helmet: true,
|
|
24
|
+
logger: true,
|
|
25
|
+
loki: false,
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
/**
|
|
@@ -52,6 +53,7 @@ async function init(args, flags, ctx) {
|
|
|
52
53
|
rateLimiting: !!(schema.options && schema.options.rateLimiting),
|
|
53
54
|
helmet: !!(schema.options && schema.options.helmet),
|
|
54
55
|
logger: !!(schema.options && schema.options.logger),
|
|
56
|
+
loki: !!(schema.options && schema.options.loki),
|
|
55
57
|
};
|
|
56
58
|
} else if (flags.yes) {
|
|
57
59
|
// --yes with no schema: use defaults, but allow CLI overrides
|
|
@@ -66,9 +68,13 @@ async function init(args, flags, ctx) {
|
|
|
66
68
|
answers = await promptUser(prefilled);
|
|
67
69
|
}
|
|
68
70
|
|
|
71
|
+
// Resolve --output directory (relative to cwd)
|
|
72
|
+
// CLI --output flag takes precedence, then interactive prompt answer
|
|
73
|
+
const outputDir = args.output || answers.output || "";
|
|
74
|
+
|
|
69
75
|
// --dry-run: report planned files without writing
|
|
70
76
|
if (flags.dryRun) {
|
|
71
|
-
const planned = planFiles(answers);
|
|
77
|
+
const planned = planFiles(answers, outputDir);
|
|
72
78
|
if (flags.json) {
|
|
73
79
|
ctx.result({
|
|
74
80
|
files: planned,
|
|
@@ -89,10 +95,10 @@ async function init(args, flags, ctx) {
|
|
|
89
95
|
ensurePackageJson();
|
|
90
96
|
|
|
91
97
|
// Generate project files
|
|
92
|
-
const generated = generateFiles(answers);
|
|
98
|
+
const generated = generateFiles(answers, outputDir);
|
|
93
99
|
|
|
94
100
|
// Update package.json with deps and scripts
|
|
95
|
-
updatePackageJson(answers);
|
|
101
|
+
updatePackageJson(answers, outputDir);
|
|
96
102
|
|
|
97
103
|
// npm install (unless --no-install)
|
|
98
104
|
const installed = !flags.noInstall;
|
|
@@ -103,7 +109,10 @@ async function init(args, flags, ctx) {
|
|
|
103
109
|
// Output
|
|
104
110
|
const allFiles = [
|
|
105
111
|
...generated.files,
|
|
106
|
-
...generated.migrationFiles.map((m) =>
|
|
112
|
+
...generated.migrationFiles.map((m) => {
|
|
113
|
+
const base = outputDir || ".";
|
|
114
|
+
return base === "." ? `migrations/${m}` : `${base}/migrations/${m}`;
|
|
115
|
+
}),
|
|
107
116
|
];
|
|
108
117
|
|
|
109
118
|
if (flags.json) {
|
|
@@ -127,24 +136,43 @@ async function init(args, flags, ctx) {
|
|
|
127
136
|
* This mirrors the file list from generateFiles() without writing anything.
|
|
128
137
|
*
|
|
129
138
|
* @param {object} answers
|
|
139
|
+
* @param {string} [outputDir] - relative output directory for source files
|
|
130
140
|
* @returns {string[]}
|
|
131
141
|
*/
|
|
132
|
-
function planFiles(answers) {
|
|
142
|
+
function planFiles(answers, outputDir) {
|
|
133
143
|
const { isSql } = require("../init/generators");
|
|
144
|
+
const srcBase = outputDir || ".";
|
|
145
|
+
const prefix = srcBase === "." ? "" : srcBase + "/";
|
|
146
|
+
|
|
134
147
|
const files = [
|
|
135
148
|
"app.js",
|
|
136
149
|
".env",
|
|
137
150
|
".env.example",
|
|
138
|
-
"middleware/logger.js",
|
|
139
|
-
"migrate.js",
|
|
140
|
-
"add_migration.js",
|
|
141
151
|
".gitignore",
|
|
142
|
-
"
|
|
143
|
-
|
|
152
|
+
"Dockerfile",
|
|
153
|
+
".dockerignore",
|
|
144
154
|
];
|
|
145
155
|
|
|
156
|
+
// docker-compose.yml for databases that need Docker
|
|
157
|
+
if (answers.database !== "sqlite3") {
|
|
158
|
+
files.push("docker-compose.yml");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
files.push(
|
|
162
|
+
`${prefix}middleware/logger.js`,
|
|
163
|
+
`${prefix}commons/session.js`,
|
|
164
|
+
`${prefix}commons/migrate.js`,
|
|
165
|
+
`${prefix}commons/add_migration.js`,
|
|
166
|
+
`${prefix}commons/security.js`,
|
|
167
|
+
`${prefix}commons/db.js`,
|
|
168
|
+
`${prefix}route/health.js`,
|
|
169
|
+
`${prefix}route/index.js`,
|
|
170
|
+
`${prefix}migrations/<timestamp>_create_migrations_table` +
|
|
171
|
+
(isSql(answers.database) ? ".sql" : ".js"),
|
|
172
|
+
);
|
|
173
|
+
|
|
146
174
|
if (answers.session === "database" && isSql(answers.database)) {
|
|
147
|
-
files.push(
|
|
175
|
+
files.push(`${prefix}migrations/<timestamp>_create_sessions_table.sql`);
|
|
148
176
|
}
|
|
149
177
|
|
|
150
178
|
return files;
|
|
@@ -40,15 +40,32 @@ function modelMetaToSchema(adapter, framework, models) {
|
|
|
40
40
|
for (const m of models) {
|
|
41
41
|
const columns = {};
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
const pk = m.primary_key || "id";
|
|
44
|
+
const opt = m.option || {};
|
|
45
|
+
|
|
46
|
+
// Add PK column as auto_increment
|
|
47
|
+
columns[pk] = "auto_increment";
|
|
48
|
+
|
|
49
|
+
// Add timestamp columns as datetime
|
|
50
|
+
if (opt.created_at) {
|
|
51
|
+
columns[opt.created_at] = "datetime";
|
|
52
|
+
}
|
|
53
|
+
if (opt.modified_at) {
|
|
54
|
+
columns[opt.modified_at] = "datetime";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Add softDelete column
|
|
58
|
+
if (opt.safeDelete) {
|
|
59
|
+
columns[opt.safeDelete] = "boolean";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Add remaining columns from structure
|
|
44
63
|
for (const [col, rule] of Object.entries(m.structure)) {
|
|
45
64
|
columns[col] = rule;
|
|
46
65
|
}
|
|
47
66
|
|
|
48
|
-
const pk = m.primary_key || "id";
|
|
49
67
|
const unique = m.unique && m.unique.length > 0 ? [...m.unique] : [pk];
|
|
50
68
|
|
|
51
|
-
const opt = m.option || {};
|
|
52
69
|
const softDelete = opt.safeDelete || null;
|
|
53
70
|
const timestamps = {
|
|
54
71
|
created_at: opt.created_at || null,
|
|
@@ -475,7 +475,7 @@ function mysqlTypeToValidator(t) {
|
|
|
475
475
|
if (/json/.test(t)) return "object";
|
|
476
476
|
if (/text|char|varchar|enum|set/.test(t)) return "string";
|
|
477
477
|
if (/blob|binary/.test(t)) return "string";
|
|
478
|
-
if (/date|time|year/.test(t)) return "
|
|
478
|
+
if (/date|time|year/.test(t)) return "datetime";
|
|
479
479
|
if (/bool/.test(t)) return "integer";
|
|
480
480
|
return "string";
|
|
481
481
|
}
|
|
@@ -487,7 +487,7 @@ function pgTypeToValidator(t) {
|
|
|
487
487
|
if (/json/.test(t)) return "object";
|
|
488
488
|
if (/bool/.test(t)) return "integer";
|
|
489
489
|
if (/char|text|varchar|uuid/.test(t)) return "string";
|
|
490
|
-
if (/date|time|interval/.test(t)) return "
|
|
490
|
+
if (/date|time|interval/.test(t)) return "datetime";
|
|
491
491
|
return "string";
|
|
492
492
|
}
|
|
493
493
|
|
|
@@ -496,6 +496,7 @@ function sqliteTypeToValidator(t) {
|
|
|
496
496
|
if (/int/.test(t)) return "integer";
|
|
497
497
|
if (/real|float|double|numeric|decimal/.test(t)) return "numeric";
|
|
498
498
|
if (/json/.test(t)) return "object";
|
|
499
|
+
if (/date|time/.test(t)) return "datetime";
|
|
499
500
|
if (/blob/.test(t)) return "string";
|
|
500
501
|
return "string";
|
|
501
502
|
}
|
|
@@ -506,7 +507,7 @@ function mssqlTypeToValidator(t) {
|
|
|
506
507
|
if (/decimal|numeric|float|real|money/.test(t)) return "numeric";
|
|
507
508
|
if (/bit/.test(t)) return "integer";
|
|
508
509
|
if (/char|text|varchar|nchar|nvarchar|ntext/.test(t)) return "string";
|
|
509
|
-
if (/date|time|datetime/.test(t)) return "
|
|
510
|
+
if (/date|time|datetime/.test(t)) return "datetime";
|
|
510
511
|
if (/uniqueidentifier/.test(t)) return "string";
|
|
511
512
|
return "string";
|
|
512
513
|
}
|
|
@@ -515,7 +516,7 @@ function oracleTypeToValidator(t) {
|
|
|
515
516
|
if (/NUMBER|INTEGER|FLOAT|BINARY_FLOAT|BINARY_DOUBLE/.test(t))
|
|
516
517
|
return "numeric";
|
|
517
518
|
if (/CLOB|BLOB|RAW|LONG/.test(t)) return "string";
|
|
518
|
-
if (/DATE|TIMESTAMP/.test(t)) return "
|
|
519
|
+
if (/DATE|TIMESTAMP/.test(t)) return "datetime";
|
|
519
520
|
if (/CHAR|VARCHAR|NCHAR|NVARCHAR/.test(t)) return "string";
|
|
520
521
|
return "string";
|
|
521
522
|
}
|
|
@@ -25,10 +25,12 @@ function safeVarName(name) {
|
|
|
25
25
|
*/
|
|
26
26
|
function generateRouteFile(tableName, modelsRelPath) {
|
|
27
27
|
const varName = safeVarName(tableName);
|
|
28
|
-
return `
|
|
29
|
-
|
|
28
|
+
return `import dbModelRouter from "db-model-router";
|
|
29
|
+
import ${varName} from "${modelsRelPath}/${tableName}.js";
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
const { route } = dbModelRouter;
|
|
32
|
+
|
|
33
|
+
export default route(${varName});
|
|
32
34
|
`;
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -43,11 +45,13 @@ function generateChildRouteFile(
|
|
|
43
45
|
modelsRelPath,
|
|
44
46
|
) {
|
|
45
47
|
const varName = safeVarName(childTable);
|
|
46
|
-
return `
|
|
47
|
-
|
|
48
|
+
return `import dbModelRouter from "db-model-router";
|
|
49
|
+
import ${varName} from "${modelsRelPath}/${childTable}.js";
|
|
50
|
+
|
|
51
|
+
const { route } = dbModelRouter;
|
|
48
52
|
|
|
49
53
|
// Child route: scoped by parent ${parentTable} via ${fkColumn}
|
|
50
|
-
|
|
54
|
+
export default route(${varName}, { ${fkColumn}: "params.${fkColumn}" });
|
|
51
55
|
`;
|
|
52
56
|
}
|
|
53
57
|
|
|
@@ -56,7 +60,7 @@ module.exports = route(${varName}, { ${fkColumn}: "params.${fkColumn}" });
|
|
|
56
60
|
* Supports parent-child nesting: parent/:pk/child
|
|
57
61
|
*/
|
|
58
62
|
function generateRoutesIndexFile(tableNames, relationships = []) {
|
|
59
|
-
let imports = `
|
|
63
|
+
let imports = `import express from "express";\n\nconst router = express.Router();\n\n`;
|
|
60
64
|
|
|
61
65
|
// Collect child tables that are nested under parents
|
|
62
66
|
const nestedChildren = new Set();
|
|
@@ -66,12 +70,12 @@ function generateRoutesIndexFile(tableNames, relationships = []) {
|
|
|
66
70
|
|
|
67
71
|
for (const table of tableNames) {
|
|
68
72
|
const varName = safeVarName(table);
|
|
69
|
-
imports += `
|
|
73
|
+
imports += `import ${varName}Route from "./${table}.js";\n`;
|
|
70
74
|
}
|
|
71
75
|
// Import child routes with _child suffix for nested ones
|
|
72
76
|
for (const rel of relationships) {
|
|
73
77
|
const varName = safeVarName(rel.child);
|
|
74
|
-
imports += `
|
|
78
|
+
imports += `import ${varName}ChildRoute from "./${rel.child}_child_of_${rel.parent}.js";\n`;
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
imports += "\n";
|
|
@@ -85,9 +89,7 @@ function generateRoutesIndexFile(tableNames, relationships = []) {
|
|
|
85
89
|
|
|
86
90
|
// Mount nested child routes under parent
|
|
87
91
|
for (const rel of relationships) {
|
|
88
|
-
const parentVar = safeVarName(rel.parent);
|
|
89
92
|
const childVar = safeVarName(rel.child);
|
|
90
|
-
// Find parent PK from model file name convention — use fkColumn without _id suffix as parent pk param
|
|
91
93
|
imports += `router.use("/${rel.parent}/:${rel.fkColumn}/${rel.child}", ${childVar}ChildRoute);\n`;
|
|
92
94
|
}
|
|
93
95
|
|
|
@@ -97,7 +99,7 @@ function generateRoutesIndexFile(tableNames, relationships = []) {
|
|
|
97
99
|
imports += `router.use("/${rel.child}", ${varName}Route);\n`;
|
|
98
100
|
}
|
|
99
101
|
|
|
100
|
-
imports += "\
|
|
102
|
+
imports += "\nexport default router;\n";
|
|
101
103
|
return imports;
|
|
102
104
|
}
|
|
103
105
|
|
|
@@ -114,13 +116,15 @@ function generateSimpleRoutesIndexFile(tableNames) {
|
|
|
114
116
|
*/
|
|
115
117
|
function generateTestFile(tableName, pk) {
|
|
116
118
|
const varName = safeVarName(tableName);
|
|
117
|
-
return `
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
return `import assert from "assert";
|
|
120
|
+
import express from "express";
|
|
121
|
+
import request from "supertest";
|
|
122
|
+
import dbModelRouter from "db-model-router";
|
|
123
|
+
|
|
124
|
+
const { route } = dbModelRouter;
|
|
121
125
|
|
|
122
126
|
// Adjust the path to your model file as needed
|
|
123
|
-
|
|
127
|
+
import ${varName} from "../models/${tableName}.js";
|
|
124
128
|
|
|
125
129
|
function createApp() {
|
|
126
130
|
const app = express();
|
|
@@ -220,12 +224,14 @@ describe("${tableName} routes", function () {
|
|
|
220
224
|
*/
|
|
221
225
|
function generateChildTestFile(childTable, parentTable, fkColumn, pk) {
|
|
222
226
|
const childVar = safeVarName(childTable);
|
|
223
|
-
return `
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
+
return `import assert from "assert";
|
|
228
|
+
import express from "express";
|
|
229
|
+
import request from "supertest";
|
|
230
|
+
import dbModelRouter from "db-model-router";
|
|
231
|
+
|
|
232
|
+
const { route } = dbModelRouter;
|
|
227
233
|
|
|
228
|
-
|
|
234
|
+
import ${childVar} from "../models/${childTable}.js";
|
|
229
235
|
|
|
230
236
|
function createApp() {
|
|
231
237
|
const app = express();
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
const DRIVER_MAP = {
|
|
7
7
|
mysql: ["mysql2"],
|
|
8
|
+
mariadb: ["mysql2"],
|
|
8
9
|
postgres: ["pg"],
|
|
9
10
|
sqlite3: ["better-sqlite3"],
|
|
10
11
|
mongodb: ["mongodb"],
|
|
@@ -53,7 +54,10 @@ function collectDependencies(answers) {
|
|
|
53
54
|
dependencies["helmet"] = "latest";
|
|
54
55
|
}
|
|
55
56
|
if (answers.logger) {
|
|
56
|
-
dependencies["
|
|
57
|
+
dependencies["winston"] = "latest";
|
|
58
|
+
if (answers.loki) {
|
|
59
|
+
dependencies["winston-loki"] = "latest";
|
|
60
|
+
}
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
// Dev dependencies
|
|
@@ -63,16 +67,21 @@ function collectDependencies(answers) {
|
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
/**
|
|
66
|
-
* Returns the
|
|
70
|
+
* Returns the package.json scripts.
|
|
71
|
+
* @param {string} [outputDir] - relative output directory for source files
|
|
67
72
|
* @returns {Record<string, string>}
|
|
68
73
|
*/
|
|
69
|
-
function getScripts() {
|
|
74
|
+
function getScripts(outputDir) {
|
|
75
|
+
const prefix = outputDir ? `${outputDir}/` : "";
|
|
70
76
|
return {
|
|
71
77
|
start: "node app.js",
|
|
72
78
|
dev: "nodemon app.js",
|
|
73
79
|
test: 'echo "Error: no test specified" && exit 1',
|
|
74
|
-
migrate:
|
|
75
|
-
add_migration:
|
|
80
|
+
migrate: `node ${prefix}commons/migrate.js`,
|
|
81
|
+
add_migration: `node ${prefix}commons/add_migration.js`,
|
|
82
|
+
"docker:build": "docker build -t app .",
|
|
83
|
+
"docker:up": "docker compose up -d",
|
|
84
|
+
"docker:down": "docker compose down",
|
|
76
85
|
};
|
|
77
86
|
}
|
|
78
87
|
|