@famgia/omnify-laravel 0.0.14 → 0.0.15
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/{chunk-UVF7W2J2.js → chunk-REDFZUQY.js} +550 -15
- package/dist/chunk-REDFZUQY.js.map +1 -0
- package/dist/index.cjs +549 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin.cjs +547 -14
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +16 -1
- package/dist/plugin.d.ts +16 -1
- package/dist/plugin.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-UVF7W2J2.js.map +0 -1
package/dist/index.js
CHANGED
package/dist/plugin.cjs
CHANGED
|
@@ -24,6 +24,8 @@ __export(plugin_exports, {
|
|
|
24
24
|
laravelPlugin: () => laravelPlugin
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(plugin_exports);
|
|
27
|
+
var import_node_fs = require("fs");
|
|
28
|
+
var import_node_path = require("path");
|
|
27
29
|
|
|
28
30
|
// src/migration/schema-builder.ts
|
|
29
31
|
var TYPE_METHOD_MAP = {
|
|
@@ -103,9 +105,8 @@ function propertyToColumnMethod(propertyName, property) {
|
|
|
103
105
|
if (baseProp.unique) {
|
|
104
106
|
modifiers.push({ method: "unique" });
|
|
105
107
|
}
|
|
106
|
-
if (baseProp.default !== void 0) {
|
|
107
|
-
|
|
108
|
-
modifiers.push({ method: "default", args: [defaultValue] });
|
|
108
|
+
if (baseProp.default !== void 0 && baseProp.default !== null) {
|
|
109
|
+
modifiers.push({ method: "default", args: [baseProp.default] });
|
|
109
110
|
}
|
|
110
111
|
if (baseProp.unsigned && (method === "integer" || method === "bigInteger")) {
|
|
111
112
|
modifiers.push({ method: "unsigned" });
|
|
@@ -341,6 +342,12 @@ function formatColumnMethod(column) {
|
|
|
341
342
|
if (typeof arg === "string") {
|
|
342
343
|
return `'${arg}'`;
|
|
343
344
|
}
|
|
345
|
+
if (typeof arg === "boolean") {
|
|
346
|
+
return arg ? "true" : "false";
|
|
347
|
+
}
|
|
348
|
+
if (typeof arg === "number") {
|
|
349
|
+
return String(arg);
|
|
350
|
+
}
|
|
344
351
|
return String(arg);
|
|
345
352
|
}).join(", ");
|
|
346
353
|
code += `->${modifier.method}(${modArgs})`;
|
|
@@ -448,6 +455,7 @@ function generatePivotTableBlueprint(pivot) {
|
|
|
448
455
|
args: [pivot.targetColumn],
|
|
449
456
|
modifiers: []
|
|
450
457
|
});
|
|
458
|
+
columns.push(...generateTimestampColumns());
|
|
451
459
|
foreignKeys.push({
|
|
452
460
|
columns: [pivot.sourceColumn],
|
|
453
461
|
references: "id",
|
|
@@ -846,7 +854,7 @@ function generateEntityBaseModel(schema, schemas, options, stubContent, authStub
|
|
|
846
854
|
if (assoc.target) {
|
|
847
855
|
imports.push(`use ${options.modelNamespace}\\${toPascalCase(assoc.target)};`);
|
|
848
856
|
}
|
|
849
|
-
relations.push(generateRelation(propName, assoc, options));
|
|
857
|
+
relations.push(generateRelation(propName, assoc, schema, schemas, options));
|
|
850
858
|
if (assoc.relation === "ManyToOne" || assoc.relation === "OneToOne") {
|
|
851
859
|
if (!assoc.mappedBy) {
|
|
852
860
|
const fkName = toSnakeCase(propName) + "_id";
|
|
@@ -903,7 +911,22 @@ ${docProperties.join("\n")}
|
|
|
903
911
|
schemaName: schema.name
|
|
904
912
|
};
|
|
905
913
|
}
|
|
906
|
-
function
|
|
914
|
+
function findInverseRelation(currentSchemaName, targetSchemaName, schemas) {
|
|
915
|
+
const targetSchema = schemas[targetSchemaName];
|
|
916
|
+
if (!targetSchema || !targetSchema.properties) {
|
|
917
|
+
return null;
|
|
918
|
+
}
|
|
919
|
+
for (const [propName, propDef] of Object.entries(targetSchema.properties)) {
|
|
920
|
+
if (propDef.type === "Association") {
|
|
921
|
+
const assoc = propDef;
|
|
922
|
+
if (assoc.relation === "ManyToOne" && assoc.target === currentSchemaName) {
|
|
923
|
+
return propName;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
return null;
|
|
928
|
+
}
|
|
929
|
+
function generateRelation(propName, assoc, schema, schemas, options) {
|
|
907
930
|
const methodName = toCamelCase(propName);
|
|
908
931
|
const targetClass = assoc.target ? toPascalCase(assoc.target) : "";
|
|
909
932
|
const fkName = toSnakeCase(propName) + "_id";
|
|
@@ -933,14 +956,28 @@ function generateRelation(propName, assoc, options) {
|
|
|
933
956
|
{
|
|
934
957
|
return $this->belongsTo(${targetClass}::class, '${fkName}');
|
|
935
958
|
}`;
|
|
936
|
-
case "OneToMany":
|
|
959
|
+
case "OneToMany": {
|
|
960
|
+
let foreignKey;
|
|
961
|
+
if (assoc.inversedBy) {
|
|
962
|
+
foreignKey = toSnakeCase(assoc.inversedBy) + "_id";
|
|
963
|
+
} else if (assoc.target) {
|
|
964
|
+
const inverseRelation = findInverseRelation(schema.name, assoc.target, schemas);
|
|
965
|
+
if (inverseRelation) {
|
|
966
|
+
foreignKey = toSnakeCase(inverseRelation) + "_id";
|
|
967
|
+
} else {
|
|
968
|
+
foreignKey = toSnakeCase(schema.name) + "_id";
|
|
969
|
+
}
|
|
970
|
+
} else {
|
|
971
|
+
foreignKey = toSnakeCase(propName) + "_id";
|
|
972
|
+
}
|
|
937
973
|
return ` /**
|
|
938
974
|
* Get the ${propName} for this model.
|
|
939
975
|
*/
|
|
940
976
|
public function ${methodName}(): HasMany
|
|
941
977
|
{
|
|
942
|
-
return $this->hasMany(${targetClass}::class, '${
|
|
978
|
+
return $this->hasMany(${targetClass}::class, '${foreignKey}');
|
|
943
979
|
}`;
|
|
980
|
+
}
|
|
944
981
|
case "ManyToMany": {
|
|
945
982
|
const pivotTable = assoc.joinTable ?? `${toSnakeCase(propName)}_pivot`;
|
|
946
983
|
return ` /**
|
|
@@ -1237,14 +1274,73 @@ class {{CLASS_NAME}} extends {{CLASS_NAME}}BaseModel
|
|
|
1237
1274
|
|
|
1238
1275
|
// Add your custom methods here
|
|
1239
1276
|
}
|
|
1277
|
+
`,
|
|
1278
|
+
"service-provider": `<?php
|
|
1279
|
+
|
|
1280
|
+
namespace App\\Providers;
|
|
1281
|
+
|
|
1282
|
+
use Illuminate\\Database\\Eloquent\\Relations\\Relation;
|
|
1283
|
+
use Illuminate\\Support\\ServiceProvider;
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* Omnify Service Provider
|
|
1287
|
+
*
|
|
1288
|
+
* DO NOT EDIT - This file is auto-generated by Omnify.
|
|
1289
|
+
* Any changes will be overwritten on next generation.
|
|
1290
|
+
*
|
|
1291
|
+
* - Loads Omnify migrations from database/migrations/omnify
|
|
1292
|
+
* - Registers morph map for polymorphic relationships
|
|
1293
|
+
*
|
|
1294
|
+
* @generated by @famgia/omnify-laravel
|
|
1295
|
+
*/
|
|
1296
|
+
class OmnifyServiceProvider extends ServiceProvider
|
|
1297
|
+
{
|
|
1298
|
+
/**
|
|
1299
|
+
* Register any application services.
|
|
1300
|
+
*/
|
|
1301
|
+
public function register(): void
|
|
1302
|
+
{
|
|
1303
|
+
//
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
/**
|
|
1307
|
+
* Bootstrap any application services.
|
|
1308
|
+
*/
|
|
1309
|
+
public function boot(): void
|
|
1310
|
+
{
|
|
1311
|
+
// Load Omnify migrations from custom directory
|
|
1312
|
+
$this->loadMigrationsFrom(database_path('migrations/omnify'));
|
|
1313
|
+
|
|
1314
|
+
// Register morph map for polymorphic relationships
|
|
1315
|
+
Relation::enforceMorphMap([
|
|
1316
|
+
{{MORPH_MAP}}
|
|
1317
|
+
]);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1240
1320
|
`
|
|
1241
1321
|
};
|
|
1242
1322
|
return stubs[stubName] ?? "";
|
|
1243
1323
|
}
|
|
1324
|
+
function generateServiceProvider(schemas, options, stubContent) {
|
|
1325
|
+
const morphMap = Object.values(schemas).filter((s) => s.kind !== "enum").map((s) => {
|
|
1326
|
+
const className = toPascalCase(s.name);
|
|
1327
|
+
return ` '${s.name}' => \\${options.modelNamespace}\\${className}::class,`;
|
|
1328
|
+
}).join("\n");
|
|
1329
|
+
const content = stubContent.replace(/\{\{MORPH_MAP\}\}/g, morphMap);
|
|
1330
|
+
return {
|
|
1331
|
+
path: "app/Providers/OmnifyServiceProvider.php",
|
|
1332
|
+
content,
|
|
1333
|
+
type: "service-provider",
|
|
1334
|
+
overwrite: true,
|
|
1335
|
+
// Always overwrite to keep morph map in sync
|
|
1336
|
+
schemaName: "__service_provider__"
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1244
1339
|
function generateModels(schemas, options) {
|
|
1245
1340
|
const resolved = resolveOptions(options);
|
|
1246
1341
|
const models = [];
|
|
1247
1342
|
models.push(generateBaseModel(schemas, resolved, getStubContent("base-model")));
|
|
1343
|
+
models.push(generateServiceProvider(schemas, resolved, getStubContent("service-provider")));
|
|
1248
1344
|
for (const schema of Object.values(schemas)) {
|
|
1249
1345
|
if (schema.kind === "enum") {
|
|
1250
1346
|
continue;
|
|
@@ -1263,6 +1359,355 @@ function generateModels(schemas, options) {
|
|
|
1263
1359
|
function getModelPath(model) {
|
|
1264
1360
|
return model.path;
|
|
1265
1361
|
}
|
|
1362
|
+
function generateProviderRegistration(existingContent, laravelVersion) {
|
|
1363
|
+
const providerClass = "App\\Providers\\OmnifyServiceProvider::class";
|
|
1364
|
+
const providerLine = ` ${providerClass},`;
|
|
1365
|
+
if (existingContent && existingContent.includes("OmnifyServiceProvider")) {
|
|
1366
|
+
return {
|
|
1367
|
+
path: laravelVersion === "laravel11+" ? "bootstrap/providers.php" : "config/app.php",
|
|
1368
|
+
content: existingContent,
|
|
1369
|
+
laravelVersion,
|
|
1370
|
+
alreadyRegistered: true
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
if (laravelVersion === "laravel11+") {
|
|
1374
|
+
if (existingContent) {
|
|
1375
|
+
const lines = existingContent.split("\n");
|
|
1376
|
+
const result = [];
|
|
1377
|
+
let inserted = false;
|
|
1378
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1379
|
+
const line = lines[i];
|
|
1380
|
+
if (!inserted && line.trim() === "];") {
|
|
1381
|
+
result.push(providerLine);
|
|
1382
|
+
inserted = true;
|
|
1383
|
+
}
|
|
1384
|
+
result.push(line);
|
|
1385
|
+
}
|
|
1386
|
+
return {
|
|
1387
|
+
path: "bootstrap/providers.php",
|
|
1388
|
+
content: result.join("\n"),
|
|
1389
|
+
laravelVersion,
|
|
1390
|
+
alreadyRegistered: false
|
|
1391
|
+
};
|
|
1392
|
+
} else {
|
|
1393
|
+
return {
|
|
1394
|
+
path: "bootstrap/providers.php",
|
|
1395
|
+
content: `<?php
|
|
1396
|
+
|
|
1397
|
+
return [
|
|
1398
|
+
App\\Providers\\AppServiceProvider::class,
|
|
1399
|
+
${providerLine}
|
|
1400
|
+
];
|
|
1401
|
+
`,
|
|
1402
|
+
laravelVersion,
|
|
1403
|
+
alreadyRegistered: false
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
} else {
|
|
1407
|
+
if (existingContent) {
|
|
1408
|
+
const providersSectionRegex = /'providers'\s*=>\s*\[[\s\S]*?\n(\s*)\]/m;
|
|
1409
|
+
const match = existingContent.match(providersSectionRegex);
|
|
1410
|
+
if (match) {
|
|
1411
|
+
const providersStart = existingContent.indexOf("'providers'");
|
|
1412
|
+
if (providersStart === -1) {
|
|
1413
|
+
return null;
|
|
1414
|
+
}
|
|
1415
|
+
let depth = 0;
|
|
1416
|
+
let foundStart = false;
|
|
1417
|
+
let insertPos = -1;
|
|
1418
|
+
for (let i = providersStart; i < existingContent.length; i++) {
|
|
1419
|
+
const char = existingContent[i];
|
|
1420
|
+
if (char === "[") {
|
|
1421
|
+
foundStart = true;
|
|
1422
|
+
depth++;
|
|
1423
|
+
} else if (char === "]") {
|
|
1424
|
+
depth--;
|
|
1425
|
+
if (foundStart && depth === 0) {
|
|
1426
|
+
insertPos = i;
|
|
1427
|
+
break;
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
if (insertPos !== -1) {
|
|
1432
|
+
const beforeClose = existingContent.substring(0, insertPos);
|
|
1433
|
+
const lastNewline = beforeClose.lastIndexOf("\n");
|
|
1434
|
+
const content = existingContent.substring(0, lastNewline + 1) + providerLine + "\n" + existingContent.substring(lastNewline + 1);
|
|
1435
|
+
return {
|
|
1436
|
+
path: "config/app.php",
|
|
1437
|
+
content,
|
|
1438
|
+
laravelVersion,
|
|
1439
|
+
alreadyRegistered: false
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
return null;
|
|
1444
|
+
} else {
|
|
1445
|
+
return null;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
// src/factory/generator.ts
|
|
1451
|
+
function resolveOptions2(options) {
|
|
1452
|
+
return {
|
|
1453
|
+
modelNamespace: options?.modelNamespace ?? "App\\Models",
|
|
1454
|
+
factoryPath: options?.factoryPath ?? "database/factories",
|
|
1455
|
+
fakerLocale: options?.fakerLocale ?? "en_US"
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1458
|
+
function getStubContent2() {
|
|
1459
|
+
return `<?php
|
|
1460
|
+
|
|
1461
|
+
namespace Database\\Factories;
|
|
1462
|
+
|
|
1463
|
+
use {{MODEL_NAMESPACE}}\\{{MODEL_NAME}};
|
|
1464
|
+
use Illuminate\\Database\\Eloquent\\Factories\\Factory;
|
|
1465
|
+
{{IMPORTS}}
|
|
1466
|
+
|
|
1467
|
+
/**
|
|
1468
|
+
* @extends Factory<{{MODEL_NAME}}>
|
|
1469
|
+
*/
|
|
1470
|
+
class {{MODEL_NAME}}Factory extends Factory
|
|
1471
|
+
{
|
|
1472
|
+
protected $model = {{MODEL_NAME}}::class;
|
|
1473
|
+
|
|
1474
|
+
/**
|
|
1475
|
+
* Define the model's default state.
|
|
1476
|
+
*
|
|
1477
|
+
* @return array<string, mixed>
|
|
1478
|
+
*/
|
|
1479
|
+
public function definition(): array
|
|
1480
|
+
{
|
|
1481
|
+
return [
|
|
1482
|
+
{{ATTRIBUTES}}
|
|
1483
|
+
];
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
`;
|
|
1487
|
+
}
|
|
1488
|
+
function generateFakeData(propertyName, property, schema, schemas) {
|
|
1489
|
+
const type = property.type;
|
|
1490
|
+
if (["deleted_at", "created_at", "updated_at"].includes(propertyName)) {
|
|
1491
|
+
return null;
|
|
1492
|
+
}
|
|
1493
|
+
if (type === "Association") {
|
|
1494
|
+
return null;
|
|
1495
|
+
}
|
|
1496
|
+
switch (type) {
|
|
1497
|
+
case "String":
|
|
1498
|
+
return generateStringFake(propertyName, property);
|
|
1499
|
+
case "Email":
|
|
1500
|
+
return `'${propertyName}' => fake()->unique()->safeEmail(),`;
|
|
1501
|
+
case "Password":
|
|
1502
|
+
return `'${propertyName}' => bcrypt('password'),`;
|
|
1503
|
+
case "Int":
|
|
1504
|
+
case "BigInt":
|
|
1505
|
+
return generateIntFake(propertyName, property);
|
|
1506
|
+
case "Float":
|
|
1507
|
+
case "Decimal":
|
|
1508
|
+
return `'${propertyName}' => fake()->randomFloat(2, 1, 10000),`;
|
|
1509
|
+
case "Boolean":
|
|
1510
|
+
return `'${propertyName}' => fake()->boolean(),`;
|
|
1511
|
+
case "Text":
|
|
1512
|
+
return `'${propertyName}' => fake()->paragraphs(3, true),`;
|
|
1513
|
+
case "LongText":
|
|
1514
|
+
return `'${propertyName}' => fake()->paragraphs(5, true),`;
|
|
1515
|
+
case "Date":
|
|
1516
|
+
return `'${propertyName}' => fake()->date(),`;
|
|
1517
|
+
case "Time":
|
|
1518
|
+
return `'${propertyName}' => fake()->time(),`;
|
|
1519
|
+
case "Timestamp":
|
|
1520
|
+
case "DateTime":
|
|
1521
|
+
return `'${propertyName}' => fake()->dateTime(),`;
|
|
1522
|
+
case "Json":
|
|
1523
|
+
return `'${propertyName}' => [],`;
|
|
1524
|
+
case "Enum":
|
|
1525
|
+
return generateEnumFake(propertyName, property);
|
|
1526
|
+
case "EnumRef":
|
|
1527
|
+
return generateEnumRefFake(propertyName, property, schemas);
|
|
1528
|
+
default:
|
|
1529
|
+
return `'${propertyName}' => fake()->sentence(),`;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
function generateStringFake(propertyName, property) {
|
|
1533
|
+
if (propertyName === "slug") {
|
|
1534
|
+
return `'${propertyName}' => fake()->unique()->slug(),`;
|
|
1535
|
+
}
|
|
1536
|
+
if (propertyName === "uuid" || propertyName === "uid") {
|
|
1537
|
+
return `'${propertyName}' => (string) \\Illuminate\\Support\\Str::uuid(),`;
|
|
1538
|
+
}
|
|
1539
|
+
if (propertyName.includes("email")) {
|
|
1540
|
+
return `'${propertyName}' => fake()->unique()->safeEmail(),`;
|
|
1541
|
+
}
|
|
1542
|
+
if (propertyName.includes("phone")) {
|
|
1543
|
+
return `'${propertyName}' => fake()->phoneNumber(),`;
|
|
1544
|
+
}
|
|
1545
|
+
if (propertyName.includes("image") || propertyName.includes("photo") || propertyName.includes("avatar")) {
|
|
1546
|
+
return `'${propertyName}' => fake()->imageUrl(),`;
|
|
1547
|
+
}
|
|
1548
|
+
if (propertyName.includes("url") || propertyName.includes("website")) {
|
|
1549
|
+
return `'${propertyName}' => fake()->url(),`;
|
|
1550
|
+
}
|
|
1551
|
+
if (propertyName.includes("path") || propertyName.includes("file")) {
|
|
1552
|
+
return `'${propertyName}' => 'uploads/' . fake()->uuid() . '.jpg',`;
|
|
1553
|
+
}
|
|
1554
|
+
if (propertyName === "name" || propertyName === "title") {
|
|
1555
|
+
return `'${propertyName}' => fake()->sentence(3),`;
|
|
1556
|
+
}
|
|
1557
|
+
if (propertyName.includes("name")) {
|
|
1558
|
+
return `'${propertyName}' => fake()->name(),`;
|
|
1559
|
+
}
|
|
1560
|
+
if (propertyName.includes("address")) {
|
|
1561
|
+
return `'${propertyName}' => fake()->address(),`;
|
|
1562
|
+
}
|
|
1563
|
+
if (propertyName.includes("city")) {
|
|
1564
|
+
return `'${propertyName}' => fake()->city(),`;
|
|
1565
|
+
}
|
|
1566
|
+
if (propertyName.includes("country")) {
|
|
1567
|
+
return `'${propertyName}' => fake()->country(),`;
|
|
1568
|
+
}
|
|
1569
|
+
if (propertyName.includes("zip") || propertyName.includes("postal")) {
|
|
1570
|
+
return `'${propertyName}' => fake()->postcode(),`;
|
|
1571
|
+
}
|
|
1572
|
+
if (propertyName.includes("color")) {
|
|
1573
|
+
return `'${propertyName}' => fake()->hexColor(),`;
|
|
1574
|
+
}
|
|
1575
|
+
if (propertyName.includes("token") || propertyName.includes("secret") || propertyName.includes("key")) {
|
|
1576
|
+
return `'${propertyName}' => \\Illuminate\\Support\\Str::random(32),`;
|
|
1577
|
+
}
|
|
1578
|
+
if (propertyName.includes("code")) {
|
|
1579
|
+
return `'${propertyName}' => fake()->unique()->regexify('[A-Z0-9]{8}'),`;
|
|
1580
|
+
}
|
|
1581
|
+
const length = property.length;
|
|
1582
|
+
if (length && length <= 50) {
|
|
1583
|
+
return `'${propertyName}' => fake()->words(3, true),`;
|
|
1584
|
+
}
|
|
1585
|
+
return `'${propertyName}' => fake()->sentence(),`;
|
|
1586
|
+
}
|
|
1587
|
+
function generateIntFake(propertyName, property) {
|
|
1588
|
+
if (propertyName.includes("count") || propertyName.includes("quantity")) {
|
|
1589
|
+
return `'${propertyName}' => fake()->numberBetween(0, 100),`;
|
|
1590
|
+
}
|
|
1591
|
+
if (propertyName.includes("price") || propertyName.includes("amount") || propertyName.includes("cost")) {
|
|
1592
|
+
return `'${propertyName}' => fake()->numberBetween(100, 10000),`;
|
|
1593
|
+
}
|
|
1594
|
+
if (propertyName.includes("order") || propertyName.includes("sort") || propertyName.includes("position")) {
|
|
1595
|
+
return `'${propertyName}' => fake()->numberBetween(1, 100),`;
|
|
1596
|
+
}
|
|
1597
|
+
if (propertyName.includes("age")) {
|
|
1598
|
+
return `'${propertyName}' => fake()->numberBetween(18, 80),`;
|
|
1599
|
+
}
|
|
1600
|
+
if (propertyName.includes("year")) {
|
|
1601
|
+
return `'${propertyName}' => fake()->year(),`;
|
|
1602
|
+
}
|
|
1603
|
+
return `'${propertyName}' => fake()->numberBetween(1, 1000),`;
|
|
1604
|
+
}
|
|
1605
|
+
function generateEnumFake(propertyName, property) {
|
|
1606
|
+
const enumValues = property.enum;
|
|
1607
|
+
if (!enumValues || enumValues.length === 0) {
|
|
1608
|
+
return `'${propertyName}' => null,`;
|
|
1609
|
+
}
|
|
1610
|
+
const values = enumValues.map((v) => typeof v === "string" ? v : v.value);
|
|
1611
|
+
const valuesStr = values.map((v) => `'${v}'`).join(", ");
|
|
1612
|
+
return `'${propertyName}' => fake()->randomElement([${valuesStr}]),`;
|
|
1613
|
+
}
|
|
1614
|
+
function generateEnumRefFake(propertyName, property, schemas) {
|
|
1615
|
+
const enumName = property.enum;
|
|
1616
|
+
if (!enumName) {
|
|
1617
|
+
return `'${propertyName}' => null,`;
|
|
1618
|
+
}
|
|
1619
|
+
const enumSchema = schemas[enumName];
|
|
1620
|
+
if (!enumSchema || enumSchema.kind !== "enum" || !enumSchema.values) {
|
|
1621
|
+
return `'${propertyName}' => null,`;
|
|
1622
|
+
}
|
|
1623
|
+
const valuesStr = enumSchema.values.map((v) => `'${v}'`).join(", ");
|
|
1624
|
+
return `'${propertyName}' => fake()->randomElement([${valuesStr}]),`;
|
|
1625
|
+
}
|
|
1626
|
+
function generateAssociationFake(propertyName, property, schema, schemas, modelNamespace) {
|
|
1627
|
+
if (property.type !== "Association") {
|
|
1628
|
+
return null;
|
|
1629
|
+
}
|
|
1630
|
+
const relation = property.relation;
|
|
1631
|
+
const target = property.target;
|
|
1632
|
+
if (relation !== "ManyToOne" || !target) {
|
|
1633
|
+
return null;
|
|
1634
|
+
}
|
|
1635
|
+
const foreignKey = `${toSnakeCase(propertyName)}_id`;
|
|
1636
|
+
const isNullable2 = property.nullable ?? false;
|
|
1637
|
+
const targetSchema = schemas[target];
|
|
1638
|
+
if (!targetSchema) {
|
|
1639
|
+
return null;
|
|
1640
|
+
}
|
|
1641
|
+
let fake;
|
|
1642
|
+
if (isNullable2) {
|
|
1643
|
+
fake = `'${foreignKey}' => ${target}::query()->inRandomOrder()->first()?->id,`;
|
|
1644
|
+
} else {
|
|
1645
|
+
fake = `'${foreignKey}' => ${target}::query()->inRandomOrder()->first()?->id ?? ${target}::factory()->create()->id,`;
|
|
1646
|
+
}
|
|
1647
|
+
let importStatement;
|
|
1648
|
+
if (target !== schema.name) {
|
|
1649
|
+
importStatement = `use ${modelNamespace}\\${target};`;
|
|
1650
|
+
}
|
|
1651
|
+
return { fake, import: importStatement };
|
|
1652
|
+
}
|
|
1653
|
+
function generateFactory(schema, schemas, options, stubContent) {
|
|
1654
|
+
if (schema.kind === "enum") {
|
|
1655
|
+
return null;
|
|
1656
|
+
}
|
|
1657
|
+
const modelName = toPascalCase(schema.name);
|
|
1658
|
+
const factoryName = `${modelName}Factory`;
|
|
1659
|
+
const attributes = [];
|
|
1660
|
+
const imports = [];
|
|
1661
|
+
if (schema.properties) {
|
|
1662
|
+
for (const [propName, prop] of Object.entries(schema.properties)) {
|
|
1663
|
+
if (prop.type === "Association") {
|
|
1664
|
+
const assocResult = generateAssociationFake(propName, prop, schema, schemas, options.modelNamespace);
|
|
1665
|
+
if (assocResult) {
|
|
1666
|
+
attributes.push(assocResult.fake);
|
|
1667
|
+
if (assocResult.import) {
|
|
1668
|
+
imports.push(assocResult.import);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
continue;
|
|
1672
|
+
}
|
|
1673
|
+
const fake = generateFakeData(propName, prop, schema, schemas);
|
|
1674
|
+
if (fake) {
|
|
1675
|
+
attributes.push(fake);
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
let content = stubContent;
|
|
1680
|
+
content = content.replace(/\{\{MODEL_NAMESPACE\}\}/g, options.modelNamespace);
|
|
1681
|
+
content = content.replace(/\{\{MODEL_NAME\}\}/g, modelName);
|
|
1682
|
+
const uniqueImports = [...new Set(imports)];
|
|
1683
|
+
const importsStr = uniqueImports.length > 0 ? "\n" + uniqueImports.join("\n") : "";
|
|
1684
|
+
content = content.replace(/\{\{IMPORTS\}\}/g, importsStr);
|
|
1685
|
+
const attributesStr = attributes.length > 0 ? attributes.map((a) => ` ${a}`).join("\n") : "";
|
|
1686
|
+
content = content.replace(/\{\{ATTRIBUTES\}\}/g, attributesStr);
|
|
1687
|
+
return {
|
|
1688
|
+
name: factoryName,
|
|
1689
|
+
schemaName: schema.name,
|
|
1690
|
+
path: `${options.factoryPath}/${factoryName}.php`,
|
|
1691
|
+
content,
|
|
1692
|
+
overwrite: false
|
|
1693
|
+
// Factories should not overwrite existing files
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
function generateFactories(schemas, options) {
|
|
1697
|
+
const resolved = resolveOptions2(options);
|
|
1698
|
+
const stubContent = getStubContent2();
|
|
1699
|
+
const factories = [];
|
|
1700
|
+
for (const schema of Object.values(schemas)) {
|
|
1701
|
+
const factory = generateFactory(schema, schemas, resolved, stubContent);
|
|
1702
|
+
if (factory) {
|
|
1703
|
+
factories.push(factory);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
return factories;
|
|
1707
|
+
}
|
|
1708
|
+
function getFactoryPath(factory) {
|
|
1709
|
+
return factory.path;
|
|
1710
|
+
}
|
|
1266
1711
|
|
|
1267
1712
|
// src/plugin.ts
|
|
1268
1713
|
var LARAVEL_CONFIG_SCHEMA = {
|
|
@@ -1271,8 +1716,8 @@ var LARAVEL_CONFIG_SCHEMA = {
|
|
|
1271
1716
|
key: "migrationsPath",
|
|
1272
1717
|
type: "path",
|
|
1273
1718
|
label: "Migrations Path",
|
|
1274
|
-
description: "Directory for Laravel migration files",
|
|
1275
|
-
default: "database/migrations",
|
|
1719
|
+
description: "Directory for Laravel migration files (loaded via OmnifyServiceProvider)",
|
|
1720
|
+
default: "database/migrations/omnify",
|
|
1276
1721
|
group: "output"
|
|
1277
1722
|
},
|
|
1278
1723
|
{
|
|
@@ -1299,6 +1744,22 @@ var LARAVEL_CONFIG_SCHEMA = {
|
|
|
1299
1744
|
default: true,
|
|
1300
1745
|
group: "options"
|
|
1301
1746
|
},
|
|
1747
|
+
{
|
|
1748
|
+
key: "factoriesPath",
|
|
1749
|
+
type: "path",
|
|
1750
|
+
label: "Factories Path",
|
|
1751
|
+
description: "Directory for Laravel factory files",
|
|
1752
|
+
default: "database/factories",
|
|
1753
|
+
group: "output"
|
|
1754
|
+
},
|
|
1755
|
+
{
|
|
1756
|
+
key: "generateFactories",
|
|
1757
|
+
type: "boolean",
|
|
1758
|
+
label: "Generate Factories",
|
|
1759
|
+
description: "Generate Laravel factory classes for testing",
|
|
1760
|
+
default: true,
|
|
1761
|
+
group: "options"
|
|
1762
|
+
},
|
|
1302
1763
|
{
|
|
1303
1764
|
key: "connection",
|
|
1304
1765
|
type: "string",
|
|
@@ -1309,20 +1770,23 @@ var LARAVEL_CONFIG_SCHEMA = {
|
|
|
1309
1770
|
}
|
|
1310
1771
|
]
|
|
1311
1772
|
};
|
|
1312
|
-
function
|
|
1773
|
+
function resolveOptions3(options) {
|
|
1313
1774
|
return {
|
|
1314
|
-
migrationsPath: options?.migrationsPath ?? "database/migrations",
|
|
1775
|
+
migrationsPath: options?.migrationsPath ?? "database/migrations/omnify",
|
|
1315
1776
|
modelsPath: options?.modelsPath ?? "app/Models",
|
|
1316
1777
|
baseModelsPath: options?.baseModelsPath ?? "app/Models/OmnifyBase",
|
|
1317
1778
|
modelNamespace: options?.modelNamespace ?? "App\\Models",
|
|
1318
1779
|
baseModelNamespace: options?.baseModelNamespace ?? "App\\Models\\OmnifyBase",
|
|
1319
1780
|
generateModels: options?.generateModels ?? true,
|
|
1781
|
+
factoriesPath: options?.factoriesPath ?? "database/factories",
|
|
1782
|
+
generateFactories: options?.generateFactories ?? true,
|
|
1783
|
+
fakerLocale: options?.fakerLocale ?? "en_US",
|
|
1320
1784
|
connection: options?.connection,
|
|
1321
1785
|
timestamp: options?.timestamp
|
|
1322
1786
|
};
|
|
1323
1787
|
}
|
|
1324
1788
|
function laravelPlugin(options) {
|
|
1325
|
-
const resolved =
|
|
1789
|
+
const resolved = resolveOptions3(options);
|
|
1326
1790
|
const migrationGenerator = {
|
|
1327
1791
|
name: "laravel-migrations",
|
|
1328
1792
|
description: "Generate Laravel migration files",
|
|
@@ -1354,7 +1818,7 @@ function laravelPlugin(options) {
|
|
|
1354
1818
|
baseModelPath: resolved.baseModelsPath
|
|
1355
1819
|
};
|
|
1356
1820
|
const models = generateModels(ctx.schemas, modelOptions);
|
|
1357
|
-
|
|
1821
|
+
const outputs = models.map((model) => ({
|
|
1358
1822
|
path: getModelPath(model),
|
|
1359
1823
|
content: model.content,
|
|
1360
1824
|
type: "model",
|
|
@@ -1365,13 +1829,82 @@ function laravelPlugin(options) {
|
|
|
1365
1829
|
schemaName: model.schemaName
|
|
1366
1830
|
}
|
|
1367
1831
|
}));
|
|
1832
|
+
const bootstrapProvidersPath = (0, import_node_path.join)(ctx.cwd, "bootstrap/providers.php");
|
|
1833
|
+
const configAppPath = (0, import_node_path.join)(ctx.cwd, "config/app.php");
|
|
1834
|
+
let existingContent = null;
|
|
1835
|
+
let laravelVersion;
|
|
1836
|
+
if ((0, import_node_fs.existsSync)(bootstrapProvidersPath)) {
|
|
1837
|
+
laravelVersion = "laravel11+";
|
|
1838
|
+
try {
|
|
1839
|
+
existingContent = (0, import_node_fs.readFileSync)(bootstrapProvidersPath, "utf-8");
|
|
1840
|
+
} catch {
|
|
1841
|
+
existingContent = null;
|
|
1842
|
+
}
|
|
1843
|
+
} else if ((0, import_node_fs.existsSync)(configAppPath)) {
|
|
1844
|
+
laravelVersion = "laravel10-";
|
|
1845
|
+
try {
|
|
1846
|
+
existingContent = (0, import_node_fs.readFileSync)(configAppPath, "utf-8");
|
|
1847
|
+
} catch {
|
|
1848
|
+
existingContent = null;
|
|
1849
|
+
}
|
|
1850
|
+
} else {
|
|
1851
|
+
laravelVersion = "laravel11+";
|
|
1852
|
+
}
|
|
1853
|
+
const registration = generateProviderRegistration(existingContent, laravelVersion);
|
|
1854
|
+
if (registration && !registration.alreadyRegistered) {
|
|
1855
|
+
outputs.push({
|
|
1856
|
+
path: registration.path,
|
|
1857
|
+
content: registration.content,
|
|
1858
|
+
type: "other",
|
|
1859
|
+
skipIfExists: false,
|
|
1860
|
+
// We want to modify the file
|
|
1861
|
+
metadata: {
|
|
1862
|
+
registrationType: "provider-registration",
|
|
1863
|
+
laravelVersion: registration.laravelVersion
|
|
1864
|
+
}
|
|
1865
|
+
});
|
|
1866
|
+
ctx.logger.info(`OmnifyServiceProvider will be registered in ${registration.path}`);
|
|
1867
|
+
} else if (registration?.alreadyRegistered) {
|
|
1868
|
+
ctx.logger.info("OmnifyServiceProvider is already registered");
|
|
1869
|
+
}
|
|
1870
|
+
return outputs;
|
|
1871
|
+
}
|
|
1872
|
+
};
|
|
1873
|
+
const factoryGenerator = {
|
|
1874
|
+
name: "laravel-factories",
|
|
1875
|
+
description: "Generate Laravel factory classes for testing",
|
|
1876
|
+
generate: async (ctx) => {
|
|
1877
|
+
const factoryOptions = {
|
|
1878
|
+
modelNamespace: resolved.modelNamespace,
|
|
1879
|
+
factoryPath: resolved.factoriesPath,
|
|
1880
|
+
fakerLocale: resolved.fakerLocale
|
|
1881
|
+
};
|
|
1882
|
+
const factories = generateFactories(ctx.schemas, factoryOptions);
|
|
1883
|
+
return factories.map((factory) => ({
|
|
1884
|
+
path: getFactoryPath(factory),
|
|
1885
|
+
content: factory.content,
|
|
1886
|
+
type: "factory",
|
|
1887
|
+
// Skip writing factories if they already exist (allow customization)
|
|
1888
|
+
skipIfExists: !factory.overwrite,
|
|
1889
|
+
metadata: {
|
|
1890
|
+
factoryName: factory.name,
|
|
1891
|
+
schemaName: factory.schemaName
|
|
1892
|
+
}
|
|
1893
|
+
}));
|
|
1368
1894
|
}
|
|
1369
1895
|
};
|
|
1896
|
+
const generators = [migrationGenerator];
|
|
1897
|
+
if (resolved.generateModels) {
|
|
1898
|
+
generators.push(modelGenerator);
|
|
1899
|
+
}
|
|
1900
|
+
if (resolved.generateFactories) {
|
|
1901
|
+
generators.push(factoryGenerator);
|
|
1902
|
+
}
|
|
1370
1903
|
return {
|
|
1371
1904
|
name: "@famgia/omnify-laravel",
|
|
1372
1905
|
version: "0.0.14",
|
|
1373
1906
|
configSchema: LARAVEL_CONFIG_SCHEMA,
|
|
1374
|
-
generators
|
|
1907
|
+
generators
|
|
1375
1908
|
};
|
|
1376
1909
|
}
|
|
1377
1910
|
// Annotate the CommonJS export names for ESM import in node:
|