@famgia/omnify-laravel 0.0.79 → 0.0.81

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.d.cts CHANGED
@@ -354,6 +354,8 @@ interface FactoryGeneratorOptions {
354
354
  factoryPath?: string;
355
355
  /** Faker locale */
356
356
  fakerLocale?: string;
357
+ /** Custom types registered by plugins */
358
+ customTypes?: ReadonlyMap<string, CustomTypeDefinition>;
357
359
  }
358
360
  /**
359
361
  * Generated factory output.
package/dist/index.d.ts CHANGED
@@ -354,6 +354,8 @@ interface FactoryGeneratorOptions {
354
354
  factoryPath?: string;
355
355
  /** Faker locale */
356
356
  fakerLocale?: string;
357
+ /** Custom types registered by plugins */
358
+ customTypes?: ReadonlyMap<string, CustomTypeDefinition>;
357
359
  }
358
360
  /**
359
361
  * Generated factory output.
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  schemaToBlueprint,
25
25
  toColumnName,
26
26
  toTableName
27
- } from "./chunk-6QYGCK3D.js";
27
+ } from "./chunk-WFEVU3LX.js";
28
28
  export {
29
29
  formatColumnMethod,
30
30
  formatForeignKey,
package/dist/plugin.cjs CHANGED
@@ -2237,7 +2237,8 @@ function resolveOptions2(options) {
2237
2237
  return {
2238
2238
  modelNamespace: options?.modelNamespace ?? "App\\Models",
2239
2239
  factoryPath: options?.factoryPath ?? "database/factories",
2240
- fakerLocale: options?.fakerLocale ?? "en_US"
2240
+ fakerLocale: options?.fakerLocale ?? "en_US",
2241
+ customTypes: options?.customTypes ?? /* @__PURE__ */ new Map()
2241
2242
  };
2242
2243
  }
2243
2244
  function getStubContent2() {
@@ -2270,7 +2271,63 @@ class {{MODEL_NAME}}Factory extends Factory
2270
2271
  }
2271
2272
  `;
2272
2273
  }
2273
- function generateFakeData(propertyName, property, schema, schemas) {
2274
+ function generateJapaneseCompoundFake(propertyName, property, options) {
2275
+ const typeDef = options.customTypes.get(property.type);
2276
+ if (!typeDef || !typeDef.compound || !typeDef.expand) {
2277
+ return null;
2278
+ }
2279
+ const snakeName = toSnakeCase(propertyName);
2280
+ const lines = [];
2281
+ const jaFaker = "fake('ja_JP')";
2282
+ switch (property.type) {
2283
+ case "JapaneseName":
2284
+ lines.push(`'${snakeName}_lastname' => ${jaFaker}->lastName(),`);
2285
+ lines.push(`'${snakeName}_firstname' => ${jaFaker}->firstName(),`);
2286
+ lines.push(`'${snakeName}_kana_lastname' => ${jaFaker}->lastKanaName(),`);
2287
+ lines.push(`'${snakeName}_kana_firstname' => ${jaFaker}->firstKanaName(),`);
2288
+ break;
2289
+ case "JapaneseAddress":
2290
+ lines.push(`'${snakeName}_postal_code' => ${jaFaker}->postcode(),`);
2291
+ lines.push(`'${snakeName}_prefecture_id' => ${jaFaker}->numberBetween(1, 47),`);
2292
+ lines.push(`'${snakeName}_address1' => ${jaFaker}->city() . ${jaFaker}->ward(),`);
2293
+ lines.push(`'${snakeName}_address2' => ${jaFaker}->streetAddress(),`);
2294
+ lines.push(`'${snakeName}_address3' => ${jaFaker}->optional()->secondaryAddress(),`);
2295
+ break;
2296
+ case "JapaneseBankAccount":
2297
+ lines.push(`'${snakeName}_bank_code' => ${jaFaker}->regexify('[0-9]{4}'),`);
2298
+ lines.push(`'${snakeName}_branch_code' => ${jaFaker}->regexify('[0-9]{3}'),`);
2299
+ lines.push(`'${snakeName}_account_type' => ${jaFaker}->randomElement(['1', '2', '4']),`);
2300
+ lines.push(`'${snakeName}_account_number' => ${jaFaker}->regexify('[0-9]{7}'),`);
2301
+ lines.push(`'${snakeName}_account_holder' => ${jaFaker}->lastKanaName() . ' ' . ${jaFaker}->firstKanaName(),`);
2302
+ break;
2303
+ default:
2304
+ for (const field of typeDef.expand) {
2305
+ const suffixSnake = toSnakeCase(field.suffix);
2306
+ const fieldName = `${snakeName}_${suffixSnake}`;
2307
+ const sql = field.sql;
2308
+ if (sql?.sqlType === "TINYINT" || sql?.sqlType === "INT" || sql?.sqlType === "BIGINT") {
2309
+ lines.push(`'${fieldName}' => fake()->numberBetween(1, 100),`);
2310
+ } else {
2311
+ lines.push(`'${fieldName}' => fake()->words(2, true),`);
2312
+ }
2313
+ }
2314
+ break;
2315
+ }
2316
+ return lines;
2317
+ }
2318
+ function generateJapaneseSimpleFake(propertyName, property) {
2319
+ const snakeName = toSnakeCase(propertyName);
2320
+ const jaFaker = "fake('ja_JP')";
2321
+ switch (property.type) {
2322
+ case "JapanesePhone":
2323
+ return `'${snakeName}' => ${jaFaker}->phoneNumber(),`;
2324
+ case "JapanesePostalCode":
2325
+ return `'${snakeName}' => ${jaFaker}->postcode(),`;
2326
+ default:
2327
+ return null;
2328
+ }
2329
+ }
2330
+ function generateFakeData(propertyName, property, schema, schemas, options) {
2274
2331
  const type = property.type;
2275
2332
  if (["deleted_at", "created_at", "updated_at"].includes(propertyName)) {
2276
2333
  return null;
@@ -2278,6 +2335,10 @@ function generateFakeData(propertyName, property, schema, schemas) {
2278
2335
  if (type === "Association") {
2279
2336
  return null;
2280
2337
  }
2338
+ const japaneseFake = generateJapaneseSimpleFake(propertyName, property);
2339
+ if (japaneseFake) {
2340
+ return japaneseFake;
2341
+ }
2281
2342
  switch (type) {
2282
2343
  case "String":
2283
2344
  return generateStringFake(propertyName, property);
@@ -2458,7 +2519,12 @@ function generateFactory(schema, schemas, options, stubContent) {
2458
2519
  }
2459
2520
  continue;
2460
2521
  }
2461
- const fake = generateFakeData(propName, prop, schema, schemas);
2522
+ const compoundFakes = generateJapaneseCompoundFake(propName, prop, options);
2523
+ if (compoundFakes) {
2524
+ attributes.push(...compoundFakes);
2525
+ continue;
2526
+ }
2527
+ const fake = generateFakeData(propName, prop, schema, schemas, options);
2462
2528
  if (fake) {
2463
2529
  attributes.push(fake);
2464
2530
  }
@@ -2624,6 +2690,33 @@ function generateStoreRules(propName, propDef, schema, schemas, options) {
2624
2690
  }
2625
2691
  }
2626
2692
  break;
2693
+ default:
2694
+ const customType = options.customTypes.get(propDef.type);
2695
+ if (customType && !customType.compound) {
2696
+ if (propDef.type === "JapanesePhone") {
2697
+ rules.push("'string'");
2698
+ rules.push("'max:15'");
2699
+ rules.push("'regex:/^\\d{2,4}-\\d{2,4}-\\d{4}$/'");
2700
+ } else if (propDef.type === "JapanesePostalCode") {
2701
+ rules.push("'string'");
2702
+ rules.push("'max:8'");
2703
+ rules.push("'regex:/^\\d{3}-\\d{4}$/'");
2704
+ } else {
2705
+ const sqlType = customType.sql?.sqlType ?? "VARCHAR";
2706
+ const sqlLength = customType.sql?.length ?? 255;
2707
+ if (sqlType === "VARCHAR" || sqlType === "TEXT") {
2708
+ rules.push("'string'");
2709
+ if (sqlType === "VARCHAR") {
2710
+ rules.push(`'max:${sqlLength}'`);
2711
+ }
2712
+ } else if (sqlType === "INT" || sqlType === "TINYINT" || sqlType === "BIGINT") {
2713
+ rules.push("'integer'");
2714
+ } else if (sqlType === "DECIMAL" || sqlType === "FLOAT") {
2715
+ rules.push("'numeric'");
2716
+ }
2717
+ }
2718
+ }
2719
+ break;
2627
2720
  }
2628
2721
  if (prop.unique === true) {
2629
2722
  rules.push(`'unique:${tableName}'`);
@@ -2706,12 +2799,100 @@ function generateUpdateRules(propName, propDef, schema, schemas, options) {
2706
2799
  }
2707
2800
  }
2708
2801
  break;
2802
+ default:
2803
+ const customType = options.customTypes.get(propDef.type);
2804
+ if (customType && !customType.compound) {
2805
+ if (propDef.type === "JapanesePhone") {
2806
+ rules.push("'string'");
2807
+ rules.push("'max:15'");
2808
+ rules.push("'regex:/^\\d{2,4}-\\d{2,4}-\\d{4}$/'");
2809
+ } else if (propDef.type === "JapanesePostalCode") {
2810
+ rules.push("'string'");
2811
+ rules.push("'max:8'");
2812
+ rules.push("'regex:/^\\d{3}-\\d{4}$/'");
2813
+ } else {
2814
+ const sqlType = customType.sql?.sqlType ?? "VARCHAR";
2815
+ const sqlLength = customType.sql?.length ?? 255;
2816
+ if (sqlType === "VARCHAR" || sqlType === "TEXT") {
2817
+ rules.push("'string'");
2818
+ if (sqlType === "VARCHAR") {
2819
+ rules.push(`'max:${sqlLength}'`);
2820
+ }
2821
+ } else if (sqlType === "INT" || sqlType === "TINYINT" || sqlType === "BIGINT") {
2822
+ rules.push("'integer'");
2823
+ } else if (sqlType === "DECIMAL" || sqlType === "FLOAT") {
2824
+ rules.push("'numeric'");
2825
+ }
2826
+ }
2827
+ }
2828
+ break;
2709
2829
  }
2710
2830
  if (prop.unique === true) {
2711
2831
  rules.push(`Rule::unique('${tableName}')->ignore($this->route('${modelVar}'))`);
2712
2832
  }
2713
2833
  return rules;
2714
2834
  }
2835
+ function getCompoundFieldRules(typeName, suffix, field, fieldOverride) {
2836
+ const rules = [];
2837
+ const sql = field.sql;
2838
+ const length = fieldOverride?.length ?? sql?.length ?? 255;
2839
+ switch (typeName) {
2840
+ case "JapaneseName":
2841
+ rules.push("'string'");
2842
+ rules.push(`'max:${length}'`);
2843
+ if (suffix === "KanaLastname" || suffix === "KanaFirstname") {
2844
+ rules.push("'regex:/^[\\x{30A0}-\\x{30FF}\\x{3000}-\\x{303F}\\x{FF00}-\\x{FF9F}\\s]+$/u'");
2845
+ }
2846
+ break;
2847
+ case "JapaneseAddress":
2848
+ if (suffix === "PostalCode") {
2849
+ rules.push("'string'");
2850
+ rules.push("'max:8'");
2851
+ rules.push("'regex:/^\\d{3}-\\d{4}$/'");
2852
+ } else if (suffix === "PrefectureId") {
2853
+ rules.push("'integer'");
2854
+ rules.push("'between:1,47'");
2855
+ } else {
2856
+ rules.push("'string'");
2857
+ rules.push(`'max:${length}'`);
2858
+ }
2859
+ break;
2860
+ case "JapaneseBankAccount":
2861
+ if (suffix === "BankCode") {
2862
+ rules.push("'string'");
2863
+ rules.push("'size:4'");
2864
+ rules.push("'regex:/^\\d{4}$/'");
2865
+ } else if (suffix === "BranchCode") {
2866
+ rules.push("'string'");
2867
+ rules.push("'size:3'");
2868
+ rules.push("'regex:/^\\d{3}$/'");
2869
+ } else if (suffix === "AccountType") {
2870
+ rules.push("'string'");
2871
+ rules.push("Rule::in(['1', '2', '4'])");
2872
+ } else if (suffix === "AccountNumber") {
2873
+ rules.push("'string'");
2874
+ rules.push("'max:7'");
2875
+ rules.push("'regex:/^\\d{1,7}$/'");
2876
+ } else if (suffix === "AccountHolder") {
2877
+ rules.push("'string'");
2878
+ rules.push(`'max:${length}'`);
2879
+ rules.push("'regex:/^[\\x{30A0}-\\x{30FF}\\x{3000}-\\x{303F}\\x{FF00}-\\x{FF9F}\\s]+$/u'");
2880
+ }
2881
+ break;
2882
+ default:
2883
+ if (sql?.sqlType === "TINYINT" || sql?.sqlType === "INT" || sql?.sqlType === "BIGINT") {
2884
+ rules.push("'integer'");
2885
+ if (sql?.unsigned) {
2886
+ rules.push("'min:0'");
2887
+ }
2888
+ } else {
2889
+ rules.push("'string'");
2890
+ rules.push(`'max:${length}'`);
2891
+ }
2892
+ break;
2893
+ }
2894
+ return rules;
2895
+ }
2715
2896
  function expandCompoundTypeFields(propName, propDef, options) {
2716
2897
  const typeDef = options.customTypes.get(propDef.type);
2717
2898
  if (!typeDef || !typeDef.compound || !typeDef.expand) {
@@ -2725,18 +2906,18 @@ function expandCompoundTypeFields(propName, propDef, options) {
2725
2906
  const suffixSnake = toSnakeCase(field.suffix);
2726
2907
  const fieldName = `${snakeName}_${suffixSnake}`;
2727
2908
  const fieldOverride = prop.fields?.[field.suffix];
2728
- const fieldNullable = fieldOverride?.nullable ?? isNullable2;
2909
+ const fieldDefNullable = field.sql?.nullable ?? false;
2910
+ const fieldNullable = fieldOverride?.nullable ?? fieldDefNullable ?? isNullable2;
2729
2911
  const rules = [];
2730
2912
  if (!fieldNullable) {
2731
2913
  rules.push("'required'");
2732
2914
  } else {
2733
2915
  rules.push("'nullable'");
2734
2916
  }
2735
- rules.push("'string'");
2736
- const fieldAny = field;
2737
- const length = fieldAny.laravel?.length ?? 255;
2738
- rules.push(`'max:${length}'`);
2739
- fields.push({ fieldName, rules });
2917
+ const typeRules = getCompoundFieldRules(propDef.type, field.suffix, field, fieldOverride);
2918
+ rules.push(...typeRules);
2919
+ const needsRuleImport = rules.some((r) => r.includes("Rule::"));
2920
+ fields.push({ fieldName, rules, needsRuleImport });
2740
2921
  }
2741
2922
  return fields;
2742
2923
  }
@@ -2770,6 +2951,7 @@ function generateStoreRequestBase(schema, schemas, options) {
2770
2951
  const expandedFields = expandCompoundTypeFields(propName, propDef, options);
2771
2952
  if (expandedFields.length > 0) {
2772
2953
  for (const field of expandedFields) {
2954
+ if (field.needsRuleImport) needsRuleImport = true;
2773
2955
  rulesLines.push(` '${field.fieldName}' => [${field.rules.join(", ")}],`);
2774
2956
  fieldList.push(field.fieldName);
2775
2957
  attributeLines.push(` '${field.fieldName}' => '${escapePhpString2(field.fieldName)}',`);
@@ -2866,6 +3048,7 @@ function generateUpdateRequestBase(schema, schemas, options) {
2866
3048
  const expandedFields = expandCompoundTypeFields(propName, propDef, options);
2867
3049
  if (expandedFields.length > 0) {
2868
3050
  for (const field of expandedFields) {
3051
+ if (field.needsRuleImport) needsRuleImport = true;
2869
3052
  const updateRules = field.rules.map((r) => r === "'required'" ? "'sometimes'" : r);
2870
3053
  rulesLines.push(` '${field.fieldName}' => [${updateRules.join(", ")}],`);
2871
3054
  attributeLines.push(` '${field.fieldName}' => '${escapePhpString2(field.fieldName)}',`);
@@ -2873,7 +3056,7 @@ function generateUpdateRequestBase(schema, schemas, options) {
2873
3056
  continue;
2874
3057
  }
2875
3058
  const rules = generateUpdateRules(propName, propDef, schema, schemas, options);
2876
- if (rules.some((r) => r.includes("Rule::") || r.includes("Rule::"))) needsRuleImport = true;
3059
+ if (rules.some((r) => r.includes("Rule::"))) needsRuleImport = true;
2877
3060
  rulesLines.push(` '${snakeName}' => [${rules.join(", ")}],`);
2878
3061
  const displayName = getDisplayName(propDef.displayName, options.locale, propName);
2879
3062
  attributeLines.push(` '${snakeName}' => '${escapePhpString2(displayName)}',`);
@@ -3099,6 +3282,230 @@ function getRequestPath(request) {
3099
3282
  return request.path;
3100
3283
  }
3101
3284
 
3285
+ // src/resource/generator.ts
3286
+ var import_omnify_types4 = require("@famgia/omnify-types");
3287
+ var DEFAULT_OPTIONS3 = {
3288
+ baseResourceNamespace: "App\\Http\\Resources\\OmnifyBase",
3289
+ resourceNamespace: "App\\Http\\Resources",
3290
+ baseResourcePath: "app/Http/Resources/OmnifyBase",
3291
+ resourcePath: "app/Http/Resources",
3292
+ customTypes: /* @__PURE__ */ new Map(),
3293
+ locale: "en"
3294
+ };
3295
+ var SKIP_FIELDS2 = /* @__PURE__ */ new Set([
3296
+ "password",
3297
+ "remember_token"
3298
+ ]);
3299
+ function resolveOptions4(options) {
3300
+ return {
3301
+ baseResourceNamespace: options?.baseResourceNamespace ?? DEFAULT_OPTIONS3.baseResourceNamespace,
3302
+ resourceNamespace: options?.resourceNamespace ?? DEFAULT_OPTIONS3.resourceNamespace,
3303
+ baseResourcePath: options?.baseResourcePath ?? DEFAULT_OPTIONS3.baseResourcePath,
3304
+ resourcePath: options?.resourcePath ?? DEFAULT_OPTIONS3.resourcePath,
3305
+ customTypes: options?.customTypes ?? /* @__PURE__ */ new Map(),
3306
+ locale: options?.locale ?? DEFAULT_OPTIONS3.locale
3307
+ };
3308
+ }
3309
+ function getModuleName2(schema) {
3310
+ if (schema.module) {
3311
+ return schema.module;
3312
+ }
3313
+ return "";
3314
+ }
3315
+ function getPropertyOutput(propName, propDef, schemas, options) {
3316
+ const snakeName = toSnakeCase(propName);
3317
+ const lines = [];
3318
+ if (SKIP_FIELDS2.has(snakeName)) {
3319
+ return lines;
3320
+ }
3321
+ if (propDef.type === "Association") {
3322
+ const assoc = propDef;
3323
+ const targetClass = assoc.target ? toPascalCase(assoc.target) : "";
3324
+ switch (assoc.relation) {
3325
+ case "ManyToOne":
3326
+ case "OneToOne":
3327
+ lines.push(` '${snakeName}_id' => $this->${snakeName}_id,`);
3328
+ lines.push(` '${snakeName}' => $this->whenLoaded('${toCamelCase(propName)}', fn() => new ${targetClass}Resource($this->${toCamelCase(propName)})),`);
3329
+ break;
3330
+ case "OneToMany":
3331
+ case "ManyToMany":
3332
+ lines.push(` '${snakeName}' => $this->whenLoaded('${toCamelCase(propName)}', fn() => ${targetClass}Resource::collection($this->${toCamelCase(propName)})),`);
3333
+ break;
3334
+ case "MorphTo":
3335
+ lines.push(` '${snakeName}_type' => $this->${snakeName}_type,`);
3336
+ lines.push(` '${snakeName}_id' => $this->${snakeName}_id,`);
3337
+ lines.push(` '${snakeName}' => $this->whenLoaded('${toCamelCase(propName)}'),`);
3338
+ break;
3339
+ case "MorphOne":
3340
+ case "MorphMany":
3341
+ lines.push(` '${snakeName}' => $this->whenLoaded('${toCamelCase(propName)}', fn() => ${targetClass}Resource::collection($this->${toCamelCase(propName)})),`);
3342
+ break;
3343
+ }
3344
+ return lines;
3345
+ }
3346
+ const typeDef = options.customTypes.get(propDef.type);
3347
+ if (typeDef?.compound && typeDef.expand) {
3348
+ for (const field of typeDef.expand) {
3349
+ const suffixSnake = toSnakeCase(field.suffix);
3350
+ const fieldName = `${snakeName}_${suffixSnake}`;
3351
+ lines.push(` '${fieldName}' => $this->${fieldName},`);
3352
+ }
3353
+ if (typeDef.accessors) {
3354
+ for (const accessor of typeDef.accessors) {
3355
+ const accessorName = `${snakeName}_${toSnakeCase(accessor.name)}`;
3356
+ lines.push(` '${accessorName}' => $this->${accessorName},`);
3357
+ }
3358
+ }
3359
+ return lines;
3360
+ }
3361
+ lines.push(` '${snakeName}' => $this->${snakeName},`);
3362
+ return lines;
3363
+ }
3364
+ function generateResourceBase(schema, schemas, options) {
3365
+ const className = toPascalCase(schema.name);
3366
+ const module2 = getModuleName2(schema);
3367
+ const namespaceModule = module2 ? `\\${module2}` : "";
3368
+ const namespace = `${options.baseResourceNamespace}${namespaceModule}`;
3369
+ const properties = schema.properties ?? {};
3370
+ const outputLines = [];
3371
+ const imports = /* @__PURE__ */ new Set();
3372
+ if (schema.options?.id !== false) {
3373
+ outputLines.push(` 'id' => $this->id,`);
3374
+ }
3375
+ for (const [propName, propDef] of Object.entries(properties)) {
3376
+ const lines = getPropertyOutput(propName, propDef, schemas, options);
3377
+ outputLines.push(...lines);
3378
+ if (propDef.type === "Association") {
3379
+ const assoc = propDef;
3380
+ if (assoc.target) {
3381
+ const targetModule = getModuleName2(schemas[assoc.target] ?? schema);
3382
+ const targetModuleNs = targetModule ? `\\${targetModule}` : "";
3383
+ imports.add(`use ${options.resourceNamespace}${targetModuleNs}\\${toPascalCase(assoc.target)}Resource;`);
3384
+ }
3385
+ }
3386
+ }
3387
+ if (schema.options?.timestamps !== false) {
3388
+ outputLines.push(` 'created_at' => $this->created_at?->toISOString(),`);
3389
+ outputLines.push(` 'updated_at' => $this->updated_at?->toISOString(),`);
3390
+ }
3391
+ if (schema.options?.softDelete) {
3392
+ outputLines.push(` 'deleted_at' => $this->deleted_at?->toISOString(),`);
3393
+ }
3394
+ const importLines = Array.from(imports).sort().join("\n");
3395
+ const importBlock = importLines ? `
3396
+ ${importLines}` : "";
3397
+ const content = `<?php
3398
+
3399
+ /**
3400
+ * AUTO-GENERATED BY OMNIFY - DO NOT EDIT!
3401
+ *
3402
+ * This file is generated from Omnify schema: ${schema.name}
3403
+ * Re-run \`npx omnify generate\` to update.
3404
+ *
3405
+ * @generated
3406
+ */
3407
+
3408
+ namespace ${namespace};
3409
+
3410
+ use Illuminate\\Http\\Request;
3411
+ use Illuminate\\Http\\Resources\\Json\\JsonResource;${importBlock}
3412
+
3413
+ class ${className}ResourceBase extends JsonResource
3414
+ {
3415
+ /**
3416
+ * Transform the resource into an array.
3417
+ *
3418
+ * @return array<string, mixed>
3419
+ */
3420
+ protected function schemaArray(Request $request): array
3421
+ {
3422
+ return [
3423
+ ${outputLines.join("\n")}
3424
+ ];
3425
+ }
3426
+ }
3427
+ `;
3428
+ const modulePath = module2 ? `/${module2}` : "";
3429
+ return {
3430
+ path: `${options.baseResourcePath}${modulePath}/${className}ResourceBase.php`,
3431
+ content,
3432
+ type: "base",
3433
+ overwrite: true,
3434
+ schemaName: schema.name,
3435
+ module: module2
3436
+ };
3437
+ }
3438
+ function generateResource(schema, options) {
3439
+ const className = toPascalCase(schema.name);
3440
+ const module2 = getModuleName2(schema);
3441
+ const namespaceModule = module2 ? `\\${module2}` : "";
3442
+ const namespace = `${options.resourceNamespace}${namespaceModule}`;
3443
+ const baseNamespace = `${options.baseResourceNamespace}${namespaceModule}`;
3444
+ const content = `<?php
3445
+
3446
+ /**
3447
+ * ${className} Resource
3448
+ *
3449
+ * SAFE TO EDIT - This file is never overwritten by Omnify.
3450
+ */
3451
+
3452
+ namespace ${namespace};
3453
+
3454
+ use Illuminate\\Http\\Request;
3455
+ use ${baseNamespace}\\${className}ResourceBase;
3456
+
3457
+ class ${className}Resource extends ${className}ResourceBase
3458
+ {
3459
+ /**
3460
+ * Transform the resource into an array.
3461
+ *
3462
+ * @return array<string, mixed>
3463
+ */
3464
+ public function toArray(Request $request): array
3465
+ {
3466
+ return array_merge($this->schemaArray($request), [
3467
+ // Custom fields here
3468
+ ]);
3469
+ }
3470
+
3471
+ /**
3472
+ * Get additional data that should be returned with the resource array.
3473
+ *
3474
+ * @return array<string, mixed>
3475
+ */
3476
+ public function with(Request $request): array
3477
+ {
3478
+ return [
3479
+ // Additional metadata here
3480
+ ];
3481
+ }
3482
+ }
3483
+ `;
3484
+ const modulePath = module2 ? `/${module2}` : "";
3485
+ return {
3486
+ path: `${options.resourcePath}${modulePath}/${className}Resource.php`,
3487
+ content,
3488
+ type: "user",
3489
+ overwrite: false,
3490
+ schemaName: schema.name,
3491
+ module: module2
3492
+ };
3493
+ }
3494
+ function generateResources(schemas, options) {
3495
+ const resolved = resolveOptions4(options);
3496
+ const resources = [];
3497
+ for (const schema of Object.values(schemas)) {
3498
+ if (schema.kind === "enum") continue;
3499
+ if (schema.options?.hidden === true) continue;
3500
+ resources.push(generateResourceBase(schema, schemas, resolved));
3501
+ resources.push(generateResource(schema, resolved));
3502
+ }
3503
+ return resources;
3504
+ }
3505
+ function getResourcePath(resource) {
3506
+ return resource.path;
3507
+ }
3508
+
3102
3509
  // src/plugin.ts
3103
3510
  function getExistingMigrationTables(migrationsDir) {
3104
3511
  const existingTables = /* @__PURE__ */ new Set();
@@ -3207,10 +3614,34 @@ var LARAVEL_CONFIG_SCHEMA = {
3207
3614
  description: "Generate Laravel FormRequest classes for validation",
3208
3615
  default: false,
3209
3616
  group: "options"
3617
+ },
3618
+ {
3619
+ key: "resourcesPath",
3620
+ type: "path",
3621
+ label: "Resources Path",
3622
+ description: "Directory for user-editable API Resource files",
3623
+ default: "app/Http/Resources",
3624
+ group: "output"
3625
+ },
3626
+ {
3627
+ key: "baseResourcesPath",
3628
+ type: "path",
3629
+ label: "Base Resources Path",
3630
+ description: "Directory for auto-generated base API Resource files",
3631
+ default: "app/Http/Resources/OmnifyBase",
3632
+ group: "output"
3633
+ },
3634
+ {
3635
+ key: "generateResources",
3636
+ type: "boolean",
3637
+ label: "Generate Resources",
3638
+ description: "Generate Laravel API Resource classes",
3639
+ default: false,
3640
+ group: "options"
3210
3641
  }
3211
3642
  ]
3212
3643
  };
3213
- function resolveOptions4(options) {
3644
+ function resolveOptions5(options) {
3214
3645
  return {
3215
3646
  migrationsPath: options?.migrationsPath ?? "database/migrations/omnify",
3216
3647
  modelsPath: options?.modelsPath ?? "app/Models",
@@ -3228,11 +3659,16 @@ function resolveOptions4(options) {
3228
3659
  baseRequestsPath: options?.baseRequestsPath ?? "app/Http/Requests/OmnifyBase",
3229
3660
  requestNamespace: options?.requestNamespace ?? "App\\Http\\Requests",
3230
3661
  baseRequestNamespace: options?.baseRequestNamespace ?? "App\\Http\\Requests\\OmnifyBase",
3231
- generateRequests: options?.generateRequests ?? false
3662
+ generateRequests: options?.generateRequests ?? false,
3663
+ resourcesPath: options?.resourcesPath ?? "app/Http/Resources",
3664
+ baseResourcesPath: options?.baseResourcesPath ?? "app/Http/Resources/OmnifyBase",
3665
+ resourceNamespace: options?.resourceNamespace ?? "App\\Http\\Resources",
3666
+ baseResourceNamespace: options?.baseResourceNamespace ?? "App\\Http\\Resources\\OmnifyBase",
3667
+ generateResources: options?.generateResources ?? false
3232
3668
  };
3233
3669
  }
3234
3670
  function laravelPlugin(options) {
3235
- const resolved = resolveOptions4(options);
3671
+ const resolved = resolveOptions5(options);
3236
3672
  const migrationGenerator = {
3237
3673
  name: "laravel-migrations",
3238
3674
  description: "Generate Laravel migration files",
@@ -3388,7 +3824,8 @@ function laravelPlugin(options) {
3388
3824
  const factoryOptions = {
3389
3825
  modelNamespace: resolved.modelNamespace,
3390
3826
  factoryPath: resolved.factoriesPath,
3391
- fakerLocale: resolved.fakerLocale
3827
+ fakerLocale: resolved.fakerLocale,
3828
+ customTypes: ctx.customTypes
3392
3829
  };
3393
3830
  const factories = generateFactories(ctx.schemas, factoryOptions);
3394
3831
  return factories.map((factory) => ({
@@ -3431,6 +3868,32 @@ function laravelPlugin(options) {
3431
3868
  }));
3432
3869
  }
3433
3870
  };
3871
+ const resourceGenerator = {
3872
+ name: "laravel-resources",
3873
+ description: "Generate Laravel API Resource classes",
3874
+ generate: async (ctx) => {
3875
+ const resourceOptions = {
3876
+ resourceNamespace: resolved.resourceNamespace,
3877
+ baseResourceNamespace: resolved.baseResourceNamespace,
3878
+ resourcePath: resolved.resourcesPath,
3879
+ baseResourcePath: resolved.baseResourcesPath,
3880
+ customTypes: ctx.customTypes
3881
+ };
3882
+ const resources = generateResources(ctx.schemas, resourceOptions);
3883
+ return resources.map((resource) => ({
3884
+ path: getResourcePath(resource),
3885
+ content: resource.content,
3886
+ type: "other",
3887
+ // Skip writing user resources if they already exist
3888
+ skipIfExists: !resource.overwrite,
3889
+ metadata: {
3890
+ resourceType: resource.type,
3891
+ schemaName: resource.schemaName,
3892
+ module: resource.module
3893
+ }
3894
+ }));
3895
+ }
3896
+ };
3434
3897
  const generators = [migrationGenerator];
3435
3898
  if (resolved.generateModels) {
3436
3899
  generators.push(modelGenerator);
@@ -3441,6 +3904,9 @@ function laravelPlugin(options) {
3441
3904
  if (resolved.generateRequests) {
3442
3905
  generators.push(requestGenerator);
3443
3906
  }
3907
+ if (resolved.generateResources) {
3908
+ generators.push(resourceGenerator);
3909
+ }
3444
3910
  return {
3445
3911
  name: "@famgia/omnify-laravel",
3446
3912
  version: "0.0.14",