@famgia/omnify-laravel 0.0.6 → 0.0.8

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/index.cjs CHANGED
@@ -30,12 +30,15 @@ __export(index_exports, {
30
30
  formatMigrationFile: () => formatMigrationFile,
31
31
  formatProperty: () => formatProperty,
32
32
  formatTypeAlias: () => formatTypeAlias,
33
+ generateAlterMigration: () => generateAlterMigration,
33
34
  generateDropMigrationForTable: () => generateDropMigrationForTable,
35
+ generateDropTableMigration: () => generateDropTableMigration,
34
36
  generateEnums: () => generateEnums,
35
37
  generateForeignKey: () => generateForeignKey,
36
38
  generateInterfaces: () => generateInterfaces,
37
39
  generateMigrationFromSchema: () => generateMigrationFromSchema,
38
40
  generateMigrations: () => generateMigrations,
41
+ generateMigrationsFromChanges: () => generateMigrationsFromChanges,
39
42
  generatePrimaryKeyColumn: () => generatePrimaryKeyColumn,
40
43
  generateSoftDeleteColumn: () => generateSoftDeleteColumn,
41
44
  generateTimestampColumns: () => generateTimestampColumns,
@@ -225,7 +228,6 @@ function generateForeignKey(propertyName, property, allSchemas) {
225
228
  onUpdate: assocProp.onUpdate ?? "cascade"
226
229
  };
227
230
  const index = {
228
- name: `idx_${columnName}`,
229
231
  columns: [columnName],
230
232
  unique: false
231
233
  };
@@ -427,12 +429,10 @@ function generatePivotTableBlueprint(pivot) {
427
429
  unique: true
428
430
  });
429
431
  indexes.push({
430
- name: `idx_${pivot.sourceColumn}`,
431
432
  columns: [pivot.sourceColumn],
432
433
  unique: false
433
434
  });
434
435
  indexes.push({
435
- name: `idx_${pivot.targetColumn}`,
436
436
  columns: [pivot.targetColumn],
437
437
  unique: false
438
438
  });
@@ -583,14 +583,57 @@ return new class extends Migration
583
583
  type: "drop"
584
584
  };
585
585
  }
