@famgia/omnify-laravel 0.0.13 → 0.0.14

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/plugin.cjs CHANGED
@@ -293,11 +293,18 @@ function schemaToBlueprint(schema, allSchemas) {
293
293
  }
294
294
  if (schema.options?.indexes) {
295
295
  for (const index of schema.options.indexes) {
296
- indexes.push({
297
- name: index.name,
298
- columns: index.columns.map(toColumnName),
299
- unique: index.unique ?? false
300
- });
296
+ if (typeof index === "string") {
297
+ indexes.push({
298
+ columns: [toColumnName(index)],
299
+ unique: false
300
+ });
301
+ } else {
302
+ indexes.push({
303
+ name: index.name,
304
+ columns: index.columns.map(toColumnName),
305
+ unique: index.unique ?? false
306
+ });
307
+ }
301
308
  }
302
309
  }
303
310
  if (schema.options?.unique) {
@@ -684,6 +691,579 @@ function getMigrationPath(migration, outputDir = "database/migrations") {
684
691
  return `${outputDir}/${migration.fileName}`;
685
692
  }
686
693
 
694
+ // src/utils.ts
695
+ function toSnakeCase(str) {
696
+ return str.replace(/([A-Z])/g, "_$1").replace(/^_/, "").toLowerCase();
697
+ }
698
+ function toPascalCase(str) {
699
+ return str.replace(/[-_](.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, (_, c) => c.toUpperCase());
700
+ }
701
+ function toCamelCase(str) {
702
+ const pascal = toPascalCase(str);
703
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
704
+ }
705
+ function pluralize(word) {
706
+ if (word.endsWith("y") && !["ay", "ey", "iy", "oy", "uy"].some((v) => word.endsWith(v))) {
707
+ return word.slice(0, -1) + "ies";
708
+ }
709
+ if (word.endsWith("s") || word.endsWith("x") || word.endsWith("z") || word.endsWith("ch") || word.endsWith("sh")) {
710
+ return word + "es";
711
+ }
712
+ return word + "s";
713
+ }
714
+
715
+ // src/model/generator.ts
716
+ var DEFAULT_OPTIONS = {
717
+ baseModelNamespace: "App\\Models\\OmnifyBase",
718
+ modelNamespace: "App\\Models",
719
+ baseModelClassName: "BaseModel",
720
+ baseModelPath: "app/Models/OmnifyBase",
721
+ modelPath: "app/Models"
722
+ };
723
+ function resolveOptions(options) {
724
+ return {
725
+ baseModelNamespace: options?.baseModelNamespace ?? DEFAULT_OPTIONS.baseModelNamespace,
726
+ modelNamespace: options?.modelNamespace ?? DEFAULT_OPTIONS.modelNamespace,
727
+ baseModelClassName: options?.baseModelClassName ?? DEFAULT_OPTIONS.baseModelClassName,
728
+ baseModelPath: options?.baseModelPath ?? DEFAULT_OPTIONS.baseModelPath,
729
+ modelPath: options?.modelPath ?? DEFAULT_OPTIONS.modelPath
730
+ };
731
+ }
732
+ function getCastType(propDef) {
733
+ switch (propDef.type) {
734
+ case "Boolean":
735
+ return "boolean";
736
+ case "Int":
737
+ case "BigInt":
738
+ return "integer";
739
+ case "Float":
740
+ return "float";
741
+ case "Decimal":
742
+ return "decimal:" + (propDef.scale ?? 2);
743
+ case "Json":
744
+ return "array";
745
+ case "Date":
746
+ return "date";
747
+ case "Timestamp":
748
+ return "datetime";
749
+ case "Password":
750
+ return "hashed";
751
+ default:
752
+ return null;
753
+ }
754
+ }
755
+ function isNullable(propDef) {
756
+ return "nullable" in propDef && propDef.nullable === true;
757
+ }
758
+ function getPhpDocType(propDef, schemas) {
759
+ const nullable = isNullable(propDef);
760
+ switch (propDef.type) {
761
+ case "String":
762
+ case "Text":
763
+ case "LongText":
764
+ case "Email":
765
+ case "Password":
766
+ return "string" + (nullable ? "|null" : "");
767
+ case "Int":
768
+ case "BigInt":
769
+ return "int" + (nullable ? "|null" : "");
770
+ case "Float":
771
+ case "Decimal":
772
+ return "float" + (nullable ? "|null" : "");
773
+ case "Boolean":
774
+ return "bool" + (nullable ? "|null" : "");
775
+ case "Date":
776
+ case "Time":
777
+ case "Timestamp":
778
+ return "\\Carbon\\Carbon" + (nullable ? "|null" : "");
779
+ case "Json":
780
+ return "array" + (nullable ? "|null" : "");
781
+ case "Enum":
782
+ case "EnumRef":
783
+ return "string" + (nullable ? "|null" : "");
784
+ case "Association": {
785
+ const assoc = propDef;
786
+ if (assoc.target) {
787
+ const className = toPascalCase(assoc.target);
788
+ switch (assoc.relation) {
789
+ case "OneToMany":
790
+ case "ManyToMany":
791
+ case "MorphMany":
792
+ case "MorphToMany":
793
+ case "MorphedByMany":
794
+ return `\\Illuminate\\Database\\Eloquent\\Collection<${className}>`;
795
+ default:
796
+ return className + "|null";
797
+ }
798
+ }
799
+ return "mixed";
800
+ }
801
+ default:
802
+ return "mixed";
803
+ }
804
+ }
805
+ function generateBaseModel(schemas, options, stubContent) {
806
+ const modelMap = Object.values(schemas).filter((s) => s.kind !== "enum").map((s) => {
807
+ const className = toPascalCase(s.name);
808
+ return ` '${s.name}' => \\${options.modelNamespace}\\${className}::class,`;
809
+ }).join("\n");
810
+ const content = stubContent.replace(/\{\{BASE_MODEL_NAMESPACE\}\}/g, options.baseModelNamespace).replace(/\{\{BASE_MODEL_CLASS\}\}/g, options.baseModelClassName).replace(/\{\{MODEL_MAP\}\}/g, modelMap);
811
+ return {
812
+ path: `${options.baseModelPath}/${options.baseModelClassName}.php`,
813
+ content,
814
+ type: "base-model",
815
+ overwrite: true,
816
+ schemaName: "__base__"
817
+ };
818
+ }
819
+ function generateEntityBaseModel(schema, schemas, options, stubContent, authStubContent) {
820
+ const className = toPascalCase(schema.name);
821
+ const tableName = schema.options?.tableName ?? pluralize(toSnakeCase(schema.name));
822
+ const isAuth = schema.options?.authenticatable ?? false;
823
+ const primaryKey = "id";
824
+ const idType = schema.options?.idType ?? "BigInt";
825
+ const isUuid = idType === "Uuid";
826
+ const isStringKey = idType === "Uuid" || idType === "String";
827
+ const imports = [];
828
+ const traits = [];
829
+ const fillable = [];
830
+ const hidden = [];
831
+ const appends = [];
832
+ const casts = [];
833
+ const relations = [];
834
+ const docProperties = [];
835
+ if (schema.options?.softDelete) {
836
+ imports.push("use Illuminate\\Database\\Eloquent\\SoftDeletes;");
837
+ traits.push(" use SoftDeletes;");
838
+ }
839
+ const properties = schema.properties ?? {};
840
+ for (const [propName, propDef] of Object.entries(properties)) {
841
+ const snakeName = toSnakeCase(propName);
842
+ const phpType = getPhpDocType(propDef, schemas);
843
+ docProperties.push(` * @property ${phpType} $${snakeName}`);
844
+ if (propDef.type === "Association") {
845
+ const assoc = propDef;
846
+ if (assoc.target) {
847
+ imports.push(`use ${options.modelNamespace}\\${toPascalCase(assoc.target)};`);
848
+ }
849
+ relations.push(generateRelation(propName, assoc, options));
850
+ if (assoc.relation === "ManyToOne" || assoc.relation === "OneToOne") {
851
+ if (!assoc.mappedBy) {
852
+ const fkName = toSnakeCase(propName) + "_id";
853
+ fillable.push(` '${fkName}',`);
854
+ docProperties.push(` * @property int|null $${fkName}`);
855
+ }
856
+ }
857
+ } else if (propDef.type === "Password") {
858
+ fillable.push(` '${snakeName}',`);
859
+ hidden.push(` '${snakeName}',`);
860
+ const cast = getCastType(propDef);
861
+ if (cast) {
862
+ casts.push(` '${snakeName}' => '${cast}',`);
863
+ }
864
+ } else if (propDef.type === "File") {
865
+ const relMethod = generateFileRelation(propName, propDef);
866
+ relations.push(relMethod);
867
+ } else {
868
+ fillable.push(` '${snakeName}',`);
869
+ const cast = getCastType(propDef);
870
+ if (cast) {
871
+ casts.push(` '${snakeName}' => '${cast}',`);
872
+ }
873
+ }
874
+ }
875
+ const docComment = `/**
876
+ * ${className}BaseModel
877
+ *
878
+ ${docProperties.join("\n")}
879
+ */`;
880
+ const stub = isAuth ? authStubContent : stubContent;
881
+ const keyType = isStringKey ? ` /**
882
+ * The "type" of the primary key ID.
883
+ */
884
+ protected $keyType = 'string';
885
+
886
+ ` : "";
887
+ const incrementing = isUuid ? ` /**
888
+ * Indicates if the IDs are auto-incrementing.
889
+ */
890
+ public $incrementing = false;
891
+
892
+ ` : "";
893
+ if (isUuid) {
894
+ imports.push("use Illuminate\\Database\\Eloquent\\Concerns\\HasUuids;");
895
+ traits.push(" use HasUuids;");
896
+ }
897
+ const content = stub.replace(/\{\{BASE_MODEL_NAMESPACE\}\}/g, options.baseModelNamespace).replace(/\{\{BASE_MODEL_CLASS\}\}/g, options.baseModelClassName).replace(/\{\{CLASS_NAME\}\}/g, className).replace(/\{\{TABLE_NAME\}\}/g, tableName).replace(/\{\{PRIMARY_KEY\}\}/g, primaryKey).replace(/\{\{KEY_TYPE\}\}/g, keyType).replace(/\{\{INCREMENTING\}\}/g, incrementing).replace(/\{\{TIMESTAMPS\}\}/g, schema.options?.timestamps !== false ? "true" : "false").replace(/\{\{IMPORTS\}\}/g, [...new Set(imports)].sort().join("\n")).replace(/\{\{TRAITS\}\}/g, traits.join("\n")).replace(/\{\{DOC_COMMENT\}\}/g, docComment).replace(/\{\{FILLABLE\}\}/g, fillable.join("\n")).replace(/\{\{HIDDEN\}\}/g, hidden.join("\n")).replace(/\{\{APPENDS\}\}/g, appends.join("\n")).replace(/\{\{CASTS\}\}/g, casts.join("\n")).replace(/\{\{RELATIONS\}\}/g, relations.join("\n\n"));
898
+ return {
899
+ path: `${options.baseModelPath}/${className}BaseModel.php`,
900
+ content,
901
+ type: "entity-base",
902
+ overwrite: true,
903
+ schemaName: schema.name
904
+ };
905
+ }
906
+ function generateRelation(propName, assoc, options) {
907
+ const methodName = toCamelCase(propName);
908
+ const targetClass = assoc.target ? toPascalCase(assoc.target) : "";
909
+ const fkName = toSnakeCase(propName) + "_id";
910
+ switch (assoc.relation) {
911
+ case "ManyToOne":
912
+ return ` /**
913
+ * Get the ${propName} that owns this model.
914
+ */
915
+ public function ${methodName}(): BelongsTo
916
+ {
917
+ return $this->belongsTo(${targetClass}::class, '${fkName}');
918
+ }`;
919
+ case "OneToOne":
920
+ if (assoc.mappedBy) {
921
+ return ` /**
922
+ * Get the ${propName} for this model.
923
+ */
924
+ public function ${methodName}(): HasOne
925
+ {
926
+ return $this->hasOne(${targetClass}::class, '${toSnakeCase(assoc.mappedBy)}_id');
927
+ }`;
928
+ }
929
+ return ` /**
930
+ * Get the ${propName} that owns this model.
931
+ */
932
+ public function ${methodName}(): BelongsTo
933
+ {
934
+ return $this->belongsTo(${targetClass}::class, '${fkName}');
935
+ }`;
936
+ case "OneToMany":
937
+ return ` /**
938
+ * Get the ${propName} for this model.
939
+ */
940
+ public function ${methodName}(): HasMany
941
+ {
942
+ return $this->hasMany(${targetClass}::class, '${toSnakeCase(assoc.inversedBy ?? propName)}_id');
943
+ }`;
944
+ case "ManyToMany": {
945
+ const pivotTable = assoc.joinTable ?? `${toSnakeCase(propName)}_pivot`;
946
+ return ` /**
947
+ * The ${propName} that belong to this model.
948
+ */
949
+ public function ${methodName}(): BelongsToMany
950
+ {
951
+ return $this->belongsToMany(${targetClass}::class, '${pivotTable}')
952
+ ->withTimestamps();
953
+ }`;
954
+ }
955
+ case "MorphTo":
956
+ return ` /**
957
+ * Get the parent ${propName} model.
958
+ */
959
+ public function ${methodName}(): MorphTo
960
+ {
961
+ return $this->morphTo('${methodName}');
962
+ }`;
963
+ case "MorphOne":
964
+ return ` /**
965
+ * Get the ${propName} for this model.
966
+ */
967
+ public function ${methodName}(): MorphOne
968
+ {
969
+ return $this->morphOne(${targetClass}::class, '${assoc.morphName ?? propName}');
970
+ }`;
971
+ case "MorphMany":
972
+ return ` /**
973
+ * Get the ${propName} for this model.
974
+ */
975
+ public function ${methodName}(): MorphMany
976
+ {
977
+ return $this->morphMany(${targetClass}::class, '${assoc.morphName ?? propName}');
978
+ }`;
979
+ default:
980
+ return ` // TODO: Implement ${assoc.relation} relation for ${propName}`;
981
+ }
982
+ }
983
+ function generateFileRelation(propName, propDef) {
984
+ const methodName = toCamelCase(propName);
985
+ const relationType = propDef.multiple ? "MorphMany" : "MorphOne";
986
+ const relationMethod = propDef.multiple ? "morphMany" : "morphOne";
987
+ return ` /**
988
+ * Get the ${propName} file(s) for this model.
989
+ */
990
+ public function ${methodName}(): ${relationType}
991
+ {
992
+ return $this->${relationMethod}(FileUpload::class, 'uploadable')
993
+ ->where('attribute_name', '${propName}');
994
+ }`;
995
+ }
996
+ function generateEntityModel(schema, options, stubContent) {
997
+ const className = toPascalCase(schema.name);
998
+ const content = stubContent.replace(/\{\{BASE_MODEL_NAMESPACE\}\}/g, options.baseModelNamespace).replace(/\{\{MODEL_NAMESPACE\}\}/g, options.modelNamespace).replace(/\{\{CLASS_NAME\}\}/g, className);
999
+ return {
1000
+ path: `${options.modelPath}/${className}.php`,
1001
+ content,
1002
+ type: "entity",
1003
+ overwrite: false,
1004
+ // Never overwrite user models
1005
+ schemaName: schema.name
1006
+ };
1007
+ }
1008
+ function getStubContent(stubName) {
1009
+ const stubs = {
1010
+ "base-model": `<?php
1011
+
1012
+ namespace {{BASE_MODEL_NAMESPACE}};
1013
+
1014
+ /**
1015
+ * Base model class for all Omnify-generated models.
1016
+ * Contains model mapping for polymorphic relations.
1017
+ *
1018
+ * DO NOT EDIT - This file is auto-generated by Omnify.
1019
+ * Any changes will be overwritten on next generation.
1020
+ *
1021
+ * @generated by @famgia/omnify-laravel
1022
+ */
1023
+
1024
+ use Illuminate\\Database\\Eloquent\\Model;
1025
+ use Illuminate\\Database\\Eloquent\\Relations\\Relation;
1026
+
1027
+ abstract class {{BASE_MODEL_CLASS}} extends Model
1028
+ {
1029
+ /**
1030
+ * Model class map for polymorphic relations.
1031
+ */
1032
+ protected static array $modelMap = [
1033
+ {{MODEL_MAP}}
1034
+ ];
1035
+
1036
+ /**
1037
+ * Boot the model and register morph map.
1038
+ */
1039
+ protected static function boot(): void
1040
+ {
1041
+ parent::boot();
1042
+
1043
+ // Register morph map for polymorphic relations
1044
+ Relation::enforceMorphMap(static::$modelMap);
1045
+ }
1046
+
1047
+ /**
1048
+ * Get the model class for a given morph type.
1049
+ */
1050
+ public static function getModelClass(string $morphType): ?string
1051
+ {
1052
+ return static::$modelMap[$morphType] ?? null;
1053
+ }
1054
+ }
1055
+ `,
1056
+ "entity-base": `<?php
1057
+
1058
+ namespace {{BASE_MODEL_NAMESPACE}};
1059
+
1060
+ /**
1061
+ * DO NOT EDIT - This file is auto-generated by Omnify.
1062
+ * Any changes will be overwritten on next generation.
1063
+ *
1064
+ * @generated by @famgia/omnify-laravel
1065
+ */
1066
+
1067
+ use Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;
1068
+ use Illuminate\\Database\\Eloquent\\Relations\\HasMany;
1069
+ use Illuminate\\Database\\Eloquent\\Relations\\HasOne;
1070
+ use Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;
1071
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphTo;
1072
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphOne;
1073
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphMany;
1074
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphToMany;
1075
+ use Illuminate\\Database\\Eloquent\\Collection as EloquentCollection;
1076
+ {{IMPORTS}}
1077
+
1078
+ {{DOC_COMMENT}}
1079
+ class {{CLASS_NAME}}BaseModel extends {{BASE_MODEL_CLASS}}
1080
+ {
1081
+ {{TRAITS}}
1082
+ /**
1083
+ * The table associated with the model.
1084
+ */
1085
+ protected $table = '{{TABLE_NAME}}';
1086
+
1087
+ /**
1088
+ * The primary key for the model.
1089
+ */
1090
+ protected $primaryKey = '{{PRIMARY_KEY}}';
1091
+
1092
+ {{KEY_TYPE}}
1093
+ {{INCREMENTING}}
1094
+ /**
1095
+ * Indicates if the model should be timestamped.
1096
+ */
1097
+ public $timestamps = {{TIMESTAMPS}};
1098
+
1099
+ /**
1100
+ * The attributes that are mass assignable.
1101
+ */
1102
+ protected $fillable = [
1103
+ {{FILLABLE}}
1104
+ ];
1105
+
1106
+ /**
1107
+ * The attributes that should be hidden for serialization.
1108
+ */
1109
+ protected $hidden = [
1110
+ {{HIDDEN}}
1111
+ ];
1112
+
1113
+ /**
1114
+ * The accessors to append to the model's array form.
1115
+ */
1116
+ protected $appends = [
1117
+ {{APPENDS}}
1118
+ ];
1119
+
1120
+ /**
1121
+ * Get the attributes that should be cast.
1122
+ */
1123
+ protected function casts(): array
1124
+ {
1125
+ return [
1126
+ {{CASTS}}
1127
+ ];
1128
+ }
1129
+
1130
+ {{RELATIONS}}
1131
+ }
1132
+ `,
1133
+ "entity-base-auth": `<?php
1134
+
1135
+ namespace {{BASE_MODEL_NAMESPACE}};
1136
+
1137
+ /**
1138
+ * DO NOT EDIT - This file is auto-generated by Omnify.
1139
+ * Any changes will be overwritten on next generation.
1140
+ *
1141
+ * @generated by @famgia/omnify-laravel
1142
+ */
1143
+
1144
+ use Illuminate\\Foundation\\Auth\\User as Authenticatable;
1145
+ use Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;
1146
+ use Illuminate\\Database\\Eloquent\\Relations\\HasMany;
1147
+ use Illuminate\\Database\\Eloquent\\Relations\\HasOne;
1148
+ use Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;
1149
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphTo;
1150
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphOne;
1151
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphMany;
1152
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphToMany;
1153
+ use Illuminate\\Database\\Eloquent\\Collection as EloquentCollection;
1154
+ use Illuminate\\Notifications\\Notifiable;
1155
+ {{IMPORTS}}
1156
+
1157
+ {{DOC_COMMENT}}
1158
+ class {{CLASS_NAME}}BaseModel extends Authenticatable
1159
+ {
1160
+ use Notifiable;
1161
+ {{TRAITS}}
1162
+ /**
1163
+ * The table associated with the model.
1164
+ */
1165
+ protected $table = '{{TABLE_NAME}}';
1166
+
1167
+ /**
1168
+ * The primary key for the model.
1169
+ */
1170
+ protected $primaryKey = '{{PRIMARY_KEY}}';
1171
+
1172
+ {{KEY_TYPE}}
1173
+ {{INCREMENTING}}
1174
+ /**
1175
+ * Indicates if the model should be timestamped.
1176
+ */
1177
+ public $timestamps = {{TIMESTAMPS}};
1178
+
1179
+ /**
1180
+ * The attributes that are mass assignable.
1181
+ */
1182
+ protected $fillable = [
1183
+ {{FILLABLE}}
1184
+ ];
1185
+
1186
+ /**
1187
+ * The attributes that should be hidden for serialization.
1188
+ */
1189
+ protected $hidden = [
1190
+ {{HIDDEN}}
1191
+ ];
1192
+
1193
+ /**
1194
+ * The accessors to append to the model's array form.
1195
+ */
1196
+ protected $appends = [
1197
+ {{APPENDS}}
1198
+ ];
1199
+
1200
+ /**
1201
+ * Get the attributes that should be cast.
1202
+ */
1203
+ protected function casts(): array
1204
+ {
1205
+ return [
1206
+ {{CASTS}}
1207
+ ];
1208
+ }
1209
+
1210
+ {{RELATIONS}}
1211
+ }
1212
+ `,
1213
+ "entity": `<?php
1214
+
1215
+ namespace {{MODEL_NAMESPACE}};
1216
+
1217
+ use {{BASE_MODEL_NAMESPACE}}\\{{CLASS_NAME}}BaseModel;
1218
+ use Illuminate\\Database\\Eloquent\\Factories\\HasFactory;
1219
+
1220
+ /**
1221
+ * {{CLASS_NAME}} Model
1222
+ *
1223
+ * This file is generated once and can be customized.
1224
+ * Add your custom methods and logic here.
1225
+ */
1226
+ class {{CLASS_NAME}} extends {{CLASS_NAME}}BaseModel
1227
+ {
1228
+ use HasFactory;
1229
+
1230
+ /**
1231
+ * Create a new model instance.
1232
+ */
1233
+ public function __construct(array $attributes = [])
1234
+ {
1235
+ parent::__construct($attributes);
1236
+ }
1237
+
1238
+ // Add your custom methods here
1239
+ }
1240
+ `
1241
+ };
1242
+ return stubs[stubName] ?? "";
1243
+ }
1244
+ function generateModels(schemas, options) {
1245
+ const resolved = resolveOptions(options);
1246
+ const models = [];
1247
+ models.push(generateBaseModel(schemas, resolved, getStubContent("base-model")));
1248
+ for (const schema of Object.values(schemas)) {
1249
+ if (schema.kind === "enum") {
1250
+ continue;
1251
+ }
1252
+ models.push(generateEntityBaseModel(
1253
+ schema,
1254
+ schemas,
1255
+ resolved,
1256
+ getStubContent("entity-base"),
1257
+ getStubContent("entity-base-auth")
1258
+ ));
1259
+ models.push(generateEntityModel(schema, resolved, getStubContent("entity")));
1260
+ }
1261
+ return models;
1262
+ }
1263
+ function getModelPath(model) {
1264
+ return model.path;
1265
+ }
1266
+
687
1267
  // src/plugin.ts
688
1268
  var LARAVEL_CONFIG_SCHEMA = {
689
1269
  fields: [
@@ -695,6 +1275,30 @@ var LARAVEL_CONFIG_SCHEMA = {
695
1275
  default: "database/migrations",
696
1276
  group: "output"
697
1277
  },
1278
+ {
1279
+ key: "modelsPath",
1280
+ type: "path",
1281
+ label: "Models Path",
1282
+ description: "Directory for user-editable model files",
1283
+ default: "app/Models",
1284
+ group: "output"
1285
+ },
1286
+ {
1287
+ key: "baseModelsPath",
1288
+ type: "path",
1289
+ label: "Base Models Path",
1290
+ description: "Directory for auto-generated base model files",
1291
+ default: "app/Models/OmnifyBase",
1292
+ group: "output"
1293
+ },
1294
+ {
1295
+ key: "generateModels",
1296
+ type: "boolean",
1297
+ label: "Generate Models",
1298
+ description: "Generate Eloquent model classes",
1299
+ default: true,
1300
+ group: "options"
1301
+ },
698
1302
  {
699
1303
  key: "connection",
700
1304
  type: "string",
@@ -705,41 +1309,69 @@ var LARAVEL_CONFIG_SCHEMA = {
705
1309
  }
706
1310
  ]
707
1311
  };
708
- function resolveOptions(options) {
1312
+ function resolveOptions2(options) {
709
1313
  return {
710
1314
  migrationsPath: options?.migrationsPath ?? "database/migrations",
1315
+ modelsPath: options?.modelsPath ?? "app/Models",
1316
+ baseModelsPath: options?.baseModelsPath ?? "app/Models/OmnifyBase",
1317
+ modelNamespace: options?.modelNamespace ?? "App\\Models",
1318
+ baseModelNamespace: options?.baseModelNamespace ?? "App\\Models\\OmnifyBase",
1319
+ generateModels: options?.generateModels ?? true,
711
1320
  connection: options?.connection,
712
1321
  timestamp: options?.timestamp
713
1322
  };
714
1323
  }
715
1324
  function laravelPlugin(options) {
716
- const resolved = resolveOptions(options);
1325
+ const resolved = resolveOptions2(options);
1326
+ const migrationGenerator = {
1327
+ name: "laravel-migrations",
1328
+ description: "Generate Laravel migration files",
1329
+ generate: async (ctx) => {
1330
+ const migrationOptions = {
1331
+ connection: resolved.connection,
1332
+ timestamp: resolved.timestamp
1333
+ };
1334
+ const migrations = generateMigrations(ctx.schemas, migrationOptions);
1335
+ return migrations.map((migration) => ({
1336
+ path: getMigrationPath(migration, resolved.migrationsPath),
1337
+ content: migration.content,
1338
+ type: "migration",
1339
+ metadata: {
1340
+ tableName: migration.tables[0],
1341
+ migrationType: migration.type
1342
+ }
1343
+ }));
1344
+ }
1345
+ };
1346
+ const modelGenerator = {
1347
+ name: "laravel-models",
1348
+ description: "Generate Eloquent model classes",
1349
+ generate: async (ctx) => {
1350
+ const modelOptions = {
1351
+ modelNamespace: resolved.modelNamespace,
1352
+ baseModelNamespace: resolved.baseModelNamespace,
1353
+ modelPath: resolved.modelsPath,
1354
+ baseModelPath: resolved.baseModelsPath
1355
+ };
1356
+ const models = generateModels(ctx.schemas, modelOptions);
1357
+ return models.map((model) => ({
1358
+ path: getModelPath(model),
1359
+ content: model.content,
1360
+ type: "model",
1361
+ // Skip writing user models if they already exist
1362
+ skipIfExists: !model.overwrite,
1363
+ metadata: {
1364
+ modelType: model.type,
1365
+ schemaName: model.schemaName
1366
+ }
1367
+ }));
1368
+ }
1369
+ };
717
1370
  return {
718
1371
  name: "@famgia/omnify-laravel",
719
- version: "0.0.13",
1372
+ version: "0.0.14",
720
1373
  configSchema: LARAVEL_CONFIG_SCHEMA,
721
- generators: [
722
- {
723
- name: "laravel-migrations",
724
- description: "Generate Laravel migration files",
725
- generate: async (ctx) => {
726
- const migrationOptions = {
727
- connection: resolved.connection,
728
- timestamp: resolved.timestamp
729
- };
730
- const migrations = generateMigrations(ctx.schemas, migrationOptions);
731
- return migrations.map((migration) => ({
732
- path: getMigrationPath(migration, resolved.migrationsPath),
733
- content: migration.content,
734
- type: "migration",
735
- metadata: {
736
- tableName: migration.tables[0],
737
- migrationType: migration.type
738
- }
739
- }));
740
- }
741
- }
742
- ]
1374
+ generators: resolved.generateModels ? [migrationGenerator, modelGenerator] : [migrationGenerator]
743
1375
  };
744
1376
  }
745
1377
  // Annotate the CommonJS export names for ESM import in node: