@famgia/omnify-laravel 0.0.14 → 0.0.16

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
@@ -121,9 +121,8 @@ function propertyToColumnMethod(propertyName, property) {
121
121
  if (baseProp.unique) {
122
122
  modifiers.push({ method: "unique" });
123
123
  }
124
- if (baseProp.default !== void 0) {
125
- const defaultValue = typeof baseProp.default === "string" ? baseProp.default : JSON.stringify(baseProp.default);
126
- modifiers.push({ method: "default", args: [defaultValue] });
124
+ if (baseProp.default !== void 0 && baseProp.default !== null) {
125
+ modifiers.push({ method: "default", args: [baseProp.default] });
127
126
  }
128
127
  if (baseProp.unsigned && (method === "integer" || method === "bigInteger")) {
129
128
  modifiers.push({ method: "unsigned" });
@@ -255,11 +254,18 @@ function generateForeignKey(propertyName, property, allSchemas) {
255
254
  } else if (targetPkType === "String") {
256
255
  method = "string";
257
256
  }
257
+ const modifiers = [];
258
+ if (assocProp.nullable || assocProp.relation === "ManyToOne") {
259
+ modifiers.push({ method: "nullable" });
260
+ }
261
+ if (assocProp.default !== void 0 && assocProp.default !== null) {
262
+ modifiers.push({ method: "default", args: [assocProp.default] });
263
+ }
258
264
  const column = {
259
265
  name: columnName,
260
266
  method,
261
267
  args: [columnName],
262
- modifiers: assocProp.relation === "ManyToOne" ? [{ method: "nullable" }] : []
268
+ modifiers
263
269
  };
264
270
  const foreignKey = {
265
271
  columns: [columnName],
@@ -359,6 +365,12 @@ function formatColumnMethod(column) {
359
365
  if (typeof arg === "string") {
360
366
  return `'${arg}'`;
361
367
  }
368
+ if (typeof arg === "boolean") {
369
+ return arg ? "true" : "false";
370
+ }
371
+ if (typeof arg === "number") {
372
+ return String(arg);
373
+ }
362
374
  return String(arg);
363
375
  }).join(", ");
364
376
  code += `->${modifier.method}(${modArgs})`;
@@ -466,6 +478,7 @@ function generatePivotTableBlueprint(pivot) {
466
478
  args: [pivot.targetColumn],
467
479
  modifiers: []
468
480
  });
481
+ columns.push(...generateTimestampColumns());
469
482
  foreignKeys.push({
470
483
  columns: [pivot.sourceColumn],
471
484
  references: "id",
@@ -1040,6 +1053,10 @@ function generateMigrationsFromChanges(changes, options = {}) {
1040
1053
  return migrations;
1041
1054
  }
1042
1055
 
1056
+ // src/plugin.ts
1057
+ var import_node_fs = require("fs");
1058
+ var import_node_path = require("path");
1059
+
1043
1060
  // src/utils.ts
1044
1061
  function toSnakeCase(str) {
1045
1062
  return str.replace(/([A-Z])/g, "_$1").replace(/^_/, "").toLowerCase();
@@ -1195,7 +1212,7 @@ function generateEntityBaseModel(schema, schemas, options, stubContent, authStub
1195
1212
  if (assoc.target) {
1196
1213
  imports.push(`use ${options.modelNamespace}\\${toPascalCase(assoc.target)};`);
1197
1214
  }
1198
- relations.push(generateRelation(propName, assoc, options));
1215
+ relations.push(generateRelation(propName, assoc, schema, schemas, options));
1199
1216
  if (assoc.relation === "ManyToOne" || assoc.relation === "OneToOne") {
1200
1217
  if (!assoc.mappedBy) {
1201
1218
  const fkName = toSnakeCase(propName) + "_id";
@@ -1252,7 +1269,22 @@ ${docProperties.join("\n")}
1252
1269
  schemaName: schema.name
1253
1270
  };
1254
1271
  }
1255
- function generateRelation(propName, assoc, options) {
1272
+ function findInverseRelation(currentSchemaName, targetSchemaName, schemas) {
1273
+ const targetSchema = schemas[targetSchemaName];
1274
+ if (!targetSchema || !targetSchema.properties) {
1275
+ return null;
1276
+ }
1277
+ for (const [propName, propDef] of Object.entries(targetSchema.properties)) {
1278
+ if (propDef.type === "Association") {
1279
+ const assoc = propDef;
1280
+ if (assoc.relation === "ManyToOne" && assoc.target === currentSchemaName) {
1281
+ return propName;
1282
+ }
1283
+ }
1284
+ }
1285
+ return null;
1286
+ }
1287
+ function generateRelation(propName, assoc, schema, schemas, options) {
1256
1288
  const methodName = toCamelCase(propName);
1257
1289
  const targetClass = assoc.target ? toPascalCase(assoc.target) : "";
1258
1290
  const fkName = toSnakeCase(propName) + "_id";
@@ -1282,14 +1314,28 @@ function generateRelation(propName, assoc, options) {
1282
1314
  {
1283
1315
  return $this->belongsTo(${targetClass}::class, '${fkName}');
1284
1316
  }`;
1285
- case "OneToMany":
1317
+ case "OneToMany": {
1318
+ let foreignKey;
1319
+ if (assoc.inversedBy) {
1320
+ foreignKey = toSnakeCase(assoc.inversedBy) + "_id";
1321
+ } else if (assoc.target) {
1322
+ const inverseRelation = findInverseRelation(schema.name, assoc.target, schemas);
1323
+ if (inverseRelation) {
1324
+ foreignKey = toSnakeCase(inverseRelation) + "_id";
1325
+ } else {
1326
+ foreignKey = toSnakeCase(schema.name) + "_id";
1327
+ }
1328
+ } else {
1329
+ foreignKey = toSnakeCase(propName) + "_id";
1330
+ }
1286
1331
  return ` /**
1287
1332
  * Get the ${propName} for this model.
1288
1333
  */
1289
1334
  public function ${methodName}(): HasMany
1290
1335
  {
1291
- return $this->hasMany(${targetClass}::class, '${toSnakeCase(assoc.inversedBy ?? propName)}_id');
1336
+ return $this->hasMany(${targetClass}::class, '${foreignKey}');
1292
1337
  }`;
1338
+ }
1293
1339
  case "ManyToMany": {
1294
1340
  const pivotTable = assoc.joinTable ?? `${toSnakeCase(propName)}_pivot`;
1295
1341
  return ` /**
@@ -1586,14 +1632,73 @@ class {{CLASS_NAME}} extends {{CLASS_NAME}}BaseModel
1586
1632
 
1587
1633
  // Add your custom methods here
1588
1634
  }
1635
+ `,
1636
+ "service-provider": `<?php
1637
+
1638
+ namespace App\\Providers;
1639
+
1640
+ use Illuminate\\Database\\Eloquent\\Relations\\Relation;
1641
+ use Illuminate\\Support\\ServiceProvider;
1642
+
1643
+ /**
1644
+ * Omnify Service Provider
1645
+ *
1646
+ * DO NOT EDIT - This file is auto-generated by Omnify.
1647
+ * Any changes will be overwritten on next generation.
1648
+ *
1649
+ * - Loads Omnify migrations from database/migrations/omnify
1650
+ * - Registers morph map for polymorphic relationships
1651
+ *
1652
+ * @generated by @famgia/omnify-laravel
1653
+ */
1654
+ class OmnifyServiceProvider extends ServiceProvider
1655
+ {
1656
+ /**
1657
+ * Register any application services.
1658
+ */
1659
+ public function register(): void
1660
+ {
1661
+ //
1662
+ }
1663
+
1664
+ /**
1665
+ * Bootstrap any application services.
1666
+ */
1667
+ public function boot(): void
1668
+ {
1669
+ // Load Omnify migrations from custom directory
1670
+ $this->loadMigrationsFrom(database_path('migrations/omnify'));
1671
+
1672
+ // Register morph map for polymorphic relationships
1673
+ Relation::enforceMorphMap([
1674
+ {{MORPH_MAP}}
1675
+ ]);
1676
+ }
1677
+ }
1589
1678
  `
1590
1679
  };
1591
1680
  return stubs[stubName] ?? "";
1592
1681
  }
1682
+ function generateServiceProvider(schemas, options, stubContent) {
1683
+ const morphMap = Object.values(schemas).filter((s) => s.kind !== "enum").map((s) => {
1684
+ const className = toPascalCase(s.name);
1685
+ return ` '${s.name}' => \\${options.modelNamespace}\\${className}::class,`;
1686
+ }).join("\n");
1687
+ const content = stubContent.replace(/\{\{MORPH_MAP\}\}/g, morphMap);
1688
+ return {
1689
+ path: "app/Providers/OmnifyServiceProvider.php",
1690
+ content,
1691
+ type: "service-provider",
1692
+ overwrite: true,
1693
+ // Always overwrite to keep morph map in sync
1694
+ schemaName: "__service_provider__"
1695
+ };
1696
+ }
1593
1697
  function generateModels(schemas, options) {
1594
1698
  const resolved = resolveOptions(options);
1595
1699
  const models = [];
1596
1700
  models.push(generateBaseModel(schemas, resolved, getStubContent("base-model")));
1701
+ models.push(generateServiceProvider(schemas, resolved, getStubContent("service-provider")));
1597
1702
  for (const schema of Object.values(schemas)) {
1598
1703
  if (schema.kind === "enum") {
1599
1704
  continue;
@@ -1612,6 +1717,355 @@ function generateModels(schemas, options) {
1612
1717
  function getModelPath(model) {
1613
1718
  return model.path;
1614
1719
  }
1720
+ function generateProviderRegistration(existingContent, laravelVersion) {
1721
+ const providerClass = "App\\Providers\\OmnifyServiceProvider::class";
1722
+ const providerLine = ` ${providerClass},`;
1723
+ if (existingContent && existingContent.includes("OmnifyServiceProvider")) {
1724
+ return {
1725
+ path: laravelVersion === "laravel11+" ? "bootstrap/providers.php" : "config/app.php",
1726
+ content: existingContent,
1727
+ laravelVersion,
1728
+ alreadyRegistered: true
1729
+ };
1730
+ }
1731
+ if (laravelVersion === "laravel11+") {
1732
+ if (existingContent) {
1733
+ const lines = existingContent.split("\n");
1734
+ const result = [];
1735
+ let inserted = false;
1736
+ for (let i = 0; i < lines.length; i++) {
1737
+ const line = lines[i];
1738
+ if (!inserted && line.trim() === "];") {
1739
+ result.push(providerLine);
1740
+ inserted = true;
1741
+ }
1742
+ result.push(line);
1743
+ }
1744
+ return {
1745
+ path: "bootstrap/providers.php",
1746
+ content: result.join("\n"),
1747
+ laravelVersion,
1748
+ alreadyRegistered: false
1749
+ };
1750
+ } else {
1751
+ return {
1752
+ path: "bootstrap/providers.php",
1753
+ content: `<?php
1754
+
1755
+ return [
1756
+ App\\Providers\\AppServiceProvider::class,
1757
+ ${providerLine}
1758
+ ];
1759
+ `,
1760
+ laravelVersion,
1761
+ alreadyRegistered: false
1762
+ };
1763
+ }
1764
+ } else {
1765
+ if (existingContent) {
1766
+ const providersSectionRegex = /'providers'\s*=>\s*\[[\s\S]*?\n(\s*)\]/m;
1767
+ const match = existingContent.match(providersSectionRegex);
1768
+ if (match) {
1769
+ const providersStart = existingContent.indexOf("'providers'");
1770
+ if (providersStart === -1) {
1771
+ return null;
1772
+ }
1773
+ let depth = 0;
1774
+ let foundStart = false;
1775
+ let insertPos = -1;
1776
+ for (let i = providersStart; i < existingContent.length; i++) {
1777
+ const char = existingContent[i];
1778
+ if (char === "[") {
1779
+ foundStart = true;
1780
+ depth++;
1781
+ } else if (char === "]") {
1782
+ depth--;
1783
+ if (foundStart && depth === 0) {
1784
+ insertPos = i;
1785
+ break;
1786
+ }
1787
+ }
1788
+ }
1789
+ if (insertPos !== -1) {
1790
+ const beforeClose = existingContent.substring(0, insertPos);
1791
+ const lastNewline = beforeClose.lastIndexOf("\n");
1792
+ const content = existingContent.substring(0, lastNewline + 1) + providerLine + "\n" + existingContent.substring(lastNewline + 1);
1793
+ return {
1794
+ path: "config/app.php",
1795
+ content,
1796
+ laravelVersion,
1797
+ alreadyRegistered: false
1798
+ };
1799
+ }
1800
+ }
1801
+ return null;
1802
+ } else {
1803
+ return null;
1804
+ }
1805
+ }
1806
+ }
1807
+
1808
+ // src/factory/generator.ts
1809
+ function resolveOptions2(options) {
1810
+ return {
1811
+ modelNamespace: options?.modelNamespace ?? "App\\Models",
1812
+ factoryPath: options?.factoryPath ?? "database/factories",
1813
+ fakerLocale: options?.fakerLocale ?? "en_US"
1814
+ };
1815
+ }
1816
+ function getStubContent2() {
1817
+ return `<?php
1818
+
1819
+ namespace Database\\Factories;
1820
+
1821
+ use {{MODEL_NAMESPACE}}\\{{MODEL_NAME}};
1822
+ use Illuminate\\Database\\Eloquent\\Factories\\Factory;
1823
+ {{IMPORTS}}
1824
+
1825
+ /**
1826
+ * @extends Factory<{{MODEL_NAME}}>
1827
+ */
1828
+ class {{MODEL_NAME}}Factory extends Factory
1829
+ {
1830
+ protected $model = {{MODEL_NAME}}::class;
1831
+
1832
+ /**
1833
+ * Define the model's default state.
1834
+ *
1835
+ * @return array<string, mixed>
1836
+ */
1837
+ public function definition(): array
1838
+ {
1839
+ return [
1840
+ {{ATTRIBUTES}}
1841
+ ];
1842
+ }
1843
+ }
1844
+ `;
1845
+ }
1846
+ function generateFakeData(propertyName, property, schema, schemas) {
1847
+ const type = property.type;
1848
+ if (["deleted_at", "created_at", "updated_at"].includes(propertyName)) {
1849
+ return null;
1850
+ }
1851
+ if (type === "Association") {
1852
+ return null;
1853
+ }
1854
+ switch (type) {
1855
+ case "String":
1856
+ return generateStringFake(propertyName, property);
1857
+ case "Email":
1858
+ return `'${propertyName}' => fake()->unique()->safeEmail(),`;
1859
+ case "Password":
1860
+ return `'${propertyName}' => bcrypt('password'),`;
1861
+ case "Int":
1862
+ case "BigInt":
1863
+ return generateIntFake(propertyName, property);
1864
+ case "Float":
1865
+ case "Decimal":
1866
+ return `'${propertyName}' => fake()->randomFloat(2, 1, 10000),`;
1867
+ case "Boolean":
1868
+ return `'${propertyName}' => fake()->boolean(),`;
1869
+ case "Text":
1870
+ return `'${propertyName}' => fake()->paragraphs(3, true),`;
1871
+ case "LongText":
1872
+ return `'${propertyName}' => fake()->paragraphs(5, true),`;
1873
+ case "Date":
1874
+ return `'${propertyName}' => fake()->date(),`;
1875
+ case "Time":
1876
+ return `'${propertyName}' => fake()->time(),`;
1877
+ case "Timestamp":
1878
+ case "DateTime":
1879
+ return `'${propertyName}' => fake()->dateTime(),`;
1880
+ case "Json":
1881
+ return `'${propertyName}' => [],`;
1882
+ case "Enum":
1883
+ return generateEnumFake(propertyName, property);
1884
+ case "EnumRef":
1885
+ return generateEnumRefFake(propertyName, property, schemas);
1886
+ default:
1887
+ return `'${propertyName}' => fake()->sentence(),`;
1888
+ }
1889
+ }
1890
+ function generateStringFake(propertyName, property) {
1891
+ if (propertyName === "slug") {
1892
+ return `'${propertyName}' => fake()->unique()->slug(),`;
1893
+ }
1894
+ if (propertyName === "uuid" || propertyName === "uid") {
1895
+ return `'${propertyName}' => (string) \\Illuminate\\Support\\Str::uuid(),`;
1896
+ }
1897
+ if (propertyName.includes("email")) {
1898
+ return `'${propertyName}' => fake()->unique()->safeEmail(),`;
1899
+ }
1900
+ if (propertyName.includes("phone")) {
1901
+ return `'${propertyName}' => fake()->phoneNumber(),`;
1902
+ }
1903
+ if (propertyName.includes("image") || propertyName.includes("photo") || propertyName.includes("avatar")) {
1904
+ return `'${propertyName}' => fake()->imageUrl(),`;
1905
+ }
1906
+ if (propertyName.includes("url") || propertyName.includes("website")) {
1907
+ return `'${propertyName}' => fake()->url(),`;
1908
+ }
1909
+ if (propertyName.includes("path") || propertyName.includes("file")) {
1910
+ return `'${propertyName}' => 'uploads/' . fake()->uuid() . '.jpg',`;
1911
+ }
1912
+ if (propertyName === "name" || propertyName === "title") {
1913
+ return `'${propertyName}' => fake()->sentence(3),`;
1914
+ }
1915
+ if (propertyName.includes("name")) {
1916
+ return `'${propertyName}' => fake()->name(),`;
1917
+ }
1918
+ if (propertyName.includes("address")) {
1919
+ return `'${propertyName}' => fake()->address(),`;
1920
+ }
1921
+ if (propertyName.includes("city")) {
1922
+ return `'${propertyName}' => fake()->city(),`;
1923
+ }
1924
+ if (propertyName.includes("country")) {
1925
+ return `'${propertyName}' => fake()->country(),`;
1926
+ }
1927
+ if (propertyName.includes("zip") || propertyName.includes("postal")) {
1928
+ return `'${propertyName}' => fake()->postcode(),`;
1929
+ }
1930
+ if (propertyName.includes("color")) {
1931
+ return `'${propertyName}' => fake()->hexColor(),`;
1932
+ }
1933
+ if (propertyName.includes("token") || propertyName.includes("secret") || propertyName.includes("key")) {
1934
+ return `'${propertyName}' => \\Illuminate\\Support\\Str::random(32),`;
1935
+ }
1936
+ if (propertyName.includes("code")) {
1937
+ return `'${propertyName}' => fake()->unique()->regexify('[A-Z0-9]{8}'),`;
1938
+ }
1939
+ const length = property.length;
1940
+ if (length && length <= 50) {
1941
+ return `'${propertyName}' => fake()->words(3, true),`;
1942
+ }
1943
+ return `'${propertyName}' => fake()->sentence(),`;
1944
+ }
1945
+ function generateIntFake(propertyName, property) {
1946
+ if (propertyName.includes("count") || propertyName.includes("quantity")) {
1947
+ return `'${propertyName}' => fake()->numberBetween(0, 100),`;
1948
+ }
1949
+ if (propertyName.includes("price") || propertyName.includes("amount") || propertyName.includes("cost")) {
1950
+ return `'${propertyName}' => fake()->numberBetween(100, 10000),`;
1951
+ }
1952
+ if (propertyName.includes("order") || propertyName.includes("sort") || propertyName.includes("position")) {
1953
+ return `'${propertyName}' => fake()->numberBetween(1, 100),`;
1954
+ }
1955
+ if (propertyName.includes("age")) {
1956
+ return `'${propertyName}' => fake()->numberBetween(18, 80),`;
1957
+ }
1958
+ if (propertyName.includes("year")) {
1959
+ return `'${propertyName}' => fake()->year(),`;
1960
+ }
1961
+ return `'${propertyName}' => fake()->numberBetween(1, 1000),`;
1962
+ }
1963
+ function generateEnumFake(propertyName, property) {
1964
+ const enumValues = property.enum;
1965
+ if (!enumValues || enumValues.length === 0) {
1966
+ return `'${propertyName}' => null,`;
1967
+ }
1968
+ const values = enumValues.map((v) => typeof v === "string" ? v : v.value);
1969
+ const valuesStr = values.map((v) => `'${v}'`).join(", ");
1970
+ return `'${propertyName}' => fake()->randomElement([${valuesStr}]),`;
1971
+ }
1972
+ function generateEnumRefFake(propertyName, property, schemas) {
1973
+ const enumName = property.enum;
1974
+ if (!enumName) {
1975
+ return `'${propertyName}' => null,`;
1976
+ }
1977
+ const enumSchema = schemas[enumName];
1978
+ if (!enumSchema || enumSchema.kind !== "enum" || !enumSchema.values) {
1979
+ return `'${propertyName}' => null,`;
1980
+ }
1981
+ const valuesStr = enumSchema.values.map((v) => `'${v}'`).join(", ");
1982
+ return `'${propertyName}' => fake()->randomElement([${valuesStr}]),`;
1983
+ }
1984
+ function generateAssociationFake(propertyName, property, schema, schemas, modelNamespace) {
1985
+ if (property.type !== "Association") {
1986
+ return null;
1987
+ }
1988
+ const relation = property.relation;
1989
+ const target = property.target;
1990
+ if (relation !== "ManyToOne" || !target) {
1991
+ return null;
1992
+ }
1993
+ const foreignKey = `${toSnakeCase(propertyName)}_id`;
1994
+ const isNullable2 = property.nullable ?? false;
1995
+ const targetSchema = schemas[target];
1996
+ if (!targetSchema) {
1997
+ return null;
1998
+ }
1999
+ let fake;
2000
+ if (isNullable2) {
2001
+ fake = `'${foreignKey}' => ${target}::query()->inRandomOrder()->first()?->id,`;
2002
+ } else {
2003
+ fake = `'${foreignKey}' => ${target}::query()->inRandomOrder()->first()?->id ?? ${target}::factory()->create()->id,`;
2004
+ }
2005
+ let importStatement;
2006
+ if (target !== schema.name) {
2007
+ importStatement = `use ${modelNamespace}\\${target};`;
2008
+ }
2009
+ return { fake, import: importStatement };
2010
+ }
2011
+ function generateFactory(schema, schemas, options, stubContent) {
2012
+ if (schema.kind === "enum") {
2013
+ return null;
2014
+ }
2015
+ const modelName = toPascalCase(schema.name);
2016
+ const factoryName = `${modelName}Factory`;
2017
+ const attributes = [];
2018
+ const imports = [];
2019
+ if (schema.properties) {
2020
+ for (const [propName, prop] of Object.entries(schema.properties)) {
2021
+ if (prop.type === "Association") {
2022
+ const assocResult = generateAssociationFake(propName, prop, schema, schemas, options.modelNamespace);
2023
+ if (assocResult) {
2024
+ attributes.push(assocResult.fake);
2025
+ if (assocResult.import) {
2026
+ imports.push(assocResult.import);
2027
+ }
2028
+ }
2029
+ continue;
2030
+ }
2031
+ const fake = generateFakeData(propName, prop, schema, schemas);
2032
+ if (fake) {
2033
+ attributes.push(fake);
2034
+ }
2035
+ }
2036
+ }
2037
+ let content = stubContent;
2038
+ content = content.replace(/\{\{MODEL_NAMESPACE\}\}/g, options.modelNamespace);
2039
+ content = content.replace(/\{\{MODEL_NAME\}\}/g, modelName);
2040
+ const uniqueImports = [...new Set(imports)];
2041
+ const importsStr = uniqueImports.length > 0 ? "\n" + uniqueImports.join("\n") : "";
2042
+ content = content.replace(/\{\{IMPORTS\}\}/g, importsStr);
2043
+ const attributesStr = attributes.length > 0 ? attributes.map((a) => ` ${a}`).join("\n") : "";
2044
+ content = content.replace(/\{\{ATTRIBUTES\}\}/g, attributesStr);
2045
+ return {
2046
+ name: factoryName,
2047
+ schemaName: schema.name,
2048
+ path: `${options.factoryPath}/${factoryName}.php`,
2049
+ content,
2050
+ overwrite: false
2051
+ // Factories should not overwrite existing files
2052
+ };
2053
+ }
2054
+ function generateFactories(schemas, options) {
2055
+ const resolved = resolveOptions2(options);
2056
+ const stubContent = getStubContent2();
2057
+ const factories = [];
2058
+ for (const schema of Object.values(schemas)) {
2059
+ const factory = generateFactory(schema, schemas, resolved, stubContent);
2060
+ if (factory) {
2061
+ factories.push(factory);
2062
+ }
2063
+ }
2064
+ return factories;
2065
+ }
2066
+ function getFactoryPath(factory) {
2067
+ return factory.path;
2068
+ }
1615
2069
 
1616
2070
  // src/plugin.ts
1617
2071
  var LARAVEL_CONFIG_SCHEMA = {
@@ -1620,8 +2074,8 @@ var LARAVEL_CONFIG_SCHEMA = {
1620
2074
  key: "migrationsPath",
1621
2075
  type: "path",
1622
2076
  label: "Migrations Path",
1623
- description: "Directory for Laravel migration files",
1624
- default: "database/migrations",
2077
+ description: "Directory for Laravel migration files (loaded via OmnifyServiceProvider)",
2078
+ default: "database/migrations/omnify",
1625
2079
  group: "output"
1626
2080
  },
1627
2081
  {
@@ -1648,6 +2102,22 @@ var LARAVEL_CONFIG_SCHEMA = {
1648
2102
  default: true,
1649
2103
  group: "options"
1650
2104
  },
2105
+ {
2106
+ key: "factoriesPath",
2107
+ type: "path",
2108
+ label: "Factories Path",
2109
+ description: "Directory for Laravel factory files",
2110
+ default: "database/factories",
2111
+ group: "output"
2112
+ },
2113
+ {
2114
+ key: "generateFactories",
2115
+ type: "boolean",
2116
+ label: "Generate Factories",
2117
+ description: "Generate Laravel factory classes for testing",
2118
+ default: true,
2119
+ group: "options"
2120
+ },
1651
2121
  {
1652
2122
  key: "connection",
1653
2123
  type: "string",
@@ -1658,20 +2128,23 @@ var LARAVEL_CONFIG_SCHEMA = {
1658
2128
  }
1659
2129
  ]
1660
2130
  };
1661
- function resolveOptions2(options) {
2131
+ function resolveOptions3(options) {
1662
2132
  return {
1663
- migrationsPath: options?.migrationsPath ?? "database/migrations",
2133
+ migrationsPath: options?.migrationsPath ?? "database/migrations/omnify",
1664
2134
  modelsPath: options?.modelsPath ?? "app/Models",
1665
2135
  baseModelsPath: options?.baseModelsPath ?? "app/Models/OmnifyBase",
1666
2136
  modelNamespace: options?.modelNamespace ?? "App\\Models",
1667
2137
  baseModelNamespace: options?.baseModelNamespace ?? "App\\Models\\OmnifyBase",
1668
2138
  generateModels: options?.generateModels ?? true,
2139
+ factoriesPath: options?.factoriesPath ?? "database/factories",
2140
+ generateFactories: options?.generateFactories ?? true,
2141
+ fakerLocale: options?.fakerLocale ?? "en_US",
1669
2142
  connection: options?.connection,
1670
2143
  timestamp: options?.timestamp
1671
2144
  };
1672
2145
  }
1673
2146
  function laravelPlugin(options) {
1674
- const resolved = resolveOptions2(options);
2147
+ const resolved = resolveOptions3(options);
1675
2148
  const migrationGenerator = {
1676
2149
  name: "laravel-migrations",
1677
2150
  description: "Generate Laravel migration files",
@@ -1703,7 +2176,7 @@ function laravelPlugin(options) {
1703
2176
  baseModelPath: resolved.baseModelsPath
1704
2177
  };
1705
2178
  const models = generateModels(ctx.schemas, modelOptions);
1706
- return models.map((model) => ({
2179
+ const outputs = models.map((model) => ({
1707
2180
  path: getModelPath(model),
1708
2181
  content: model.content,
1709
2182
  type: "model",
@@ -1714,13 +2187,82 @@ function laravelPlugin(options) {
1714
2187
  schemaName: model.schemaName
1715
2188
  }
1716
2189
  }));
2190
+ const bootstrapProvidersPath = (0, import_node_path.join)(ctx.cwd, "bootstrap/providers.php");
2191
+ const configAppPath = (0, import_node_path.join)(ctx.cwd, "config/app.php");
2192
+ let existingContent = null;
2193
+ let laravelVersion;
2194
+ if ((0, import_node_fs.existsSync)(bootstrapProvidersPath)) {
2195
+ laravelVersion = "laravel11+";
2196
+ try {
2197
+ existingContent = (0, import_node_fs.readFileSync)(bootstrapProvidersPath, "utf-8");
2198
+ } catch {
2199
+ existingContent = null;
2200
+ }
2201
+ } else if ((0, import_node_fs.existsSync)(configAppPath)) {
2202
+ laravelVersion = "laravel10-";
2203
+ try {
2204
+ existingContent = (0, import_node_fs.readFileSync)(configAppPath, "utf-8");
2205
+ } catch {
2206
+ existingContent = null;
2207
+ }
2208
+ } else {
2209
+ laravelVersion = "laravel11+";
2210
+ }
2211
+ const registration = generateProviderRegistration(existingContent, laravelVersion);
2212
+ if (registration && !registration.alreadyRegistered) {
2213
+ outputs.push({
2214
+ path: registration.path,
2215
+ content: registration.content,
2216
+ type: "other",
2217
+ skipIfExists: false,
2218
+ // We want to modify the file
2219
+ metadata: {
2220
+ registrationType: "provider-registration",
2221
+ laravelVersion: registration.laravelVersion
2222
+ }
2223
+ });
2224
+ ctx.logger.info(`OmnifyServiceProvider will be registered in ${registration.path}`);
2225
+ } else if (registration?.alreadyRegistered) {
2226
+ ctx.logger.info("OmnifyServiceProvider is already registered");
2227
+ }
2228
+ return outputs;
2229
+ }
2230
+ };
2231
+ const factoryGenerator = {
2232
+ name: "laravel-factories",
2233
+ description: "Generate Laravel factory classes for testing",
2234
+ generate: async (ctx) => {
2235
+ const factoryOptions = {
2236
+ modelNamespace: resolved.modelNamespace,
2237
+ factoryPath: resolved.factoriesPath,
2238
+ fakerLocale: resolved.fakerLocale
2239
+ };
2240
+ const factories = generateFactories(ctx.schemas, factoryOptions);
2241
+ return factories.map((factory) => ({
2242
+ path: getFactoryPath(factory),
2243
+ content: factory.content,
2244
+ type: "factory",
2245
+ // Skip writing factories if they already exist (allow customization)
2246
+ skipIfExists: !factory.overwrite,
2247
+ metadata: {
2248
+ factoryName: factory.name,
2249
+ schemaName: factory.schemaName
2250
+ }
2251
+ }));
1717
2252
  }
1718
2253
  };
2254
+ const generators = [migrationGenerator];
2255
+ if (resolved.generateModels) {
2256
+ generators.push(modelGenerator);
2257
+ }
2258
+ if (resolved.generateFactories) {
2259
+ generators.push(factoryGenerator);
2260
+ }
1719
2261
  return {
1720
2262
  name: "@famgia/omnify-laravel",
1721
2263
  version: "0.0.14",
1722
2264
  configSchema: LARAVEL_CONFIG_SCHEMA,
1723
- generators: resolved.generateModels ? [migrationGenerator, modelGenerator] : [migrationGenerator]
2265
+ generators
1724
2266
  };
1725
2267
  }
1726
2268
  // Annotate the CommonJS export names for ESM import in node: