@famgia/omnify-laravel 2.0.21 → 2.0.22
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-U3NDJ6S6.js → chunk-RSC5ATSV.js} +201 -9
- package/dist/chunk-RSC5ATSV.js.map +1 -0
- package/dist/index.cjs +200 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +1 -1
- package/dist/plugin.cjs +200 -8
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.js +1 -1
- package/package.json +4 -4
- package/dist/chunk-U3NDJ6S6.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -483,7 +483,8 @@ function expandCompoundType(propName, property, customTypes, options = {}) {
|
|
|
483
483
|
return expanded;
|
|
484
484
|
}
|
|
485
485
|
function schemaToBlueprint(schema, allSchemas, options = {}) {
|
|
486
|
-
const { customTypes = /* @__PURE__ */ new Map(), pluginEnums = /* @__PURE__ */ new Map(), locale } = options;
|
|
486
|
+
const { customTypes = /* @__PURE__ */ new Map(), pluginEnums = /* @__PURE__ */ new Map(), locale, excludeFKs } = options;
|
|
487
|
+
const schemaName = options.schemaName ?? schema.name;
|
|
487
488
|
const compoundOptions = { locale, pluginEnums };
|
|
488
489
|
const tableName = schema.options?.tableName ?? toTableName(schema.name);
|
|
489
490
|
const columns = [];
|
|
@@ -553,7 +554,10 @@ function schemaToBlueprint(schema, allSchemas, options = {}) {
|
|
|
553
554
|
if (!explicitColumnNames.has(fkResult.column.name)) {
|
|
554
555
|
columns.push(fkResult.column);
|
|
555
556
|
}
|
|
556
|
-
|
|
557
|
+
const fkKey = `${schemaName}.${propName}`;
|
|
558
|
+
if (!excludeFKs?.has(fkKey)) {
|
|
559
|
+
foreignKeys.push(fkResult.foreignKey);
|
|
560
|
+
}
|
|
557
561
|
indexes.push(fkResult.index);
|
|
558
562
|
}
|
|
559
563
|
const polyResult = generatePolymorphicColumns(propName, property, allSchemas);
|
|
@@ -1220,6 +1224,143 @@ function extractDependencies(schema) {
|
|
|
1220
1224
|
}
|
|
1221
1225
|
return deps;
|
|
1222
1226
|
}
|
|
1227
|
+
function detectCircularDependencies(schemas) {
|
|
1228
|
+
const circularFKs = /* @__PURE__ */ new Set();
|
|
1229
|
+
const graph = /* @__PURE__ */ new Map();
|
|
1230
|
+
for (const schema of Object.values(schemas)) {
|
|
1231
|
+
if (schema.kind === "enum" || !schema.properties) continue;
|
|
1232
|
+
const refs = /* @__PURE__ */ new Map();
|
|
1233
|
+
for (const [propName, property] of Object.entries(schema.properties)) {
|
|
1234
|
+
if (property.type !== "Association") continue;
|
|
1235
|
+
const assocProp = property;
|
|
1236
|
+
if ((assocProp.relation === "ManyToOne" || assocProp.relation === "OneToOne") && !assocProp.mappedBy && assocProp.target && assocProp.target !== schema.name) {
|
|
1237
|
+
refs.set(assocProp.target, propName);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
if (refs.size > 0) {
|
|
1241
|
+
graph.set(schema.name, refs);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
for (const [schemaA, refsA] of graph) {
|
|
1245
|
+
for (const [targetB, propNameA] of refsA) {
|
|
1246
|
+
if (targetB === schemaA) continue;
|
|
1247
|
+
const refsB = graph.get(targetB);
|
|
1248
|
+
if (refsB && refsB.has(schemaA)) {
|
|
1249
|
+
if (schemaA > targetB) {
|
|
1250
|
+
circularFKs.add(`${schemaA}.${propNameA}`);
|
|
1251
|
+
} else {
|
|
1252
|
+
const propNameB = refsB.get(schemaA);
|
|
1253
|
+
circularFKs.add(`${targetB}.${propNameB}`);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
return circularFKs;
|
|
1259
|
+
}
|
|
1260
|
+
function extractDeferredFKs(schemas, circularFKs) {
|
|
1261
|
+
const deferred = [];
|
|
1262
|
+
for (const schema of Object.values(schemas)) {
|
|
1263
|
+
if (schema.kind === "enum" || !schema.properties) continue;
|
|
1264
|
+
const tableName = schema.options?.tableName ?? toTableName(schema.name);
|
|
1265
|
+
for (const [propName, property] of Object.entries(schema.properties)) {
|
|
1266
|
+
const key = `${schema.name}.${propName}`;
|
|
1267
|
+
if (!circularFKs.has(key)) continue;
|
|
1268
|
+
const assocProp = property;
|
|
1269
|
+
if (!assocProp.target) continue;
|
|
1270
|
+
const targetSchema = schemas[assocProp.target];
|
|
1271
|
+
const targetTable = targetSchema?.options?.tableName ?? toTableName(assocProp.target);
|
|
1272
|
+
const columnName = toColumnName(propName) + "_id";
|
|
1273
|
+
deferred.push({
|
|
1274
|
+
tableName,
|
|
1275
|
+
columnName,
|
|
1276
|
+
targetTable,
|
|
1277
|
+
onDelete: assocProp.onDelete,
|
|
1278
|
+
onUpdate: assocProp.onUpdate
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
return deferred;
|
|
1283
|
+
}
|
|
1284
|
+
function generateDeferredFKMigration(deferredFKs, options = {}) {
|
|
1285
|
+
if (deferredFKs.length === 0) return null;
|
|
1286
|
+
const timestamp = options.timestamp ?? generateTimestamp();
|
|
1287
|
+
const fileName = `${timestamp}_add_circular_foreign_keys.php`;
|
|
1288
|
+
const connection = options.connection ? `
|
|
1289
|
+
protected $connection = '${options.connection}';
|
|
1290
|
+
` : "";
|
|
1291
|
+
const byTable = /* @__PURE__ */ new Map();
|
|
1292
|
+
for (const fk of deferredFKs) {
|
|
1293
|
+
const existing = byTable.get(fk.tableName) ?? [];
|
|
1294
|
+
existing.push(fk);
|
|
1295
|
+
byTable.set(fk.tableName, existing);
|
|
1296
|
+
}
|
|
1297
|
+
const upStatements = [];
|
|
1298
|
+
const downStatements = [];
|
|
1299
|
+
for (const [tableName, fks] of byTable) {
|
|
1300
|
+
const fkLines = fks.map((fk) => {
|
|
1301
|
+
let line = ` $table->foreign('${fk.columnName}')->references('id')->on('${fk.targetTable}')`;
|
|
1302
|
+
if (fk.onDelete) line += `->onDelete('${fk.onDelete}')`;
|
|
1303
|
+
if (fk.onUpdate) line += `->onUpdate('${fk.onUpdate}')`;
|
|
1304
|
+
return line + ";";
|
|
1305
|
+
});
|
|
1306
|
+
upStatements.push(` Schema::table('${tableName}', function (Blueprint $table) {
|
|
1307
|
+
${fkLines.join("\n")}
|
|
1308
|
+
});`);
|
|
1309
|
+
const dropLines = fks.map(
|
|
1310
|
+
(fk) => ` $table->dropForeign(['${fk.columnName}']);`
|
|
1311
|
+
);
|
|
1312
|
+
downStatements.push(` Schema::table('${tableName}', function (Blueprint $table) {
|
|
1313
|
+
${dropLines.join("\n")}
|
|
1314
|
+
});`);
|
|
1315
|
+
}
|
|
1316
|
+
const content = `<?php
|
|
1317
|
+
|
|
1318
|
+
/**
|
|
1319
|
+
* \u26A0\uFE0F DO NOT EDIT THIS FILE! \u26A0\uFE0F
|
|
1320
|
+
* \u3053\u306E\u30D5\u30A1\u30A4\u30EB\u3092\u7DE8\u96C6\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\uFF01
|
|
1321
|
+
* KH\xD4NG \u0110\u01AF\u1EE2C S\u1EECA FILE N\xC0Y!
|
|
1322
|
+
*
|
|
1323
|
+
* This file is AUTO-GENERATED by Omnify.
|
|
1324
|
+
* Any manual changes will be OVERWRITTEN on next generation.
|
|
1325
|
+
*
|
|
1326
|
+
* This migration adds foreign key constraints for circular dependencies.
|
|
1327
|
+
* These constraints are deferred to run after all tables are created.
|
|
1328
|
+
*
|
|
1329
|
+
* @generated by @famgia/omnify-laravel
|
|
1330
|
+
*/
|
|
1331
|
+
|
|
1332
|
+
use Illuminate\\Database\\Migrations\\Migration;
|
|
1333
|
+
use Illuminate\\Database\\Schema\\Blueprint;
|
|
1334
|
+
use Illuminate\\Support\\Facades\\Schema;
|
|
1335
|
+
|
|
1336
|
+
return new class extends Migration
|
|
1337
|
+
{${connection}
|
|
1338
|
+
/**
|
|
1339
|
+
* Run the migrations.
|
|
1340
|
+
*/
|
|
1341
|
+
public function up(): void
|
|
1342
|
+
{
|
|
1343
|
+
${upStatements.join("\n\n")}
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
/**
|
|
1347
|
+
* Reverse the migrations.
|
|
1348
|
+
*/
|
|
1349
|
+
public function down(): void
|
|
1350
|
+
{
|
|
1351
|
+
${downStatements.join("\n\n")}
|
|
1352
|
+
}
|
|
1353
|
+
};
|
|
1354
|
+
`;
|
|
1355
|
+
const tables = [...byTable.keys()];
|
|
1356
|
+
return {
|
|
1357
|
+
fileName,
|
|
1358
|
+
className: "AddCircularForeignKeys",
|
|
1359
|
+
content,
|
|
1360
|
+
tables,
|
|
1361
|
+
type: "alter"
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1223
1364
|
function topologicalSort(schemas) {
|
|
1224
1365
|
const schemaList = Object.values(schemas).filter((s) => s.kind !== "enum");
|
|
1225
1366
|
const sorted = [];
|
|
@@ -1253,6 +1394,7 @@ function generateMigrations(schemas, options = {}) {
|
|
|
1253
1394
|
const migrations = [];
|
|
1254
1395
|
const pivotTablesGenerated = /* @__PURE__ */ new Set();
|
|
1255
1396
|
let timestampOffset = 0;
|
|
1397
|
+
const circularFKs = detectCircularDependencies(schemas);
|
|
1256
1398
|
const sortedSchemas = topologicalSort(schemas);
|
|
1257
1399
|
const baseTimestamp = options.timestamp ?? generateTimestamp();
|
|
1258
1400
|
for (const schema of sortedSchemas) {
|
|
@@ -1261,7 +1403,9 @@ function generateMigrations(schemas, options = {}) {
|
|
|
1261
1403
|
const blueprint = schemaToBlueprint(schema, schemas, {
|
|
1262
1404
|
customTypes: options.customTypes,
|
|
1263
1405
|
pluginEnums: options.pluginEnums,
|
|
1264
|
-
locale: options.locale
|
|
1406
|
+
locale: options.locale,
|
|
1407
|
+
excludeFKs: circularFKs,
|
|
1408
|
+
schemaName: schema.name
|
|
1265
1409
|
});
|
|
1266
1410
|
const migration = generateCreateMigration(blueprint, {
|
|
1267
1411
|
...options,
|
|
@@ -1301,6 +1445,19 @@ function generateMigrations(schemas, options = {}) {
|
|
|
1301
1445
|
});
|
|
1302
1446
|
}
|
|
1303
1447
|
}
|
|
1448
|
+
if (circularFKs.size > 0) {
|
|
1449
|
+
const deferredFKs = extractDeferredFKs(schemas, circularFKs);
|
|
1450
|
+
if (deferredFKs.length > 0) {
|
|
1451
|
+
const offsetTimestamp = incrementTimestamp(baseTimestamp, timestampOffset);
|
|
1452
|
+
const deferredMigration = generateDeferredFKMigration(deferredFKs, {
|
|
1453
|
+
...options,
|
|
1454
|
+
timestamp: offsetTimestamp
|
|
1455
|
+
});
|
|
1456
|
+
if (deferredMigration) {
|
|
1457
|
+
migrations.push(deferredMigration);
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1304
1461
|
return migrations;
|
|
1305
1462
|
}
|
|
1306
1463
|
function incrementTimestamp(timestamp, seconds) {
|
|
@@ -1390,8 +1547,16 @@ function generateTimestamp2() {
|
|
|
1390
1547
|
const seconds = String(now.getSeconds()).padStart(2, "0");
|
|
1391
1548
|
return `${year}_${month}_${day}_${hours}${minutes}${seconds}`;
|
|
1392
1549
|
}
|
|
1550
|
+
function shouldSkipAssociationColumn(prop) {
|
|
1551
|
+
if (prop.type !== "Association") return false;
|
|
1552
|
+
if (isAssociationWithFkColumn(prop)) return false;
|
|
1553
|
+
return true;
|
|
1554
|
+
}
|
|
1393
1555
|
function formatAddColumn(columnName, prop) {
|
|
1394
1556
|
const lines = [];
|
|
1557
|
+
if (shouldSkipAssociationColumn(prop)) {
|
|
1558
|
+
return lines;
|
|
1559
|
+
}
|
|
1395
1560
|
if (isAssociationWithFkColumn(prop)) {
|
|
1396
1561
|
const fkColumn = getAssociationFkColumnName(columnName);
|
|
1397
1562
|
let code2 = `$table->unsignedBigInteger('${fkColumn}')`;
|
|
@@ -1431,6 +1596,9 @@ function formatAddColumn(columnName, prop) {
|
|
|
1431
1596
|
}
|
|
1432
1597
|
function formatDropColumn(columnName, prop) {
|
|
1433
1598
|
const lines = [];
|
|
1599
|
+
if (prop && shouldSkipAssociationColumn(prop)) {
|
|
1600
|
+
return lines;
|
|
1601
|
+
}
|
|
1434
1602
|
if (prop && isAssociationWithFkColumn(prop)) {
|
|
1435
1603
|
const fkColumn = getAssociationFkColumnName(columnName);
|
|
1436
1604
|
lines.push(`$table->dropForeign(['${fkColumn}']);`);
|
|
@@ -3080,9 +3248,26 @@ ${providerLine}
|
|
|
3080
3248
|
}
|
|
3081
3249
|
|
|
3082
3250
|
// src/factory/generator.ts
|
|
3251
|
+
function deriveFactoryNamespace(modelNamespace) {
|
|
3252
|
+
if (modelNamespace === "App\\Models") {
|
|
3253
|
+
return "Database\\Factories";
|
|
3254
|
+
}
|
|
3255
|
+
if (modelNamespace.endsWith("\\Models")) {
|
|
3256
|
+
const base = modelNamespace.slice(0, -7);
|
|
3257
|
+
return `${base}\\Database\\Factories`;
|
|
3258
|
+
}
|
|
3259
|
+
const parts = modelNamespace.split("\\");
|
|
3260
|
+
if (parts.length > 1 && parts[parts.length - 1] === "Models") {
|
|
3261
|
+
parts.pop();
|
|
3262
|
+
return [...parts, "Database", "Factories"].join("\\");
|
|
3263
|
+
}
|
|
3264
|
+
return "Database\\Factories";
|
|
3265
|
+
}
|
|
3083
3266
|
function resolveOptions2(options) {
|
|
3267
|
+
const modelNamespace = options?.modelNamespace ?? "App\\Models";
|
|
3084
3268
|
return {
|
|
3085
|
-
modelNamespace
|
|
3269
|
+
modelNamespace,
|
|
3270
|
+
factoryNamespace: options?.factoryNamespace ?? deriveFactoryNamespace(modelNamespace),
|
|
3086
3271
|
factoryPath: options?.factoryPath ?? "database/factories",
|
|
3087
3272
|
fakerLocale: options?.fakerLocale ?? "en_US",
|
|
3088
3273
|
customTypes: options?.customTypes ?? /* @__PURE__ */ new Map(),
|
|
@@ -3095,16 +3280,18 @@ function resolveSchemaOptions2(schema, globalOptions) {
|
|
|
3095
3280
|
return globalOptions;
|
|
3096
3281
|
}
|
|
3097
3282
|
const base = pkgOutput.base;
|
|
3283
|
+
const modelNamespace = pkgOutput.modelsNamespace;
|
|
3098
3284
|
return {
|
|
3099
3285
|
...globalOptions,
|
|
3100
|
-
modelNamespace
|
|
3286
|
+
modelNamespace,
|
|
3287
|
+
factoryNamespace: pkgOutput.factoriesNamespace ?? deriveFactoryNamespace(modelNamespace),
|
|
3101
3288
|
factoryPath: `${base}/${pkgOutput.factoriesPath ?? "database/factories"}`
|
|
3102
3289
|
};
|
|
3103
3290
|
}
|
|
3104
3291
|
function getStubContent2() {
|
|
3105
3292
|
return `<?php
|
|
3106
3293
|
|
|
3107
|
-
namespace
|
|
3294
|
+
namespace {{FACTORY_NAMESPACE}};
|
|
3108
3295
|
|
|
3109
3296
|
use {{MODEL_NAMESPACE}}\\{{MODEL_NAME}};
|
|
3110
3297
|
use Illuminate\\Database\\Eloquent\\Factories\\Factory;
|
|
@@ -3401,6 +3588,7 @@ function generateFactory(schema, schemas, options, stubContent) {
|
|
|
3401
3588
|
}
|
|
3402
3589
|
}
|
|
3403
3590
|
let content = stubContent;
|
|
3591
|
+
content = content.replace(/\{\{FACTORY_NAMESPACE\}\}/g, options.factoryNamespace);
|
|
3404
3592
|
content = content.replace(/\{\{MODEL_NAMESPACE\}\}/g, options.modelNamespace);
|
|
3405
3593
|
content = content.replace(/\{\{MODEL_NAME\}\}/g, modelName);
|
|
3406
3594
|
const uniqueImports = [...new Set(imports)];
|
|
@@ -5026,9 +5214,10 @@ function laravelPlugin(options) {
|
|
|
5026
5214
|
name: "laravel-migrations",
|
|
5027
5215
|
description: "Generate Laravel migration files",
|
|
5028
5216
|
generate: async (ctx) => {
|
|
5217
|
+
const baseTimestamp = resolved.timestamp ?? generateTimestamp();
|
|
5029
5218
|
const migrationOptions = {
|
|
5030
5219
|
connection: resolved.connection,
|
|
5031
|
-
timestamp:
|
|
5220
|
+
timestamp: baseTimestamp,
|
|
5032
5221
|
customTypes: ctx.customTypes,
|
|
5033
5222
|
pluginEnums: ctx.pluginEnums
|
|
5034
5223
|
};
|
|
@@ -5050,6 +5239,7 @@ function laravelPlugin(options) {
|
|
|
5050
5239
|
if (ctx.changes.length === 0) {
|
|
5051
5240
|
return outputs;
|
|
5052
5241
|
}
|
|
5242
|
+
let timestampOffset = 0;
|
|
5053
5243
|
const addedSchemaNames = new Set(
|
|
5054
5244
|
ctx.changes.filter((c) => c.changeType === "added").map((c) => c.schemaName)
|
|
5055
5245
|
);
|
|
@@ -5074,15 +5264,17 @@ function laravelPlugin(options) {
|
|
|
5074
5264
|
schemaName: migration.schemaName
|
|
5075
5265
|
}
|
|
5076
5266
|
});
|
|
5267
|
+
timestampOffset++;
|
|
5077
5268
|
}
|
|
5078
5269
|
}
|
|
5079
5270
|
const alterChanges = ctx.changes.filter(
|
|
5080
5271
|
(c) => c.changeType === "modified" || c.changeType === "removed"
|
|
5081
5272
|
);
|
|
5082
5273
|
if (alterChanges.length > 0) {
|
|
5274
|
+
const alterTimestamp = timestampOffset > 0 ? incrementTimestamp(baseTimestamp, timestampOffset) : baseTimestamp;
|
|
5083
5275
|
const alterMigrations = generateMigrationsFromChanges(
|
|
5084
5276
|
alterChanges,
|
|
5085
|
-
migrationOptions
|
|
5277
|
+
{ ...migrationOptions, timestamp: alterTimestamp }
|
|
5086
5278
|
);
|
|
5087
5279
|
for (const migration of alterMigrations) {
|
|
5088
5280
|
outputs.push({
|