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