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