586
+ function extractDependencies(schema) {
587
+ const deps = [];
588
+ if (!schema.properties) {
589
+ return deps;
590
+ }
591
+ for (const property of Object.values(schema.properties)) {
592
+ if (property.type !== "Association") {
593
+ continue;
594
+ }
595
+ const assocProp = property;
596
+ if ((assocProp.relation === "ManyToOne" || assocProp.relation === "OneToOne") && !assocProp.mappedBy && assocProp.target) {
597
+ deps.push(assocProp.target);
598
+ }
599
+ }
600
+ return deps;
601
+ }
602
+ function topologicalSort(schemas) {
603
+ const schemaList = Object.values(schemas).filter((s) => s.kind !== "enum");
604
+ const sorted = [];
605
+ const visited = /* @__PURE__ */ new Set();
606
+ const visiting = /* @__PURE__ */ new Set();
607
+ function visit(schema) {
608
+ if (visited.has(schema.name)) {
609
+ return;
610
+ }
611
+ if (visiting.has(schema.name)) {
612
+ return;
613
+ }
614
+ visiting.add(schema.name);
615
+ const deps = extractDependencies(schema);
616
+ for (const depName of deps) {
617
+ const depSchema = schemas[depName];
618
+ if (depSchema && depSchema.kind !== "enum") {
619
+ visit(depSchema);
620
+ }
621
+ }
622
+ visiting.delete(schema.name);
623
+ visited.add(schema.name);
624
+ sorted.push(schema);
625
+ }
626
+ for (const schema of schemaList) {
627
+ visit(schema);
628
+ }
629
+ return sorted;
630
+ }
586
631
  function generateMigrations(schemas, options = {}) {
587
632
  const migrations = [];
588
633
  const pivotTablesGenerated = /* @__PURE__ */ new Set();
589
634
  let timestampOffset = 0;
590
- for (const schema of Object.values(schemas)) {
591
- if (schema.kind === "enum") {
592
- continue;
593
- }
635
+ const sortedSchemas = topologicalSort(schemas);
636
+ for (const schema of sortedSchemas) {
594
637
  const timestamp = options.timestamp ?? generateTimestamp();
595
638
  const offsetTimestamp = incrementTimestamp(timestamp, timestampOffset);
596
639
  timestampOffset++;
@@ -601,10 +644,7 @@ function generateMigrations(schemas, options = {}) {
601
644
  });
602
645
  migrations.push(migration);
603
646
  }
604
- for (const schema of Object.values(schemas)) {
605
- if (schema.kind === "enum") {
606
- continue;
607
- }
647
+ for (const schema of sortedSchemas) {
608
648
  const pivotTables = extractManyToManyRelations(schema, schemas);
609
649
  for (const pivot of pivotTables) {
610
650
  if (pivotTablesGenerated.has(pivot.tableName)) {
@@ -663,6 +703,272 @@ function getMigrationPath(migration, outputDir = "database/migrations") {
663
703
  return `${outputDir}/${migration.fileName}`;
664
704
  }
665
705
 
706
+ // src/migration/alter-generator.ts
707
+ var TYPE_METHOD_MAP2 = {
708
+ String: "string",
709
+ Int: "integer",
710
+ BigInt: "bigInteger",
711
+ Float: "double",
712
+ Boolean: "boolean",
713
+ Text: "text",
714
+ LongText: "longText",
715
+ Date: "date",
716
+ Time: "time",
717
+ Timestamp: "timestamp",
718
+ Json: "json",
719
+ Email: "string",
720
+ Password: "string",
721
+ File: "string",
722
+ MultiFile: "json",
723
+ Enum: "enum",
724
+ Select: "string",
725
+ Lookup: "unsignedBigInteger"
726
+ };
727
+ function generateTimestamp2() {
728
+ const now = /* @__PURE__ */ new Date();
729
+ const year = now.getFullYear();
730
+ const month = String(now.getMonth() + 1).padStart(2, "0");
731
+ const day = String(now.getDate()).padStart(2, "0");
732
+ const hours = String(now.getHours()).padStart(2, "0");
733
+ const minutes = String(now.getMinutes()).padStart(2, "0");
734
+ const seconds = String(now.getSeconds()).padStart(2, "0");
735
+ return `${year}_${month}_${day}_${hours}${minutes}${seconds}`;
736
+ }
737
+ function formatAddColumn(columnName, prop) {
738
+ const snakeColumn = toColumnName(columnName);
739
+ const method = TYPE_METHOD_MAP2[prop.type] ?? "string";
740
+ let code = `$table->${method}('${snakeColumn}')`;
741
+ if (prop.nullable) code += "->nullable()";
742
+ if (prop.unique) code += "->unique()";
743
+ if (prop.default !== void 0) {
744
+ const defaultValue = typeof prop.default === "string" ? `'${prop.default}'` : JSON.stringify(prop.default);
745
+ code += `->default(${defaultValue})`;
746
+ }
747
+ return code + ";";
748
+ }
749
+ function formatDropColumn(columnName) {
750
+ const snakeColumn = toColumnName(columnName);
751
+ return `$table->dropColumn('${snakeColumn}');`;
752
+ }
753
+ function formatRenameColumn(oldName, newName) {
754
+ const oldSnake = toColumnName(oldName);
755
+ const newSnake = toColumnName(newName);
756
+ return `$table->renameColumn('${oldSnake}', '${newSnake}');`;
757
+ }
758
+ function formatModifyColumn(columnName, _prevProp, currProp) {
759
+ const snakeColumn = toColumnName(columnName);
760
+ const method = TYPE_METHOD_MAP2[currProp.type] ?? "string";
761
+ let code = `$table->${method}('${snakeColumn}')`;
762
+ if (currProp.nullable) code += "->nullable()";
763
+ if (currProp.unique) code += "->unique()";
764
+ if (currProp.default !== void 0) {
765
+ const defaultValue = typeof currProp.default === "string" ? `'${currProp.default}'` : JSON.stringify(currProp.default);
766
+ code += `->default(${defaultValue})`;
767
+ }
768
+ return code + "->change();";
769
+ }
770
+ function formatAddIndex(columns, unique) {
771
+ const snakeColumns = columns.map(toColumnName);
772
+ const method = unique ? "unique" : "index";
773
+ const colsArg = snakeColumns.length === 1 ? `'${snakeColumns[0]}'` : `[${snakeColumns.map((c) => `'${c}'`).join(", ")}]`;
774
+ return `$table->${method}(${colsArg});`;
775
+ }
776
+ function formatDropIndex(tableName, columns, unique) {
777
+ const snakeColumns = columns.map(toColumnName);
778
+ const method = unique ? "dropUnique" : "dropIndex";
779
+ const suffix = unique ? "unique" : "index";
780
+ const indexName = `${tableName}_${snakeColumns.join("_")}_${suffix}`;
781
+ return `$table->${method}('${indexName}');`;
782
+ }
783
+ function generateAlterMigrationContent(tableName, change, options = {}) {
784
+ const upLines = [];
785
+ const downLines = [];
786
+ if (change.columnChanges) {
787
+ for (const col of change.columnChanges) {
788
+ if (col.changeType === "added" && col.currentDef) {
789
+ upLines.push(` ${formatAddColumn(col.column, col.currentDef)}`);
790
+ downLines.push(` ${formatDropColumn(col.column)}`);
791
+ } else if (col.changeType === "removed" && col.previousDef) {
792
+ upLines.push(` ${formatDropColumn(col.column)}`);
793
+ downLines.push(` ${formatAddColumn(col.column, col.previousDef)}`);
794
+ } else if (col.changeType === "modified" && col.previousDef && col.currentDef) {
795
+ upLines.push(` ${formatModifyColumn(col.column, col.previousDef, col.currentDef)}`);
796
+ downLines.push(` ${formatModifyColumn(col.column, col.currentDef, col.previousDef)}`);
797
+ } else if (col.changeType === "renamed" && col.previousColumn) {
798
+ upLines.push(` ${formatRenameColumn(col.previousColumn, col.column)}`);
799
+ downLines.push(` ${formatRenameColumn(col.column, col.previousColumn)}`);
800
+ if (col.modifications && col.modifications.length > 0 && col.previousDef && col.currentDef) {
801
+ upLines.push(` ${formatModifyColumn(col.column, col.previousDef, col.currentDef)}`);
802
+ downLines.push(` ${formatModifyColumn(col.column, col.currentDef, col.previousDef)}`);
803
+ }
804
+ }
805
+ }
806
+ }
807
+ if (change.indexChanges) {
808
+ for (const idx of change.indexChanges) {
809
+ if (idx.changeType === "added") {
810
+ upLines.push(` ${formatAddIndex(idx.index.columns, idx.index.unique)}`);
811
+ downLines.push(` ${formatDropIndex(tableName, idx.index.columns, idx.index.unique)}`);
812
+ } else {
813
+ upLines.push(` ${formatDropIndex(tableName, idx.index.columns, idx.index.unique)}`);
814
+ downLines.push(` ${formatAddIndex(idx.index.columns, idx.index.unique)}`);
815
+ }
816
+ }
817
+ }
818
+ if (change.optionChanges) {
819
+ if (change.optionChanges.timestamps) {
820
+ const { from, to } = change.optionChanges.timestamps;
821
+ if (to && !from) {
822
+ upLines.push(` $table->timestamps();`);
823
+ downLines.push(` $table->dropTimestamps();`);
824
+ } else if (from && !to) {
825
+ upLines.push(` $table->dropTimestamps();`);
826
+ downLines.push(` $table->timestamps();`);
827
+ }
828
+ }
829
+ if (change.optionChanges.softDelete) {
830
+ const { from, to } = change.optionChanges.softDelete;
831
+ if (to && !from) {
832
+ upLines.push(` $table->softDeletes();`);
833
+ downLines.push(` $table->dropSoftDeletes();`);
834
+ } else if (from && !to) {
835
+ upLines.push(` $table->dropSoftDeletes();`);
836
+ downLines.push(` $table->softDeletes();`);
837
+ }
838
+ }
839
+ }
840
+ const connection = options.connection ? `
841
+ protected $connection = '${options.connection}';
842
+ ` : "";
843
+ return `<?php
844
+
845
+ use Illuminate\\Database\\Migrations\\Migration;
846
+ use Illuminate\\Database\\Schema\\Blueprint;
847
+ use Illuminate\\Support\\Facades\\Schema;
848
+
849
+ return new class extends Migration
850
+ {${connection}
851
+ /**
852
+ * Run the migrations.
853
+ */
854
+ public function up(): void
855
+ {
856
+ Schema::table('${tableName}', function (Blueprint $table) {
857
+ ${upLines.join("\n")}
858
+ });
859
+ }
860
+
861
+ /**
862
+ * Reverse the migrations.
863
+ */
864
+ public function down(): void
865
+ {
866
+ Schema::table('${tableName}', function (Blueprint $table) {
867
+ ${downLines.join("\n")}
868
+ });
869
+ }
870
+ };
871
+ `;
872
+ }
873
+ function generateAlterMigration(change, options = {}) {
874
+ if (change.changeType !== "modified") {
875
+ return null;
876
+ }
877
+ const hasChanges = change.columnChanges && change.columnChanges.length > 0 || change.indexChanges && change.indexChanges.length > 0 || change.optionChanges && (change.optionChanges.timestamps || change.optionChanges.softDelete);
878
+ if (!hasChanges) {
879
+ return null;
880
+ }
881
+ const tableName = toTableName(change.schemaName);
882
+ const timestamp = options.timestamp ?? generateTimestamp2();
883
+ const fileName = `${timestamp}_update_${tableName}_table.php`;
884
+ const content = generateAlterMigrationContent(tableName, change, options);
885
+ return {
886
+ fileName,
887
+ className: `Update${change.schemaName}Table`,
888
+ content,
889
+ tables: [tableName],
890
+ type: "alter"
891
+ };
892
+ }
893
+ function generateDropTableMigration(schemaName, options = {}) {
894
+ const tableName = toTableName(schemaName);
895
+ const timestamp = options.timestamp ?? generateTimestamp2();
896
+ const fileName = `${timestamp}_drop_${tableName}_table.php`;
897
+ const connection = options.connection ? `
898
+ protected $connection = '${options.connection}';
899
+ ` : "";
900
+ const content = `<?php
901
+
902
+ use Illuminate\\Database\\Migrations\\Migration;
903
+ use Illuminate\\Database\\Schema\\Blueprint;
904
+ use Illuminate\\Support\\Facades\\Schema;
905
+
906
+ return new class extends Migration
907
+ {${connection}
908
+ /**
909
+ * Run the migrations.
910
+ */
911
+ public function up(): void
912
+ {
913
+ Schema::dropIfExists('${tableName}');
914
+ }
915
+
916
+ /**
917
+ * Reverse the migrations.
918
+ */
919
+ public function down(): void
920
+ {
921
+ // Cannot recreate table without full schema
922
+ // Consider restoring from backup if needed
923
+ }
924
+ };
925
+ `;
926
+ return {
927
+ fileName,
928
+ className: `Drop${schemaName}Table`,
929
+ content,
930
+ tables: [tableName],
931
+ type: "drop"
932
+ };
933
+ }
934
+ function generateMigrationsFromChanges(changes, options = {}) {
935
+ const migrations = [];
936
+ let timestampOffset = 0;
937
+ const getNextTimestamp = () => {
938
+ const ts = options.timestamp ?? generateTimestamp2();
939
+ const offset = timestampOffset++;
940
+ if (offset === 0) return ts;
941
+ const parts = ts.split("_");
942
+ if (parts.length >= 4) {
943
+ const timePart = parts[3] ?? "000000";
944
+ const secs = parseInt(timePart.substring(4, 6), 10) + offset;
945
+ const newSecs = String(secs % 60).padStart(2, "0");
946
+ parts[3] = timePart.substring(0, 4) + newSecs;
947
+ return parts.join("_");
948
+ }
949
+ return ts;
950
+ };
951
+ for (const change of changes) {
952
+ if (change.changeType === "modified") {
953
+ const migration = generateAlterMigration(change, {
954
+ ...options,
955
+ timestamp: getNextTimestamp()
956
+ });
957
+ if (migration) {
958
+ migrations.push(migration);
959
+ }
960
+ } else if (change.changeType === "removed") {
961
+ migrations.push(
962
+ generateDropTableMigration(change.schemaName, {
963
+ ...options,
964
+ timestamp: getNextTimestamp()
965
+ })
966
+ );
967
+ }
968
+ }
969
+ return migrations;
970
+ }
971
+
666
972
  // src/typescript/interface-generator.ts
667
973
  var TYPE_MAP = {
668
974
  String: "string",
@@ -1076,7 +1382,7 @@ function laravelPlugin(options) {
1076
1382
  const resolved = resolveOptions(options);
1077
1383
  return {
1078
1384
  name: "@famgia/omnify-laravel",
1079
- version: "0.0.6",
1385
+ version: "0.0.7",
1080
1386
  generators: [
1081
1387
  // Laravel Migrations Generator
1082
1388
  ...resolved.generateMigrations ? [
@@ -1137,12 +1443,15 @@ function laravelPlugin(options) {
1137
1443
  formatMigrationFile,
1138
1444
  formatProperty,
1139
1445
  formatTypeAlias,
1446
+ generateAlterMigration,
1140
1447
  generateDropMigrationForTable,
1448
+ generateDropTableMigration,
1141
1449
  generateEnums,
1142
1450
  generateForeignKey,
1143
1451
  generateInterfaces,
1144
1452
  generateMigrationFromSchema,
1145
1453
  generateMigrations,
1454
+ generateMigrationsFromChanges,
1146
1455
  generatePrimaryKeyColumn,
1147
1456
  generateSoftDeleteColumn,
1148
1457
  generateTimestampColumns,