@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.
@@ -1,3 +1,7 @@
1
+ // src/plugin.ts
2
+ import { readFileSync, existsSync } from "fs";
3
+ import { join } from "path";
4
+
1
5
  // src/migration/schema-builder.ts
2
6
  var TYPE_METHOD_MAP = {
3
7
  String: "string",
@@ -76,9 +80,8 @@ function propertyToColumnMethod(propertyName, property) {
76
80
  if (baseProp.unique) {
77
81
  modifiers.push({ method: "unique" });
78
82
  }
79
- if (baseProp.default !== void 0) {
80
- const defaultValue = typeof baseProp.default === "string" ? baseProp.default : JSON.stringify(baseProp.default);
81
- modifiers.push({ method: "default", args: [defaultValue] });
83
+ if (baseProp.default !== void 0 && baseProp.default !== null) {
84
+ modifiers.push({ method: "default", args: [baseProp.default] });
82
85
  }
83
86
  if (baseProp.unsigned && (method === "integer" || method === "bigInteger")) {
84
87
  modifiers.push({ method: "unsigned" });
@@ -314,6 +317,12 @@ function formatColumnMethod(column) {
314
317
  if (typeof arg === "string") {
315
318
  return `'${arg}'`;
316
319
  }
320
+ if (typeof arg === "boolean") {
321
+ return arg ? "true" : "false";
322
+ }
323
+ if (typeof arg === "number") {
324
+ return String(arg);
325
+ }
317
326
  return String(arg);
318
327
  }).join(", ");
319
328
  code += `->${modifier.method}(${modArgs})`;
@@ -421,6 +430,7 @@ function generatePivotTableBlueprint(pivot) {
421
430
  args: [pivot.targetColumn],
422
431
  modifiers: []
423
432
  });
433
+ columns.push(...generateTimestampColumns());
424
434
  foreignKeys.push({
425
435
  columns: [pivot.sourceColumn],
426
436
  references: "id",
@@ -1150,7 +1160,7 @@ function generateEntityBaseModel(schema, schemas, options, stubContent, authStub
1150
1160
  if (assoc.target) {
1151
1161
  imports.push(`use ${options.modelNamespace}\\${toPascalCase(assoc.target)};`);
1152
1162
  }
1153
- relations.push(generateRelation(propName, assoc, options));
1163
+ relations.push(generateRelation(propName, assoc, schema, schemas, options));
1154
1164
  if (assoc.relation === "ManyToOne" || assoc.relation === "OneToOne") {
1155
1165
  if (!assoc.mappedBy) {
1156
1166
  const fkName = toSnakeCase(propName) + "_id";
@@ -1207,7 +1217,22 @@ ${docProperties.join("\n")}
1207
1217
  schemaName: schema.name
1208
1218
  };
1209
1219
  }
1210
- function generateRelation(propName, assoc, options) {
1220
+ function findInverseRelation(currentSchemaName, targetSchemaName, schemas) {
1221
+ const targetSchema = schemas[targetSchemaName];
1222
+ if (!targetSchema || !targetSchema.properties) {
1223
+ return null;
1224
+ }
1225
+ for (const [propName, propDef] of Object.entries(targetSchema.properties)) {
1226
+ if (propDef.type === "Association") {
1227
+ const assoc = propDef;
1228
+ if (assoc.relation === "ManyToOne" && assoc.target === currentSchemaName) {
1229
+ return propName;
1230
+ }
1231
+ }
1232
+ }
1233
+ return null;
1234
+ }
1235
+ function generateRelation(propName, assoc, schema, schemas, options) {
1211
1236
  const methodName = toCamelCase(propName);
1212
1237
  const targetClass = assoc.target ? toPascalCase(assoc.target) : "";
1213
1238
  const fkName = toSnakeCase(propName) + "_id";
@@ -1237,14 +1262,28 @@ function generateRelation(propName, assoc, options) {
1237
1262
  {
1238
1263
  return $this->belongsTo(${targetClass}::class, '${fkName}');
1239
1264
  }`;
1240
- case "OneToMany":
1265
+ case "OneToMany": {
1266
+ let foreignKey;
1267
+ if (assoc.inversedBy) {
1268
+ foreignKey = toSnakeCase(assoc.inversedBy) + "_id";
1269
+ } else if (assoc.target) {
1270
+ const inverseRelation = findInverseRelation(schema.name, assoc.target, schemas);
1271
+ if (inverseRelation) {
1272
+ foreignKey = toSnakeCase(inverseRelation) + "_id";
1273
+ } else {
1274
+ foreignKey = toSnakeCase(schema.name) + "_id";
1275
+ }
1276
+ } else {
1277
+ foreignKey = toSnakeCase(propName) + "_id";
1278
+ }
1241
1279
  return ` /**
1242
1280
  * Get the ${propName} for this model.
1243
1281
  */
1244
1282
  public function ${methodName}(): HasMany
1245
1283
  {
1246
- return $this->hasMany(${targetClass}::class, '${toSnakeCase(assoc.inversedBy ?? propName)}_id');
1284
+ return $this->hasMany(${targetClass}::class, '${foreignKey}');
1247
1285
  }`;
1286
+ }
1248
1287
  case "ManyToMany": {
1249
1288
  const pivotTable = assoc.joinTable ?? `${toSnakeCase(propName)}_pivot`;
1250
1289
  return ` /**
@@ -1541,14 +1580,73 @@ class {{CLASS_NAME}} extends {{CLASS_NAME}}BaseModel
1541
1580
 
1542
1581
  // Add your custom methods here
1543
1582
  }
1583
+ `,
1584
+ "service-provider": `<?php
1585
+
1586
+ namespace App\\Providers;
1587
+
1588
+ use Illuminate\\Database\\Eloquent\\Relations\\Relation;
1589
+ use Illuminate\\Support\\ServiceProvider;
1590
+
1591
+ /**
1592
+ * Omnify Service Provider
1593
+ *
1594
+ * DO NOT EDIT - This file is auto-generated by Omnify.
1595
+ * Any changes will be overwritten on next generation.
1596
+ *
1597
+ * - Loads Omnify migrations from database/migrations/omnify
1598
+ * - Registers morph map for polymorphic relationships
1599
+ *
1600
+ * @generated by @famgia/omnify-laravel
1601
+ */
1602
+ class OmnifyServiceProvider extends ServiceProvider
1603
+ {
1604
+ /**
1605
+ * Register any application services.
1606
+ */
1607
+ public function register(): void
1608
+ {
1609
+ //
1610
+ }
1611
+
1612
+ /**
1613
+ * Bootstrap any application services.
1614
+ */
1615
+ public function boot(): void
1616
+ {
1617
+ // Load Omnify migrations from custom directory
1618
+ $this->loadMigrationsFrom(database_path('migrations/omnify'));
1619
+
1620
+ // Register morph map for polymorphic relationships
1621
+ Relation::enforceMorphMap([
1622
+ {{MORPH_MAP}}
1623
+ ]);
1624
+ }
1625
+ }
1544
1626
  `
1545
1627
  };
1546
1628
  return stubs[stubName] ?? "";
1547
1629
  }
1630
+ function generateServiceProvider(schemas, options, stubContent) {
1631
+ const morphMap = Object.values(schemas).filter((s) => s.kind !== "enum").map((s) => {
1632
+ const className = toPascalCase(s.name);
1633
+ return ` '${s.name}' => \\${options.modelNamespace}\\${className}::class,`;
1634
+ }).join("\n");
1635
+ const content = stubContent.replace(/\{\{MORPH_MAP\}\}/g, morphMap);
1636
+ return {
1637
+ path: "app/Providers/OmnifyServiceProvider.php",
1638
+ content,
1639
+ type: "service-provider",
1640
+ overwrite: true,
1641
+ // Always overwrite to keep morph map in sync
1642
+ schemaName: "__service_provider__"
1643
+ };
1644
+ }
1548
1645
  function generateModels(schemas, options) {
1549
1646
  const resolved = resolveOptions(options);
1550
1647
  const models = [];
1551
1648
  models.push(generateBaseModel(schemas, resolved, getStubContent("base-model")));
1649
+ models.push(generateServiceProvider(schemas, resolved, getStubContent("service-provider")));
1552
1650
  for (const schema of Object.values(schemas)) {
1553
1651
  if (schema.kind === "enum") {
1554
1652
  continue;
@@ -1567,6 +1665,355 @@ function generateModels(schemas, options) {
1567
1665
  function getModelPath(model) {
1568
1666
  return model.path;
1569
1667
  }
1668
+ function generateProviderRegistration(existingContent, laravelVersion) {
1669
+ const providerClass = "App\\Providers\\OmnifyServiceProvider::class";
1670
+ const providerLine = ` ${providerClass},`;
1671
+ if (existingContent && existingContent.includes("OmnifyServiceProvider")) {
1672
+ return {
1673
+ path: laravelVersion === "laravel11+" ? "bootstrap/providers.php" : "config/app.php",
1674
+ content: existingContent,
1675
+ laravelVersion,
1676
+ alreadyRegistered: true
1677
+ };
1678
+ }
1679
+ if (laravelVersion === "laravel11+") {
1680
+ if (existingContent) {
1681
+ const lines = existingContent.split("\n");
1682
+ const result = [];
1683
+ let inserted = false;
1684
+ for (let i = 0; i < lines.length; i++) {
1685
+ const line = lines[i];
1686
+ if (!inserted && line.trim() === "];") {
1687
+ result.push(providerLine);
1688
+ inserted = true;
1689
+ }
1690
+ result.push(line);
1691
+ }
1692
+ return {
1693
+ path: "bootstrap/providers.php",
1694
+ content: result.join("\n"),
1695
+ laravelVersion,
1696
+ alreadyRegistered: false
1697
+ };
1698
+ } else {
1699
+ return {
1700
+ path: "bootstrap/providers.php",
1701
+ content: `<?php
1702
+
1703
+ return [
1704
+ App\\Providers\\AppServiceProvider::class,
1705
+ ${providerLine}
1706
+ ];
1707
+ `,
1708
+ laravelVersion,
1709
+ alreadyRegistered: false
1710
+ };
1711
+ }
1712
+ } else {
1713
+ if (existingContent) {
1714
+ const providersSectionRegex = /'providers'\s*=>\s*\[[\s\S]*?\n(\s*)\]/m;
1715
+ const match = existingContent.match(providersSectionRegex);
1716
+ if (match) {
1717
+ const providersStart = existingContent.indexOf("'providers'");
1718
+ if (providersStart === -1) {
1719
+ return null;
1720
+ }
1721
+ let depth = 0;
1722
+ let foundStart = false;
1723
+ let insertPos = -1;
1724
+ for (let i = providersStart; i < existingContent.length; i++) {
1725
+ const char = existingContent[i];
1726
+ if (char === "[") {
1727
+ foundStart = true;
1728
+ depth++;
1729
+ } else if (char === "]") {
1730
+ depth--;
1731
+ if (foundStart && depth === 0) {
1732
+ insertPos = i;
1733
+ break;
1734
+ }
1735
+ }
1736
+ }
1737
+ if (insertPos !== -1) {
1738
+ const beforeClose = existingContent.substring(0, insertPos);
1739
+ const lastNewline = beforeClose.lastIndexOf("\n");
1740
+ const content = existingContent.substring(0, lastNewline + 1) + providerLine + "\n" + existingContent.substring(lastNewline + 1);
1741
+ return {
1742
+ path: "config/app.php",
1743
+ content,
1744
+ laravelVersion,
1745
+ alreadyRegistered: false
1746
+ };
1747
+ }
1748
+ }
1749
+ return null;
1750
+ } else {
1751
+ return null;
1752
+ }
1753
+ }
1754
+ }
1755
+
1756
+ // src/factory/generator.ts
1757
+ function resolveOptions2(options) {
1758
+ return {
1759
+ modelNamespace: options?.modelNamespace ?? "App\\Models",
1760
+ factoryPath: options?.factoryPath ?? "database/factories",
1761
+ fakerLocale: options?.fakerLocale ?? "en_US"
1762
+ };
1763
+ }
1764
+ function getStubContent2() {
1765
+ return `<?php
1766
+
1767
+ namespace Database\\Factories;
1768
+
1769
+ use {{MODEL_NAMESPACE}}\\{{MODEL_NAME}};
1770
+ use Illuminate\\Database\\Eloquent\\Factories\\Factory;
1771
+ {{IMPORTS}}
1772
+
1773
+ /**
1774
+ * @extends Factory<{{MODEL_NAME}}>
1775
+ */
1776
+ class {{MODEL_NAME}}Factory extends Factory
1777
+ {
1778
+ protected $model = {{MODEL_NAME}}::class;
1779
+
1780
+ /**
1781
+ * Define the model's default state.
1782
+ *
1783
+ * @return array<string, mixed>
1784
+ */
1785
+ public function definition(): array
1786
+ {
1787
+ return [
1788
+ {{ATTRIBUTES}}
1789
+ ];
1790
+ }
1791
+ }
1792
+ `;
1793
+ }
1794
+ function generateFakeData(propertyName, property, schema, schemas) {
1795
+ const type = property.type;
1796
+ if (["deleted_at", "created_at", "updated_at"].includes(propertyName)) {
1797
+ return null;
1798
+ }
1799
+ if (type === "Association") {
1800
+ return null;
1801
+ }
1802
+ switch (type) {
1803
+ case "String":
1804
+ return generateStringFake(propertyName, property);
1805
+ case "Email":
1806
+ return `'${propertyName}' => fake()->unique()->safeEmail(),`;
1807
+ case "Password":
1808
+ return `'${propertyName}' => bcrypt('password'),`;
1809
+ case "Int":
1810
+ case "BigInt":
1811
+ return generateIntFake(propertyName, property);
1812
+ case "Float":
1813
+ case "Decimal":
1814
+ return `'${propertyName}' => fake()->randomFloat(2, 1, 10000),`;
1815
+ case "Boolean":
1816
+ return `'${propertyName}' => fake()->boolean(),`;
1817
+ case "Text":
1818
+ return `'${propertyName}' => fake()->paragraphs(3, true),`;
1819
+ case "LongText":
1820
+ return `'${propertyName}' => fake()->paragraphs(5, true),`;
1821
+ case "Date":
1822
+ return `'${propertyName}' => fake()->date(),`;
1823
+ case "Time":
1824
+ return `'${propertyName}' => fake()->time(),`;
1825
+ case "Timestamp":
1826
+ case "DateTime":
1827
+ return `'${propertyName}' => fake()->dateTime(),`;
1828
+ case "Json":
1829
+ return `'${propertyName}' => [],`;
1830
+ case "Enum":
1831
+ return generateEnumFake(propertyName, property);
1832
+ case "EnumRef":
1833
+ return generateEnumRefFake(propertyName, property, schemas);
1834
+ default:
1835
+ return `'${propertyName}' => fake()->sentence(),`;
1836
+ }
1837
+ }
1838
+ function generateStringFake(propertyName, property) {
1839
+ if (propertyName === "slug") {
1840
+ return `'${propertyName}' => fake()->unique()->slug(),`;
1841
+ }
1842
+ if (propertyName === "uuid" || propertyName === "uid") {
1843
+ return `'${propertyName}' => (string) \\Illuminate\\Support\\Str::uuid(),`;
1844
+ }
1845
+ if (propertyName.includes("email")) {
1846
+ return `'${propertyName}' => fake()->unique()->safeEmail(),`;
1847
+ }
1848
+ if (propertyName.includes("phone")) {
1849
+ return `'${propertyName}' => fake()->phoneNumber(),`;
1850
+ }
1851
+ if (propertyName.includes("image") || propertyName.includes("photo") || propertyName.includes("avatar")) {
1852
+ return `'${propertyName}' => fake()->imageUrl(),`;
1853
+ }
1854
+ if (propertyName.includes("url") || propertyName.includes("website")) {
1855
+ return `'${propertyName}' => fake()->url(),`;
1856
+ }
1857
+ if (propertyName.includes("path") || propertyName.includes("file")) {
1858
+ return `'${propertyName}' => 'uploads/' . fake()->uuid() . '.jpg',`;
1859
+ }
1860
+ if (propertyName === "name" || propertyName === "title") {
1861
+ return `'${propertyName}' => fake()->sentence(3),`;
1862
+ }
1863
+ if (propertyName.includes("name")) {
1864
+ return `'${propertyName}' => fake()->name(),`;
1865
+ }
1866
+ if (propertyName.includes("address")) {
1867
+ return `'${propertyName}' => fake()->address(),`;
1868
+ }
1869
+ if (propertyName.includes("city")) {
1870
+ return `'${propertyName}' => fake()->city(),`;
1871
+ }
1872
+ if (propertyName.includes("country")) {
1873
+ return `'${propertyName}' => fake()->country(),`;
1874
+ }
1875
+ if (propertyName.includes("zip") || propertyName.includes("postal")) {
1876
+ return `'${propertyName}' => fake()->postcode(),`;
1877
+ }
1878
+ if (propertyName.includes("color")) {
1879
+ return `'${propertyName}' => fake()->hexColor(),`;
1880
+ }
1881
+ if (propertyName.includes("token") || propertyName.includes("secret") || propertyName.includes("key")) {
1882
+ return `'${propertyName}' => \\Illuminate\\Support\\Str::random(32),`;
1883
+ }
1884
+ if (propertyName.includes("code")) {
1885
+ return `'${propertyName}' => fake()->unique()->regexify('[A-Z0-9]{8}'),`;
1886
+ }
1887
+ const length = property.length;
1888
+ if (length && length <= 50) {
1889
+ return `'${propertyName}' => fake()->words(3, true),`;
1890
+ }
1891
+ return `'${propertyName}' => fake()->sentence(),`;
1892
+ }
1893
+ function generateIntFake(propertyName, property) {
1894
+ if (propertyName.includes("count") || propertyName.includes("quantity")) {
1895
+ return `'${propertyName}' => fake()->numberBetween(0, 100),`;
1896
+ }
1897
+ if (propertyName.includes("price") || propertyName.includes("amount") || propertyName.includes("cost")) {
1898
+ return `'${propertyName}' => fake()->numberBetween(100, 10000),`;
1899
+ }
1900
+ if (propertyName.includes("order") || propertyName.includes("sort") || propertyName.includes("position")) {
1901
+ return `'${propertyName}' => fake()->numberBetween(1, 100),`;
1902
+ }
1903
+ if (propertyName.includes("age")) {
1904
+ return `'${propertyName}' => fake()->numberBetween(18, 80),`;
1905
+ }
1906
+ if (propertyName.includes("year")) {
1907
+ return `'${propertyName}' => fake()->year(),`;
1908
+ }
1909
+ return `'${propertyName}' => fake()->numberBetween(1, 1000),`;
1910
+ }
1911
+ function generateEnumFake(propertyName, property) {
1912
+ const enumValues = property.enum;
1913
+ if (!enumValues || enumValues.length === 0) {
1914
+ return `'${propertyName}' => null,`;
1915
+ }
1916
+ const values = enumValues.map((v) => typeof v === "string" ? v : v.value);
1917
+ const valuesStr = values.map((v) => `'${v}'`).join(", ");
1918
+ return `'${propertyName}' => fake()->randomElement([${valuesStr}]),`;
1919
+ }
1920
+ function generateEnumRefFake(propertyName, property, schemas) {
1921
+ const enumName = property.enum;
1922
+ if (!enumName) {
1923
+ return `'${propertyName}' => null,`;
1924
+ }
1925
+ const enumSchema = schemas[enumName];
1926
+ if (!enumSchema || enumSchema.kind !== "enum" || !enumSchema.values) {
1927
+ return `'${propertyName}' => null,`;
1928
+ }
1929
+ const valuesStr = enumSchema.values.map((v) => `'${v}'`).join(", ");
1930
+ return `'${propertyName}' => fake()->randomElement([${valuesStr}]),`;
1931
+ }
1932
+ function generateAssociationFake(propertyName, property, schema, schemas, modelNamespace) {
1933
+ if (property.type !== "Association") {
1934
+ return null;
1935
+ }
1936
+ const relation = property.relation;
1937
+ const target = property.target;
1938
+ if (relation !== "ManyToOne" || !target) {
1939
+ return null;
1940
+ }
1941
+ const foreignKey = `${toSnakeCase(propertyName)}_id`;
1942
+ const isNullable2 = property.nullable ?? false;
1943
+ const targetSchema = schemas[target];
1944
+ if (!targetSchema) {
1945
+ return null;
1946
+ }
1947
+ let fake;
1948
+ if (isNullable2) {
1949
+ fake = `'${foreignKey}' => ${target}::query()->inRandomOrder()->first()?->id,`;
1950
+ } else {
1951
+ fake = `'${foreignKey}' => ${target}::query()->inRandomOrder()->first()?->id ?? ${target}::factory()->create()->id,`;
1952
+ }
1953
+ let importStatement;
1954
+ if (target !== schema.name) {
1955
+ importStatement = `use ${modelNamespace}\\${target};`;
1956
+ }
1957
+ return { fake, import: importStatement };
1958
+ }
1959
+ function generateFactory(schema, schemas, options, stubContent) {
1960
+ if (schema.kind === "enum") {
1961
+ return null;
1962
+ }
1963
+ const modelName = toPascalCase(schema.name);
1964
+ const factoryName = `${modelName}Factory`;
1965
+ const attributes = [];
1966
+ const imports = [];
1967
+ if (schema.properties) {
1968
+ for (const [propName, prop] of Object.entries(schema.properties)) {
1969
+ if (prop.type === "Association") {
1970
+ const assocResult = generateAssociationFake(propName, prop, schema, schemas, options.modelNamespace);
1971
+ if (assocResult) {
1972
+ attributes.push(assocResult.fake);
1973
+ if (assocResult.import) {
1974
+ imports.push(assocResult.import);
1975
+ }
1976
+ }
1977
+ continue;
1978
+ }
1979
+ const fake = generateFakeData(propName, prop, schema, schemas);
1980
+ if (fake) {
1981
+ attributes.push(fake);
1982
+ }
1983
+ }
1984
+ }
1985
+ let content = stubContent;
1986
+ content = content.replace(/\{\{MODEL_NAMESPACE\}\}/g, options.modelNamespace);
1987
+ content = content.replace(/\{\{MODEL_NAME\}\}/g, modelName);
1988
+ const uniqueImports = [...new Set(imports)];
1989
+ const importsStr = uniqueImports.length > 0 ? "\n" + uniqueImports.join("\n") : "";
1990
+ content = content.replace(/\{\{IMPORTS\}\}/g, importsStr);
1991
+ const attributesStr = attributes.length > 0 ? attributes.map((a) => ` ${a}`).join("\n") : "";
1992
+ content = content.replace(/\{\{ATTRIBUTES\}\}/g, attributesStr);
1993
+ return {
1994
+ name: factoryName,
1995
+ schemaName: schema.name,
1996
+ path: `${options.factoryPath}/${factoryName}.php`,
1997
+ content,
1998
+ overwrite: false
1999
+ // Factories should not overwrite existing files
2000
+ };
2001
+ }
2002
+ function generateFactories(schemas, options) {
2003
+ const resolved = resolveOptions2(options);
2004
+ const stubContent = getStubContent2();
2005
+ const factories = [];
2006
+ for (const schema of Object.values(schemas)) {
2007
+ const factory = generateFactory(schema, schemas, resolved, stubContent);
2008
+ if (factory) {
2009
+ factories.push(factory);
2010
+ }
2011
+ }
2012
+ return factories;
2013
+ }
2014
+ function getFactoryPath(factory) {
2015
+ return factory.path;
2016
+ }
1570
2017
 
1571
2018
  // src/plugin.ts
1572
2019
  var LARAVEL_CONFIG_SCHEMA = {
@@ -1575,8 +2022,8 @@ var LARAVEL_CONFIG_SCHEMA = {
1575
2022
  key: "migrationsPath",
1576
2023
  type: "path",
1577
2024
  label: "Migrations Path",
1578
- description: "Directory for Laravel migration files",
1579
- default: "database/migrations",
2025
+ description: "Directory for Laravel migration files (loaded via OmnifyServiceProvider)",
2026
+ default: "database/migrations/omnify",
1580
2027
  group: "output"
1581
2028
  },
1582
2029
  {
@@ -1603,6 +2050,22 @@ var LARAVEL_CONFIG_SCHEMA = {
1603
2050
  default: true,
1604
2051
  group: "options"
1605
2052
  },
2053
+ {
2054
+ key: "factoriesPath",
2055
+ type: "path",
2056
+ label: "Factories Path",
2057
+ description: "Directory for Laravel factory files",
2058
+ default: "database/factories",
2059
+ group: "output"
2060
+ },
2061
+ {
2062
+ key: "generateFactories",
2063
+ type: "boolean",
2064
+ label: "Generate Factories",
2065
+ description: "Generate Laravel factory classes for testing",
2066
+ default: true,
2067
+ group: "options"
2068
+ },
1606
2069
  {
1607
2070
  key: "connection",
1608
2071
  type: "string",
@@ -1613,20 +2076,23 @@ var LARAVEL_CONFIG_SCHEMA = {
1613
2076
  }
1614
2077
  ]
1615
2078
  };
1616
- function resolveOptions2(options) {
2079
+ function resolveOptions3(options) {
1617
2080
  return {
1618
- migrationsPath: options?.migrationsPath ?? "database/migrations",
2081
+ migrationsPath: options?.migrationsPath ?? "database/migrations/omnify",
1619
2082
  modelsPath: options?.modelsPath ?? "app/Models",
1620
2083
  baseModelsPath: options?.baseModelsPath ?? "app/Models/OmnifyBase",
1621
2084
  modelNamespace: options?.modelNamespace ?? "App\\Models",
1622
2085
  baseModelNamespace: options?.baseModelNamespace ?? "App\\Models\\OmnifyBase",
1623
2086
  generateModels: options?.generateModels ?? true,
2087
+ factoriesPath: options?.factoriesPath ?? "database/factories",
2088
+ generateFactories: options?.generateFactories ?? true,
2089
+ fakerLocale: options?.fakerLocale ?? "en_US",
1624
2090
  connection: options?.connection,
1625
2091
  timestamp: options?.timestamp
1626
2092
  };
1627
2093
  }
1628
2094
  function laravelPlugin(options) {
1629
- const resolved = resolveOptions2(options);
2095
+ const resolved = resolveOptions3(options);
1630
2096
  const migrationGenerator = {
1631
2097
  name: "laravel-migrations",
1632
2098
  description: "Generate Laravel migration files",
@@ -1658,7 +2124,7 @@ function laravelPlugin(options) {
1658
2124
  baseModelPath: resolved.baseModelsPath
1659
2125
  };
1660
2126
  const models = generateModels(ctx.schemas, modelOptions);
1661
- return models.map((model) => ({
2127
+ const outputs = models.map((model) => ({
1662
2128
  path: getModelPath(model),
1663
2129
  content: model.content,
1664
2130
  type: "model",
@@ -1669,13 +2135,82 @@ function laravelPlugin(options) {
1669
2135
  schemaName: model.schemaName
1670
2136
  }
1671
2137
  }));
2138
+ const bootstrapProvidersPath = join(ctx.cwd, "bootstrap/providers.php");
2139
+ const configAppPath = join(ctx.cwd, "config/app.php");
2140
+ let existingContent = null;
2141
+ let laravelVersion;
2142
+ if (existsSync(bootstrapProvidersPath)) {
2143
+ laravelVersion = "laravel11+";
2144
+ try {
2145
+ existingContent = readFileSync(bootstrapProvidersPath, "utf-8");
2146
+ } catch {
2147
+ existingContent = null;
2148
+ }
2149
+ } else if (existsSync(configAppPath)) {
2150
+ laravelVersion = "laravel10-";
2151
+ try {
2152
+ existingContent = readFileSync(configAppPath, "utf-8");
2153
+ } catch {
2154
+ existingContent = null;
2155
+ }
2156
+ } else {
2157
+ laravelVersion = "laravel11+";
2158
+ }
2159
+ const registration = generateProviderRegistration(existingContent, laravelVersion);
2160
+ if (registration && !registration.alreadyRegistered) {
2161
+ outputs.push({
2162
+ path: registration.path,
2163
+ content: registration.content,
2164
+ type: "other",
2165
+ skipIfExists: false,
2166
+ // We want to modify the file
2167
+ metadata: {
2168
+ registrationType: "provider-registration",
2169
+ laravelVersion: registration.laravelVersion
2170
+ }
2171
+ });
2172
+ ctx.logger.info(`OmnifyServiceProvider will be registered in ${registration.path}`);
2173
+ } else if (registration?.alreadyRegistered) {
2174
+ ctx.logger.info("OmnifyServiceProvider is already registered");
2175
+ }
2176
+ return outputs;
2177
+ }
2178
+ };
2179
+ const factoryGenerator = {
2180
+ name: "laravel-factories",
2181
+ description: "Generate Laravel factory classes for testing",
2182
+ generate: async (ctx) => {
2183
+ const factoryOptions = {
2184
+ modelNamespace: resolved.modelNamespace,
2185
+ factoryPath: resolved.factoriesPath,
2186
+ fakerLocale: resolved.fakerLocale
2187
+ };
2188
+ const factories = generateFactories(ctx.schemas, factoryOptions);
2189
+ return factories.map((factory) => ({
2190
+ path: getFactoryPath(factory),
2191
+ content: factory.content,
2192
+ type: "factory",
2193
+ // Skip writing factories if they already exist (allow customization)
2194
+ skipIfExists: !factory.overwrite,
2195
+ metadata: {
2196
+ factoryName: factory.name,
2197
+ schemaName: factory.schemaName
2198
+ }
2199
+ }));
1672
2200
  }
1673
2201
  };
2202
+ const generators = [migrationGenerator];
2203
+ if (resolved.generateModels) {
2204
+ generators.push(modelGenerator);
2205
+ }
2206
+ if (resolved.generateFactories) {
2207
+ generators.push(factoryGenerator);
2208
+ }
1674
2209
  return {
1675
2210
  name: "@famgia/omnify-laravel",
1676
2211
  version: "0.0.14",
1677
2212
  configSchema: LARAVEL_CONFIG_SCHEMA,
1678
- generators: resolved.generateModels ? [migrationGenerator, modelGenerator] : [migrationGenerator]
2213
+ generators
1679
2214
  };
1680
2215
  }
1681
2216
 
@@ -1701,4 +2236,4 @@ export {
1701
2236
  generateMigrationsFromChanges,
1702
2237
  laravelPlugin
1703
2238
  };
1704
- //# sourceMappingURL=chunk-UVF7W2J2.js.map
2239
+ //# sourceMappingURL=chunk-REDFZUQY.js.map