prisma-flare 1.0.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/dist/cli/db-create.cjs +240 -0
- package/dist/cli/db-create.d.cts +1 -0
- package/dist/cli/db-create.d.ts +1 -0
- package/dist/cli/db-create.js +217 -0
- package/dist/cli/db-drop.cjs +263 -0
- package/dist/cli/db-drop.d.cts +1 -0
- package/dist/cli/db-drop.d.ts +1 -0
- package/dist/cli/db-drop.js +240 -0
- package/dist/cli/db-migrate.cjs +318 -0
- package/dist/cli/db-migrate.d.cts +1 -0
- package/dist/cli/db-migrate.d.ts +1 -0
- package/dist/cli/db-migrate.js +295 -0
- package/dist/cli/db-reset.cjs +110 -0
- package/dist/cli/db-reset.d.cts +1 -0
- package/dist/cli/db-reset.d.ts +1 -0
- package/dist/cli/db-reset.js +87 -0
- package/dist/cli/db-seed.cjs +87 -0
- package/dist/cli/db-seed.d.cts +1 -0
- package/dist/cli/db-seed.d.ts +1 -0
- package/dist/cli/db-seed.js +64 -0
- package/dist/cli/index.cjs +352 -0
- package/dist/cli/index.d.cts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +328 -0
- package/dist/core/flareBuilder.cjs +681 -0
- package/dist/core/flareBuilder.d.cts +402 -0
- package/dist/core/flareBuilder.d.ts +402 -0
- package/dist/core/flareBuilder.js +658 -0
- package/dist/core/hooks.cjs +243 -0
- package/dist/core/hooks.d.cts +13 -0
- package/dist/core/hooks.d.ts +13 -0
- package/dist/core/hooks.js +209 -0
- package/dist/generated.cjs +31 -0
- package/dist/generated.d.cts +4 -0
- package/dist/generated.d.ts +4 -0
- package/dist/generated.js +6 -0
- package/dist/index.cjs +1315 -0
- package/dist/index.d.cts +237 -0
- package/dist/index.d.ts +237 -0
- package/dist/index.js +1261 -0
- package/dist/prisma.types-nGNe1CG8.d.cts +201 -0
- package/dist/prisma.types-nGNe1CG8.d.ts +201 -0
- package/license.md +21 -0
- package/package.json +115 -0
- package/readme.md +957 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/cli/db-migrate.ts
|
|
27
|
+
var import_child_process = require("child_process");
|
|
28
|
+
var dotenv = __toESM(require("dotenv"), 1);
|
|
29
|
+
|
|
30
|
+
// src/cli/generate-queries.ts
|
|
31
|
+
var fs2 = __toESM(require("fs"), 1);
|
|
32
|
+
var path2 = __toESM(require("path"), 1);
|
|
33
|
+
var import_pluralize = __toESM(require("pluralize"), 1);
|
|
34
|
+
|
|
35
|
+
// src/cli/config.ts
|
|
36
|
+
var fs = __toESM(require("fs"), 1);
|
|
37
|
+
var path = __toESM(require("path"), 1);
|
|
38
|
+
function findProjectRoot(currentDir) {
|
|
39
|
+
if (fs.existsSync(path.join(currentDir, "package.json"))) {
|
|
40
|
+
return currentDir;
|
|
41
|
+
}
|
|
42
|
+
const parentDir = path.dirname(currentDir);
|
|
43
|
+
if (parentDir === currentDir) {
|
|
44
|
+
throw new Error("Could not find package.json");
|
|
45
|
+
}
|
|
46
|
+
return findProjectRoot(parentDir);
|
|
47
|
+
}
|
|
48
|
+
function loadConfig(rootDir) {
|
|
49
|
+
const projectRoot = rootDir || findProjectRoot(process.cwd());
|
|
50
|
+
const configPath = path.join(projectRoot, "prisma-flare.config.json");
|
|
51
|
+
let config3 = {
|
|
52
|
+
modelsPath: "prisma/models",
|
|
53
|
+
dbPath: "prisma/db",
|
|
54
|
+
callbacksPath: "prisma/callbacks"
|
|
55
|
+
};
|
|
56
|
+
if (fs.existsSync(configPath)) {
|
|
57
|
+
try {
|
|
58
|
+
const configFile = fs.readFileSync(configPath, "utf-8");
|
|
59
|
+
const userConfig = JSON.parse(configFile);
|
|
60
|
+
config3 = { ...config3, ...userConfig };
|
|
61
|
+
} catch {
|
|
62
|
+
console.warn("\u26A0\uFE0F Could not read prisma-flare.config.json, using defaults.");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
...config3
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/cli/generate-queries.ts
|
|
71
|
+
function toCamelCase(str) {
|
|
72
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
73
|
+
}
|
|
74
|
+
function parseRelations(schemaContent, models) {
|
|
75
|
+
const relations = /* @__PURE__ */ new Map();
|
|
76
|
+
models.forEach((model) => relations.set(model, []));
|
|
77
|
+
const modelBlockRegex = /model\s+(\w+)\s+{([^}]+)}/g;
|
|
78
|
+
let modelMatch;
|
|
79
|
+
while ((modelMatch = modelBlockRegex.exec(schemaContent)) !== null) {
|
|
80
|
+
const modelName = modelMatch[1];
|
|
81
|
+
const modelBody = modelMatch[2];
|
|
82
|
+
const lines = modelBody.split("\n");
|
|
83
|
+
for (const line of lines) {
|
|
84
|
+
const trimmed = line.trim();
|
|
85
|
+
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("@")) continue;
|
|
86
|
+
const fieldMatch = trimmed.match(/^(\w+)\s+(\w+)(\[\])?\s*/);
|
|
87
|
+
if (fieldMatch) {
|
|
88
|
+
const fieldName = fieldMatch[1];
|
|
89
|
+
const fieldType = fieldMatch[2];
|
|
90
|
+
const isArray = !!fieldMatch[3];
|
|
91
|
+
if (models.includes(fieldType)) {
|
|
92
|
+
const modelRelations = relations.get(modelName) || [];
|
|
93
|
+
modelRelations.push({ fieldName, targetModel: fieldType });
|
|
94
|
+
relations.set(modelName, modelRelations);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return relations;
|
|
100
|
+
}
|
|
101
|
+
function generateQueries() {
|
|
102
|
+
const rootDir = findProjectRoot(process.cwd());
|
|
103
|
+
const config3 = loadConfig(rootDir);
|
|
104
|
+
const schemaPath = path2.join(rootDir, "prisma", "schema.prisma");
|
|
105
|
+
if (!fs2.existsSync(schemaPath)) {
|
|
106
|
+
console.error(`\u274C Schema not found at ${schemaPath}`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const schemaContent = fs2.readFileSync(schemaPath, "utf-8");
|
|
110
|
+
const modelRegex = /model\s+(\w+)\s+{/g;
|
|
111
|
+
const models = [];
|
|
112
|
+
let match;
|
|
113
|
+
while ((match = modelRegex.exec(schemaContent)) !== null) {
|
|
114
|
+
models.push(match[1]);
|
|
115
|
+
}
|
|
116
|
+
const queriesDir = path2.join(rootDir, config3.modelsPath);
|
|
117
|
+
if (!fs2.existsSync(queriesDir)) {
|
|
118
|
+
fs2.mkdirSync(queriesDir, { recursive: true });
|
|
119
|
+
}
|
|
120
|
+
const absDbPath = path2.join(rootDir, config3.dbPath);
|
|
121
|
+
let relativePathToDb = path2.relative(queriesDir, absDbPath);
|
|
122
|
+
if (!relativePathToDb.startsWith(".")) relativePathToDb = "./" + relativePathToDb;
|
|
123
|
+
relativePathToDb = relativePathToDb.replace(/\\/g, "/");
|
|
124
|
+
models.forEach((model) => {
|
|
125
|
+
const queryFileName = `${model}.ts`;
|
|
126
|
+
const queryFilePath = path2.join(queriesDir, queryFileName);
|
|
127
|
+
const modelCamel = toCamelCase(model);
|
|
128
|
+
if (fs2.existsSync(queryFilePath)) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
console.log(`Generating ${queryFileName}...`);
|
|
132
|
+
let queryBuilderImport = "import { FlareBuilder } from 'prisma-flare';";
|
|
133
|
+
const localQueryBuilderPath = path2.join(rootDir, "src/core/flareBuilder.ts");
|
|
134
|
+
if (fs2.existsSync(localQueryBuilderPath)) {
|
|
135
|
+
const absSrcPath = path2.join(rootDir, "src");
|
|
136
|
+
let relativePathToSrc = path2.relative(queriesDir, absSrcPath);
|
|
137
|
+
if (!relativePathToSrc.startsWith(".")) relativePathToSrc = "./" + relativePathToSrc;
|
|
138
|
+
relativePathToSrc = relativePathToSrc.replace(/\\/g, "/");
|
|
139
|
+
queryBuilderImport = `import { FlareBuilder } from '${relativePathToSrc}';`;
|
|
140
|
+
}
|
|
141
|
+
const content = `import { db } from '${relativePathToDb}';
|
|
142
|
+
${queryBuilderImport}
|
|
143
|
+
|
|
144
|
+
export default class ${model} extends FlareBuilder<'${modelCamel}'> {
|
|
145
|
+
constructor() {
|
|
146
|
+
super(db.${modelCamel});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
`;
|
|
150
|
+
fs2.writeFileSync(queryFilePath, content);
|
|
151
|
+
});
|
|
152
|
+
let pfDistDir;
|
|
153
|
+
const pfPackageDir = path2.join(rootDir, "node_modules", "prisma-flare");
|
|
154
|
+
let realPfPackageDir = pfPackageDir;
|
|
155
|
+
if (fs2.existsSync(pfPackageDir)) {
|
|
156
|
+
realPfPackageDir = fs2.realpathSync(pfPackageDir);
|
|
157
|
+
}
|
|
158
|
+
pfDistDir = path2.join(realPfPackageDir, "dist");
|
|
159
|
+
let dbPathWithExt = absDbPath;
|
|
160
|
+
if (!absDbPath.endsWith(".ts") && !absDbPath.endsWith(".js")) {
|
|
161
|
+
if (fs2.existsSync(absDbPath + ".ts")) {
|
|
162
|
+
dbPathWithExt = absDbPath + ".ts";
|
|
163
|
+
} else if (fs2.existsSync(absDbPath + ".js")) {
|
|
164
|
+
dbPathWithExt = absDbPath + ".js";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
let relativePathToDbForDist = path2.relative(pfDistDir, dbPathWithExt);
|
|
168
|
+
if (!relativePathToDbForDist.startsWith(".")) relativePathToDbForDist = "./" + relativePathToDbForDist;
|
|
169
|
+
relativePathToDbForDist = relativePathToDbForDist.replace(/\\/g, "/");
|
|
170
|
+
const relativePathToDbForDts = relativePathToDbForDist;
|
|
171
|
+
const absModelsPath = path2.join(queriesDir);
|
|
172
|
+
let relativePathToModels = path2.relative(pfDistDir, absModelsPath);
|
|
173
|
+
if (!relativePathToModels.startsWith(".")) relativePathToModels = "./" + relativePathToModels;
|
|
174
|
+
relativePathToModels = relativePathToModels.replace(/\\/g, "/");
|
|
175
|
+
const relations = parseRelations(schemaContent, models);
|
|
176
|
+
const getters = models.map((model) => {
|
|
177
|
+
const modelCamel = toCamelCase(model);
|
|
178
|
+
const customPlural = config3.plurals?.[model];
|
|
179
|
+
const modelPlural = customPlural || (0, import_pluralize.default)(modelCamel);
|
|
180
|
+
return ` static get ${modelPlural}() {
|
|
181
|
+
return new ${model}();
|
|
182
|
+
}`;
|
|
183
|
+
}).join("\n\n");
|
|
184
|
+
const registrationLines = [];
|
|
185
|
+
models.forEach((model) => {
|
|
186
|
+
const modelCamel = toCamelCase(model);
|
|
187
|
+
const customPlural = config3.plurals?.[model];
|
|
188
|
+
const modelPlural = customPlural || (0, import_pluralize.default)(modelCamel);
|
|
189
|
+
registrationLines.push(`modelRegistry.register('${modelCamel}', ${model});`);
|
|
190
|
+
registrationLines.push(`modelRegistry.register('${modelPlural}', ${model});`);
|
|
191
|
+
});
|
|
192
|
+
relations.forEach((rels, modelName) => {
|
|
193
|
+
rels.forEach((rel) => {
|
|
194
|
+
registrationLines.push(`modelRegistry.register('${rel.fieldName}', ${rel.targetModel});`);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
const modelRegistrations = registrationLines.join("\n");
|
|
198
|
+
const generatedJsPath = path2.join(pfDistDir, "generated.js");
|
|
199
|
+
const imports = models.map((model) => {
|
|
200
|
+
return `import ${model} from '${relativePathToModels}/${model}.ts';`;
|
|
201
|
+
}).join("\n");
|
|
202
|
+
const generatedContent = `
|
|
203
|
+
import { db } from '${relativePathToDbForDist}';
|
|
204
|
+
import { modelRegistry } from './index.js';
|
|
205
|
+
${imports}
|
|
206
|
+
|
|
207
|
+
// Register all models so include() can use custom model classes
|
|
208
|
+
${modelRegistrations}
|
|
209
|
+
|
|
210
|
+
export class DB {
|
|
211
|
+
static get instance() {
|
|
212
|
+
return db;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
${getters}
|
|
216
|
+
}
|
|
217
|
+
`;
|
|
218
|
+
fs2.writeFileSync(generatedJsPath, generatedContent);
|
|
219
|
+
const generatedCjsPath = path2.join(pfDistDir, "generated.cjs");
|
|
220
|
+
const importsCjs = models.map((model) => {
|
|
221
|
+
return `const ${model} = require('${relativePathToModels}/${model}').default;`;
|
|
222
|
+
}).join("\n");
|
|
223
|
+
const generatedCjsContent = `
|
|
224
|
+
const { db } = require('${relativePathToDbForDist}');
|
|
225
|
+
const { modelRegistry } = require('./index.cjs');
|
|
226
|
+
${importsCjs}
|
|
227
|
+
|
|
228
|
+
// Register all models so include() can use custom model classes
|
|
229
|
+
${modelRegistrations}
|
|
230
|
+
|
|
231
|
+
class DB {
|
|
232
|
+
static get instance() {
|
|
233
|
+
return db;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
${getters}
|
|
237
|
+
}
|
|
238
|
+
exports.DB = DB;
|
|
239
|
+
`;
|
|
240
|
+
fs2.writeFileSync(generatedCjsPath, generatedCjsContent);
|
|
241
|
+
const generatedDtsPath = path2.join(pfDistDir, "generated.d.ts");
|
|
242
|
+
const importsDts = models.map((model) => {
|
|
243
|
+
return `import ${model} from '${relativePathToModels}/${model}.ts';`;
|
|
244
|
+
}).join("\n");
|
|
245
|
+
const gettersTypes = models.map((model) => {
|
|
246
|
+
const modelCamel = toCamelCase(model);
|
|
247
|
+
const customPlural = config3.plurals?.[model];
|
|
248
|
+
const modelPlural = customPlural || (0, import_pluralize.default)(modelCamel);
|
|
249
|
+
return ` static get ${modelPlural}(): ${model};`;
|
|
250
|
+
}).join("\n");
|
|
251
|
+
const relationMapEntries = [];
|
|
252
|
+
models.forEach((model) => {
|
|
253
|
+
const modelCamel = toCamelCase(model);
|
|
254
|
+
const customPlural = config3.plurals?.[model];
|
|
255
|
+
const modelPlural = customPlural || (0, import_pluralize.default)(modelCamel);
|
|
256
|
+
relationMapEntries.push(` ${modelCamel}: ${model};`);
|
|
257
|
+
relationMapEntries.push(` ${modelPlural}: ${model};`);
|
|
258
|
+
});
|
|
259
|
+
relations.forEach((rels, modelName) => {
|
|
260
|
+
rels.forEach((rel) => {
|
|
261
|
+
const entry = ` ${rel.fieldName}: ${rel.targetModel};`;
|
|
262
|
+
if (!relationMapEntries.includes(entry)) {
|
|
263
|
+
relationMapEntries.push(entry);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
const generatedDtsContent = `
|
|
268
|
+
import { db } from '${relativePathToDbForDts}';
|
|
269
|
+
${importsDts}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Module augmentation to provide type-safe includes.
|
|
273
|
+
* This maps relation field names to their custom model classes.
|
|
274
|
+
*/
|
|
275
|
+
declare module 'prisma-flare' {
|
|
276
|
+
interface RelationModelMap {
|
|
277
|
+
${relationMapEntries.join("\n")}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export declare class DB {
|
|
282
|
+
static get instance(): typeof db;
|
|
283
|
+
|
|
284
|
+
${gettersTypes}
|
|
285
|
+
}
|
|
286
|
+
`;
|
|
287
|
+
fs2.writeFileSync(generatedDtsPath, generatedDtsContent);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// src/cli/db-migrate.ts
|
|
291
|
+
var config2 = loadConfig();
|
|
292
|
+
dotenv.config({ path: config2.envPath });
|
|
293
|
+
function runMigrations() {
|
|
294
|
+
const databaseUrl = process.env.DATABASE_URL;
|
|
295
|
+
if (!databaseUrl) {
|
|
296
|
+
console.error("\u274C DATABASE_URL environment variable is not set");
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
try {
|
|
300
|
+
console.log("\u{1F504} Running Prisma migrations...");
|
|
301
|
+
const args = process.argv.slice(2).join(" ");
|
|
302
|
+
const command = `npx prisma migrate dev ${args} && npx prisma generate`;
|
|
303
|
+
console.log(`Running: ${command}`);
|
|
304
|
+
(0, import_child_process.execSync)(command, {
|
|
305
|
+
stdio: "inherit",
|
|
306
|
+
env: process.env
|
|
307
|
+
});
|
|
308
|
+
console.log("\u2713 Migrations completed successfully");
|
|
309
|
+
console.log("\u{1F504} Generating Query classes...");
|
|
310
|
+
generateQueries();
|
|
311
|
+
console.log("\u2713 Query classes generated successfully");
|
|
312
|
+
process.exit(0);
|
|
313
|
+
} catch (error) {
|
|
314
|
+
console.error("\u274C Error running migrations:", error);
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
runMigrations();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/db-migrate.ts
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
import * as dotenv from "dotenv";
|
|
6
|
+
|
|
7
|
+
// src/cli/generate-queries.ts
|
|
8
|
+
import * as fs2 from "fs";
|
|
9
|
+
import * as path2 from "path";
|
|
10
|
+
import pluralize from "pluralize";
|
|
11
|
+
|
|
12
|
+
// src/cli/config.ts
|
|
13
|
+
import * as fs from "fs";
|
|
14
|
+
import * as path from "path";
|
|
15
|
+
function findProjectRoot(currentDir) {
|
|
16
|
+
if (fs.existsSync(path.join(currentDir, "package.json"))) {
|
|
17
|
+
return currentDir;
|
|
18
|
+
}
|
|
19
|
+
const parentDir = path.dirname(currentDir);
|
|
20
|
+
if (parentDir === currentDir) {
|
|
21
|
+
throw new Error("Could not find package.json");
|
|
22
|
+
}
|
|
23
|
+
return findProjectRoot(parentDir);
|
|
24
|
+
}
|
|
25
|
+
function loadConfig(rootDir) {
|
|
26
|
+
const projectRoot = rootDir || findProjectRoot(process.cwd());
|
|
27
|
+
const configPath = path.join(projectRoot, "prisma-flare.config.json");
|
|
28
|
+
let config3 = {
|
|
29
|
+
modelsPath: "prisma/models",
|
|
30
|
+
dbPath: "prisma/db",
|
|
31
|
+
callbacksPath: "prisma/callbacks"
|
|
32
|
+
};
|
|
33
|
+
if (fs.existsSync(configPath)) {
|
|
34
|
+
try {
|
|
35
|
+
const configFile = fs.readFileSync(configPath, "utf-8");
|
|
36
|
+
const userConfig = JSON.parse(configFile);
|
|
37
|
+
config3 = { ...config3, ...userConfig };
|
|
38
|
+
} catch {
|
|
39
|
+
console.warn("\u26A0\uFE0F Could not read prisma-flare.config.json, using defaults.");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
...config3
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/cli/generate-queries.ts
|
|
48
|
+
function toCamelCase(str) {
|
|
49
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
50
|
+
}
|
|
51
|
+
function parseRelations(schemaContent, models) {
|
|
52
|
+
const relations = /* @__PURE__ */ new Map();
|
|
53
|
+
models.forEach((model) => relations.set(model, []));
|
|
54
|
+
const modelBlockRegex = /model\s+(\w+)\s+{([^}]+)}/g;
|
|
55
|
+
let modelMatch;
|
|
56
|
+
while ((modelMatch = modelBlockRegex.exec(schemaContent)) !== null) {
|
|
57
|
+
const modelName = modelMatch[1];
|
|
58
|
+
const modelBody = modelMatch[2];
|
|
59
|
+
const lines = modelBody.split("\n");
|
|
60
|
+
for (const line of lines) {
|
|
61
|
+
const trimmed = line.trim();
|
|
62
|
+
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("@")) continue;
|
|
63
|
+
const fieldMatch = trimmed.match(/^(\w+)\s+(\w+)(\[\])?\s*/);
|
|
64
|
+
if (fieldMatch) {
|
|
65
|
+
const fieldName = fieldMatch[1];
|
|
66
|
+
const fieldType = fieldMatch[2];
|
|
67
|
+
const isArray = !!fieldMatch[3];
|
|
68
|
+
if (models.includes(fieldType)) {
|
|
69
|
+
const modelRelations = relations.get(modelName) || [];
|
|
70
|
+
modelRelations.push({ fieldName, targetModel: fieldType });
|
|
71
|
+
relations.set(modelName, modelRelations);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return relations;
|
|
77
|
+
}
|
|
78
|
+
function generateQueries() {
|
|
79
|
+
const rootDir = findProjectRoot(process.cwd());
|
|
80
|
+
const config3 = loadConfig(rootDir);
|
|
81
|
+
const schemaPath = path2.join(rootDir, "prisma", "schema.prisma");
|
|
82
|
+
if (!fs2.existsSync(schemaPath)) {
|
|
83
|
+
console.error(`\u274C Schema not found at ${schemaPath}`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const schemaContent = fs2.readFileSync(schemaPath, "utf-8");
|
|
87
|
+
const modelRegex = /model\s+(\w+)\s+{/g;
|
|
88
|
+
const models = [];
|
|
89
|
+
let match;
|
|
90
|
+
while ((match = modelRegex.exec(schemaContent)) !== null) {
|
|
91
|
+
models.push(match[1]);
|
|
92
|
+
}
|
|
93
|
+
const queriesDir = path2.join(rootDir, config3.modelsPath);
|
|
94
|
+
if (!fs2.existsSync(queriesDir)) {
|
|
95
|
+
fs2.mkdirSync(queriesDir, { recursive: true });
|
|
96
|
+
}
|
|
97
|
+
const absDbPath = path2.join(rootDir, config3.dbPath);
|
|
98
|
+
let relativePathToDb = path2.relative(queriesDir, absDbPath);
|
|
99
|
+
if (!relativePathToDb.startsWith(".")) relativePathToDb = "./" + relativePathToDb;
|
|
100
|
+
relativePathToDb = relativePathToDb.replace(/\\/g, "/");
|
|
101
|
+
models.forEach((model) => {
|
|
102
|
+
const queryFileName = `${model}.ts`;
|
|
103
|
+
const queryFilePath = path2.join(queriesDir, queryFileName);
|
|
104
|
+
const modelCamel = toCamelCase(model);
|
|
105
|
+
if (fs2.existsSync(queryFilePath)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
console.log(`Generating ${queryFileName}...`);
|
|
109
|
+
let queryBuilderImport = "import { FlareBuilder } from 'prisma-flare';";
|
|
110
|
+
const localQueryBuilderPath = path2.join(rootDir, "src/core/flareBuilder.ts");
|
|
111
|
+
if (fs2.existsSync(localQueryBuilderPath)) {
|
|
112
|
+
const absSrcPath = path2.join(rootDir, "src");
|
|
113
|
+
let relativePathToSrc = path2.relative(queriesDir, absSrcPath);
|
|
114
|
+
if (!relativePathToSrc.startsWith(".")) relativePathToSrc = "./" + relativePathToSrc;
|
|
115
|
+
relativePathToSrc = relativePathToSrc.replace(/\\/g, "/");
|
|
116
|
+
queryBuilderImport = `import { FlareBuilder } from '${relativePathToSrc}';`;
|
|
117
|
+
}
|
|
118
|
+
const content = `import { db } from '${relativePathToDb}';
|
|
119
|
+
${queryBuilderImport}
|
|
120
|
+
|
|
121
|
+
export default class ${model} extends FlareBuilder<'${modelCamel}'> {
|
|
122
|
+
constructor() {
|
|
123
|
+
super(db.${modelCamel});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
`;
|
|
127
|
+
fs2.writeFileSync(queryFilePath, content);
|
|
128
|
+
});
|
|
129
|
+
let pfDistDir;
|
|
130
|
+
const pfPackageDir = path2.join(rootDir, "node_modules", "prisma-flare");
|
|
131
|
+
let realPfPackageDir = pfPackageDir;
|
|
132
|
+
if (fs2.existsSync(pfPackageDir)) {
|
|
133
|
+
realPfPackageDir = fs2.realpathSync(pfPackageDir);
|
|
134
|
+
}
|
|
135
|
+
pfDistDir = path2.join(realPfPackageDir, "dist");
|
|
136
|
+
let dbPathWithExt = absDbPath;
|
|
137
|
+
if (!absDbPath.endsWith(".ts") && !absDbPath.endsWith(".js")) {
|
|
138
|
+
if (fs2.existsSync(absDbPath + ".ts")) {
|
|
139
|
+
dbPathWithExt = absDbPath + ".ts";
|
|
140
|
+
} else if (fs2.existsSync(absDbPath + ".js")) {
|
|
141
|
+
dbPathWithExt = absDbPath + ".js";
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
let relativePathToDbForDist = path2.relative(pfDistDir, dbPathWithExt);
|
|
145
|
+
if (!relativePathToDbForDist.startsWith(".")) relativePathToDbForDist = "./" + relativePathToDbForDist;
|
|
146
|
+
relativePathToDbForDist = relativePathToDbForDist.replace(/\\/g, "/");
|
|
147
|
+
const relativePathToDbForDts = relativePathToDbForDist;
|
|
148
|
+
const absModelsPath = path2.join(queriesDir);
|
|
149
|
+
let relativePathToModels = path2.relative(pfDistDir, absModelsPath);
|
|
150
|
+
if (!relativePathToModels.startsWith(".")) relativePathToModels = "./" + relativePathToModels;
|
|
151
|
+
relativePathToModels = relativePathToModels.replace(/\\/g, "/");
|
|
152
|
+
const relations = parseRelations(schemaContent, models);
|
|
153
|
+
const getters = models.map((model) => {
|
|
154
|
+
const modelCamel = toCamelCase(model);
|
|
155
|
+
const customPlural = config3.plurals?.[model];
|
|
156
|
+
const modelPlural = customPlural || pluralize(modelCamel);
|
|
157
|
+
return ` static get ${modelPlural}() {
|
|
158
|
+
return new ${model}();
|
|
159
|
+
}`;
|
|
160
|
+
}).join("\n\n");
|
|
161
|
+
const registrationLines = [];
|
|
162
|
+
models.forEach((model) => {
|
|
163
|
+
const modelCamel = toCamelCase(model);
|
|
164
|
+
const customPlural = config3.plurals?.[model];
|
|
165
|
+
const modelPlural = customPlural || pluralize(modelCamel);
|
|
166
|
+
registrationLines.push(`modelRegistry.register('${modelCamel}', ${model});`);
|
|
167
|
+
registrationLines.push(`modelRegistry.register('${modelPlural}', ${model});`);
|
|
168
|
+
});
|
|
169
|
+
relations.forEach((rels, modelName) => {
|
|
170
|
+
rels.forEach((rel) => {
|
|
171
|
+
registrationLines.push(`modelRegistry.register('${rel.fieldName}', ${rel.targetModel});`);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
const modelRegistrations = registrationLines.join("\n");
|
|
175
|
+
const generatedJsPath = path2.join(pfDistDir, "generated.js");
|
|
176
|
+
const imports = models.map((model) => {
|
|
177
|
+
return `import ${model} from '${relativePathToModels}/${model}.ts';`;
|
|
178
|
+
}).join("\n");
|
|
179
|
+
const generatedContent = `
|
|
180
|
+
import { db } from '${relativePathToDbForDist}';
|
|
181
|
+
import { modelRegistry } from './index.js';
|
|
182
|
+
${imports}
|
|
183
|
+
|
|
184
|
+
// Register all models so include() can use custom model classes
|
|
185
|
+
${modelRegistrations}
|
|
186
|
+
|
|
187
|
+
export class DB {
|
|
188
|
+
static get instance() {
|
|
189
|
+
return db;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
${getters}
|
|
193
|
+
}
|
|
194
|
+
`;
|
|
195
|
+
fs2.writeFileSync(generatedJsPath, generatedContent);
|
|
196
|
+
const generatedCjsPath = path2.join(pfDistDir, "generated.cjs");
|
|
197
|
+
const importsCjs = models.map((model) => {
|
|
198
|
+
return `const ${model} = require('${relativePathToModels}/${model}').default;`;
|
|
199
|
+
}).join("\n");
|
|
200
|
+
const generatedCjsContent = `
|
|
201
|
+
const { db } = require('${relativePathToDbForDist}');
|
|
202
|
+
const { modelRegistry } = require('./index.cjs');
|
|
203
|
+
${importsCjs}
|
|
204
|
+
|
|
205
|
+
// Register all models so include() can use custom model classes
|
|
206
|
+
${modelRegistrations}
|
|
207
|
+
|
|
208
|
+
class DB {
|
|
209
|
+
static get instance() {
|
|
210
|
+
return db;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
${getters}
|
|
214
|
+
}
|
|
215
|
+
exports.DB = DB;
|
|
216
|
+
`;
|
|
217
|
+
fs2.writeFileSync(generatedCjsPath, generatedCjsContent);
|
|
218
|
+
const generatedDtsPath = path2.join(pfDistDir, "generated.d.ts");
|
|
219
|
+
const importsDts = models.map((model) => {
|
|
220
|
+
return `import ${model} from '${relativePathToModels}/${model}.ts';`;
|
|
221
|
+
}).join("\n");
|
|
222
|
+
const gettersTypes = models.map((model) => {
|
|
223
|
+
const modelCamel = toCamelCase(model);
|
|
224
|
+
const customPlural = config3.plurals?.[model];
|
|
225
|
+
const modelPlural = customPlural || pluralize(modelCamel);
|
|
226
|
+
return ` static get ${modelPlural}(): ${model};`;
|
|
227
|
+
}).join("\n");
|
|
228
|
+
const relationMapEntries = [];
|
|
229
|
+
models.forEach((model) => {
|
|
230
|
+
const modelCamel = toCamelCase(model);
|
|
231
|
+
const customPlural = config3.plurals?.[model];
|
|
232
|
+
const modelPlural = customPlural || pluralize(modelCamel);
|
|
233
|
+
relationMapEntries.push(` ${modelCamel}: ${model};`);
|
|
234
|
+
relationMapEntries.push(` ${modelPlural}: ${model};`);
|
|
235
|
+
});
|
|
236
|
+
relations.forEach((rels, modelName) => {
|
|
237
|
+
rels.forEach((rel) => {
|
|
238
|
+
const entry = ` ${rel.fieldName}: ${rel.targetModel};`;
|
|
239
|
+
if (!relationMapEntries.includes(entry)) {
|
|
240
|
+
relationMapEntries.push(entry);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
const generatedDtsContent = `
|
|
245
|
+
import { db } from '${relativePathToDbForDts}';
|
|
246
|
+
${importsDts}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Module augmentation to provide type-safe includes.
|
|
250
|
+
* This maps relation field names to their custom model classes.
|
|
251
|
+
*/
|
|
252
|
+
declare module 'prisma-flare' {
|
|
253
|
+
interface RelationModelMap {
|
|
254
|
+
${relationMapEntries.join("\n")}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export declare class DB {
|
|
259
|
+
static get instance(): typeof db;
|
|
260
|
+
|
|
261
|
+
${gettersTypes}
|
|
262
|
+
}
|
|
263
|
+
`;
|
|
264
|
+
fs2.writeFileSync(generatedDtsPath, generatedDtsContent);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/cli/db-migrate.ts
|
|
268
|
+
var config2 = loadConfig();
|
|
269
|
+
dotenv.config({ path: config2.envPath });
|
|
270
|
+
function runMigrations() {
|
|
271
|
+
const databaseUrl = process.env.DATABASE_URL;
|
|
272
|
+
if (!databaseUrl) {
|
|
273
|
+
console.error("\u274C DATABASE_URL environment variable is not set");
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
console.log("\u{1F504} Running Prisma migrations...");
|
|
278
|
+
const args = process.argv.slice(2).join(" ");
|
|
279
|
+
const command = `npx prisma migrate dev ${args} && npx prisma generate`;
|
|
280
|
+
console.log(`Running: ${command}`);
|
|
281
|
+
execSync(command, {
|
|
282
|
+
stdio: "inherit",
|
|
283
|
+
env: process.env
|
|
284
|
+
});
|
|
285
|
+
console.log("\u2713 Migrations completed successfully");
|
|
286
|
+
console.log("\u{1F504} Generating Query classes...");
|
|
287
|
+
generateQueries();
|
|
288
|
+
console.log("\u2713 Query classes generated successfully");
|
|
289
|
+
process.exit(0);
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.error("\u274C Error running migrations:", error);
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
runMigrations();
|