@famgia/omnify-cli 0.0.25 → 0.0.26
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/cli.js +541 -58
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +522 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +518 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { existsSync as
|
|
5
|
-
import { resolve as
|
|
4
|
+
import { existsSync as existsSync6 } from "fs";
|
|
5
|
+
import { resolve as resolve8 } from "path";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import { OmnifyError as OmnifyError5 } from "@famgia/omnify-core";
|
|
8
8
|
|
|
@@ -670,8 +670,8 @@ function registerDiffCommand(program2) {
|
|
|
670
670
|
}
|
|
671
671
|
|
|
672
672
|
// src/commands/generate.ts
|
|
673
|
-
import { existsSync as
|
|
674
|
-
import { resolve as
|
|
673
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
674
|
+
import { resolve as resolve6, dirname as dirname4 } from "path";
|
|
675
675
|
import {
|
|
676
676
|
loadSchemas as loadSchemas3,
|
|
677
677
|
validateSchemas as validateSchemas3,
|
|
@@ -694,6 +694,481 @@ import {
|
|
|
694
694
|
getModelPath
|
|
695
695
|
} from "@famgia/omnify-laravel";
|
|
696
696
|
import { generateTypeScript } from "@famgia/omnify-typescript";
|
|
697
|
+
|
|
698
|
+
// src/guides/index.ts
|
|
699
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
700
|
+
import { resolve as resolve5 } from "path";
|
|
701
|
+
var CLAUDE_MD = `## Omnify
|
|
702
|
+
|
|
703
|
+
This project uses Omnify for schema-driven code generation.
|
|
704
|
+
|
|
705
|
+
**Documentation**: \`.claude/omnify/\`
|
|
706
|
+
- \`schema-guide.md\` - Schema format and property types
|
|
707
|
+
- \`laravel-guide.md\` - Laravel generator (if installed)
|
|
708
|
+
- \`typescript-guide.md\` - TypeScript generator (if installed)
|
|
709
|
+
- \`japan-guide.md\` - Japan plugin types (if installed)
|
|
710
|
+
|
|
711
|
+
**Commands**:
|
|
712
|
+
- \`npx omnify generate\` - Generate code from schemas
|
|
713
|
+
- \`npx omnify validate\` - Validate schemas
|
|
714
|
+
`;
|
|
715
|
+
var SCHEMA_GUIDE = `# Omnify Schema Guide
|
|
716
|
+
|
|
717
|
+
## Schema File Format
|
|
718
|
+
|
|
719
|
+
Schemas are YAML files defining data models. Each file represents one entity.
|
|
720
|
+
|
|
721
|
+
\`\`\`yaml
|
|
722
|
+
name: User
|
|
723
|
+
displayName:
|
|
724
|
+
ja: \u30E6\u30FC\u30B6\u30FC
|
|
725
|
+
en: User
|
|
726
|
+
kind: object
|
|
727
|
+
|
|
728
|
+
properties:
|
|
729
|
+
email:
|
|
730
|
+
type: Email
|
|
731
|
+
unique: true
|
|
732
|
+
name:
|
|
733
|
+
type: String
|
|
734
|
+
bio:
|
|
735
|
+
type: Text
|
|
736
|
+
nullable: true
|
|
737
|
+
|
|
738
|
+
options:
|
|
739
|
+
timestamps: true
|
|
740
|
+
softDelete: true
|
|
741
|
+
\`\`\`
|
|
742
|
+
|
|
743
|
+
## Property Types
|
|
744
|
+
|
|
745
|
+
### String Types
|
|
746
|
+
- \`String\` - VARCHAR(255)
|
|
747
|
+
- \`Text\` - TEXT
|
|
748
|
+
- \`LongText\` - LONGTEXT
|
|
749
|
+
- \`Email\` - VARCHAR(255) for email addresses
|
|
750
|
+
- \`Password\` - VARCHAR(255), hidden in serialization
|
|
751
|
+
|
|
752
|
+
### Numeric Types
|
|
753
|
+
- \`Int\` - INTEGER
|
|
754
|
+
- \`BigInt\` - BIGINT
|
|
755
|
+
- \`TinyInt\` - TINYINT
|
|
756
|
+
- \`Float\` - DOUBLE
|
|
757
|
+
- \`Decimal\` - DECIMAL(precision, scale)
|
|
758
|
+
|
|
759
|
+
### Date/Time Types
|
|
760
|
+
- \`Date\` - DATE
|
|
761
|
+
- \`Time\` - TIME
|
|
762
|
+
- \`DateTime\` - DATETIME
|
|
763
|
+
- \`Timestamp\` - TIMESTAMP
|
|
764
|
+
|
|
765
|
+
### Other Types
|
|
766
|
+
- \`Boolean\` - BOOLEAN
|
|
767
|
+
- \`Json\` - JSON
|
|
768
|
+
- \`Enum\` - ENUM with values
|
|
769
|
+
- \`EnumRef\` - Reference to enum schema
|
|
770
|
+
|
|
771
|
+
## Property Options
|
|
772
|
+
|
|
773
|
+
\`\`\`yaml
|
|
774
|
+
propertyName:
|
|
775
|
+
type: String
|
|
776
|
+
nullable: true # Can be NULL
|
|
777
|
+
unique: true # Unique constraint
|
|
778
|
+
default: "value" # Default value
|
|
779
|
+
length: 100 # VARCHAR length
|
|
780
|
+
hidden: true # Hide from JSON output
|
|
781
|
+
fillable: false # Exclude from mass assignment
|
|
782
|
+
\`\`\`
|
|
783
|
+
|
|
784
|
+
## Associations
|
|
785
|
+
|
|
786
|
+
\`\`\`yaml
|
|
787
|
+
# Many-to-One (belongsTo)
|
|
788
|
+
author:
|
|
789
|
+
type: Association
|
|
790
|
+
relation: ManyToOne
|
|
791
|
+
target: User
|
|
792
|
+
onDelete: CASCADE
|
|
793
|
+
|
|
794
|
+
# One-to-Many (hasMany)
|
|
795
|
+
posts:
|
|
796
|
+
type: Association
|
|
797
|
+
relation: OneToMany
|
|
798
|
+
target: Post
|
|
799
|
+
|
|
800
|
+
# Many-to-Many (belongsToMany)
|
|
801
|
+
tags:
|
|
802
|
+
type: Association
|
|
803
|
+
relation: ManyToMany
|
|
804
|
+
target: Tag
|
|
805
|
+
joinTable: post_tags
|
|
806
|
+
|
|
807
|
+
# Polymorphic
|
|
808
|
+
commentable:
|
|
809
|
+
type: Association
|
|
810
|
+
relation: MorphTo
|
|
811
|
+
\`\`\`
|
|
812
|
+
|
|
813
|
+
## Indexes
|
|
814
|
+
|
|
815
|
+
\`\`\`yaml
|
|
816
|
+
indexes:
|
|
817
|
+
- columns: [status, published_at]
|
|
818
|
+
- columns: [email]
|
|
819
|
+
unique: true
|
|
820
|
+
\`\`\`
|
|
821
|
+
|
|
822
|
+
## Schema Options
|
|
823
|
+
|
|
824
|
+
\`\`\`yaml
|
|
825
|
+
options:
|
|
826
|
+
timestamps: true # Add created_at, updated_at
|
|
827
|
+
softDelete: true # Add deleted_at
|
|
828
|
+
idType: BigInt # Primary key type (Int, BigInt, Uuid)
|
|
829
|
+
\`\`\`
|
|
830
|
+
`;
|
|
831
|
+
var LARAVEL_GUIDE = `# Laravel Generator Guide
|
|
832
|
+
|
|
833
|
+
## Generated Files
|
|
834
|
+
|
|
835
|
+
### Migrations
|
|
836
|
+
Located in \`database/migrations/omnify/\`
|
|
837
|
+
- Auto-generated from schema changes
|
|
838
|
+
- Handles column additions, modifications, removals
|
|
839
|
+
- Preserves manual migrations outside omnify folder
|
|
840
|
+
|
|
841
|
+
### Models
|
|
842
|
+
Two-tier model structure:
|
|
843
|
+
- \`app/Models/OmnifyBase/*BaseModel.php\` - Auto-generated, DO NOT EDIT
|
|
844
|
+
- \`app/Models/*.php\` - User models, extend base models, safe to customize
|
|
845
|
+
|
|
846
|
+
### Factories
|
|
847
|
+
Located in \`database/factories/\`
|
|
848
|
+
- Generated once, safe to customize
|
|
849
|
+
- Uses appropriate Faker methods for each type
|
|
850
|
+
|
|
851
|
+
## Model Features
|
|
852
|
+
|
|
853
|
+
### Fillable
|
|
854
|
+
All schema properties are mass-assignable by default.
|
|
855
|
+
Use \`fillable: false\` to exclude.
|
|
856
|
+
|
|
857
|
+
### Hidden
|
|
858
|
+
Use \`hidden: true\` to exclude from JSON/array output.
|
|
859
|
+
|
|
860
|
+
\`\`\`yaml
|
|
861
|
+
password:
|
|
862
|
+
type: Password
|
|
863
|
+
hidden: true
|
|
864
|
+
\`\`\`
|
|
865
|
+
|
|
866
|
+
### Casts
|
|
867
|
+
Auto-generated based on property types:
|
|
868
|
+
- \`Boolean\` \u2192 \`'boolean'\`
|
|
869
|
+
- \`Json\` \u2192 \`'array'\`
|
|
870
|
+
- \`Timestamp\` \u2192 \`'datetime'\`
|
|
871
|
+
|
|
872
|
+
### Relationships
|
|
873
|
+
Generated from Association properties:
|
|
874
|
+
- \`ManyToOne\` \u2192 \`belongsTo()\`
|
|
875
|
+
- \`OneToMany\` \u2192 \`hasMany()\`
|
|
876
|
+
- \`ManyToMany\` \u2192 \`belongsToMany()\`
|
|
877
|
+
- \`MorphTo\` \u2192 \`morphTo()\`
|
|
878
|
+
- \`MorphMany\` \u2192 \`morphMany()\`
|
|
879
|
+
|
|
880
|
+
## Commands
|
|
881
|
+
|
|
882
|
+
\`\`\`bash
|
|
883
|
+
# Generate migrations and models
|
|
884
|
+
npx omnify generate
|
|
885
|
+
|
|
886
|
+
# Force regeneration
|
|
887
|
+
npx omnify generate --force
|
|
888
|
+
|
|
889
|
+
# Validate schemas
|
|
890
|
+
npx omnify validate
|
|
891
|
+
\`\`\`
|
|
892
|
+
`;
|
|
893
|
+
var TYPESCRIPT_GUIDE = `# TypeScript Generator Guide
|
|
894
|
+
|
|
895
|
+
## Generated Types
|
|
896
|
+
|
|
897
|
+
Types are generated in the configured output directory.
|
|
898
|
+
|
|
899
|
+
### Interface Generation
|
|
900
|
+
|
|
901
|
+
Each schema generates a TypeScript interface:
|
|
902
|
+
|
|
903
|
+
\`\`\`typescript
|
|
904
|
+
export interface User {
|
|
905
|
+
id: number;
|
|
906
|
+
email: string;
|
|
907
|
+
name: string;
|
|
908
|
+
bio: string | null;
|
|
909
|
+
created_at: string;
|
|
910
|
+
updated_at: string;
|
|
911
|
+
}
|
|
912
|
+
\`\`\`
|
|
913
|
+
|
|
914
|
+
### Type Mappings
|
|
915
|
+
|
|
916
|
+
| Omnify Type | TypeScript Type |
|
|
917
|
+
|-------------|-----------------|
|
|
918
|
+
| String, Text | string |
|
|
919
|
+
| Int, BigInt | number |
|
|
920
|
+
| Float, Decimal | number |
|
|
921
|
+
| Boolean | boolean |
|
|
922
|
+
| Date, DateTime | string |
|
|
923
|
+
| Json | Record<string, unknown> |
|
|
924
|
+
| Enum | union of literals |
|
|
925
|
+
|
|
926
|
+
### Nullable Types
|
|
927
|
+
|
|
928
|
+
Nullable properties become \`T | null\`:
|
|
929
|
+
|
|
930
|
+
\`\`\`typescript
|
|
931
|
+
bio: string | null;
|
|
932
|
+
\`\`\`
|
|
933
|
+
|
|
934
|
+
### Associations
|
|
935
|
+
|
|
936
|
+
Associations generate optional relation properties:
|
|
937
|
+
|
|
938
|
+
\`\`\`typescript
|
|
939
|
+
export interface Post {
|
|
940
|
+
id: number;
|
|
941
|
+
title: string;
|
|
942
|
+
author_id: number;
|
|
943
|
+
author?: User; // Optional relation
|
|
944
|
+
comments?: Comment[]; // Optional array relation
|
|
945
|
+
}
|
|
946
|
+
\`\`\`
|
|
947
|
+
`;
|
|
948
|
+
var JAPAN_GUIDE = `# Japan Plugin Types Guide
|
|
949
|
+
|
|
950
|
+
This project uses \`@famgia/omnify-japan\` plugin which provides Japan-specific types.
|
|
951
|
+
|
|
952
|
+
## Available Types
|
|
953
|
+
|
|
954
|
+
### Simple Types
|
|
955
|
+
|
|
956
|
+
#### JapanPhone
|
|
957
|
+
Japanese phone number format (e.g., \`090-1234-5678\`, \`03-1234-5678\`)
|
|
958
|
+
- SQL: \`VARCHAR(15)\`
|
|
959
|
+
- Accepts with or without hyphens
|
|
960
|
+
|
|
961
|
+
\`\`\`yaml
|
|
962
|
+
phone:
|
|
963
|
+
type: JapanPhone
|
|
964
|
+
\`\`\`
|
|
965
|
+
|
|
966
|
+
#### JapanPostalCode
|
|
967
|
+
Japanese postal code format (e.g., \`123-4567\`)
|
|
968
|
+
- SQL: \`VARCHAR(8)\`
|
|
969
|
+
- Accepts with or without hyphen
|
|
970
|
+
|
|
971
|
+
\`\`\`yaml
|
|
972
|
+
postal_code:
|
|
973
|
+
type: JapanPostalCode
|
|
974
|
+
nullable: true
|
|
975
|
+
\`\`\`
|
|
976
|
+
|
|
977
|
+
### Compound Types
|
|
978
|
+
|
|
979
|
+
Compound types expand into multiple database columns automatically.
|
|
980
|
+
|
|
981
|
+
#### JapanName
|
|
982
|
+
Japanese name with kanji and kana variants.
|
|
983
|
+
|
|
984
|
+
**Expands to 4 columns:**
|
|
985
|
+
- \`{property}_lastname\` - VARCHAR(50) - Family name (\u59D3)
|
|
986
|
+
- \`{property}_firstname\` - VARCHAR(50) - Given name (\u540D)
|
|
987
|
+
- \`{property}_kana_lastname\` - VARCHAR(100) - Family name in katakana
|
|
988
|
+
- \`{property}_kana_firstname\` - VARCHAR(100) - Given name in katakana
|
|
989
|
+
|
|
990
|
+
**Accessors generated:**
|
|
991
|
+
- \`{property}_full_name\` - "\u59D3 \u540D" (space-separated)
|
|
992
|
+
- \`{property}_full_name_kana\` - "\u30BB\u30A4 \u30E1\u30A4" (space-separated)
|
|
993
|
+
|
|
994
|
+
\`\`\`yaml
|
|
995
|
+
name:
|
|
996
|
+
type: JapanName
|
|
997
|
+
displayName:
|
|
998
|
+
ja: \u6C0F\u540D
|
|
999
|
+
en: Full Name
|
|
1000
|
+
# Per-field overrides
|
|
1001
|
+
fields:
|
|
1002
|
+
KanaLastname:
|
|
1003
|
+
nullable: true
|
|
1004
|
+
hidden: true
|
|
1005
|
+
KanaFirstname:
|
|
1006
|
+
nullable: true
|
|
1007
|
+
hidden: true
|
|
1008
|
+
\`\`\`
|
|
1009
|
+
|
|
1010
|
+
#### JapanAddress
|
|
1011
|
+
Japanese address with postal code and prefecture ID.
|
|
1012
|
+
|
|
1013
|
+
**Expands to 5 columns:**
|
|
1014
|
+
- \`{property}_postal_code\` - VARCHAR(8) - Postal code (\u90F5\u4FBF\u756A\u53F7)
|
|
1015
|
+
- \`{property}_prefecture_id\` - TINYINT UNSIGNED - Prefecture ID 1-47 (\u90FD\u9053\u5E9C\u770C)
|
|
1016
|
+
- \`{property}_address1\` - VARCHAR(255) - City/Ward (\u5E02\u533A\u753A\u6751)
|
|
1017
|
+
- \`{property}_address2\` - VARCHAR(255) - Street address (\u4E01\u76EE\u756A\u5730\u53F7)
|
|
1018
|
+
- \`{property}_address3\` - VARCHAR(255) NULLABLE - Building name (\u30D3\u30EB\u30FB\u30DE\u30F3\u30B7\u30E7\u30F3\u540D)
|
|
1019
|
+
|
|
1020
|
+
**Accessors generated:**
|
|
1021
|
+
- \`{property}_full_address\` - Concatenation of address1 + address2 + address3
|
|
1022
|
+
|
|
1023
|
+
\`\`\`yaml
|
|
1024
|
+
address:
|
|
1025
|
+
type: JapanAddress
|
|
1026
|
+
displayName:
|
|
1027
|
+
ja: \u4F4F\u6240
|
|
1028
|
+
en: Address
|
|
1029
|
+
fields:
|
|
1030
|
+
Address3:
|
|
1031
|
+
nullable: true
|
|
1032
|
+
\`\`\`
|
|
1033
|
+
|
|
1034
|
+
**Prefecture IDs (JIS X 0401):**
|
|
1035
|
+
| ID | Prefecture | ID | Prefecture | ID | Prefecture |
|
|
1036
|
+
|----|-----------|----|-----------|----|-----------|
|
|
1037
|
+
| 1 | \u5317\u6D77\u9053 | 17 | \u77F3\u5DDD\u770C | 33 | \u5CA1\u5C71\u770C |
|
|
1038
|
+
| 2 | \u9752\u68EE\u770C | 18 | \u798F\u4E95\u770C | 34 | \u5E83\u5CF6\u770C |
|
|
1039
|
+
| 3 | \u5CA9\u624B\u770C | 19 | \u5C71\u68A8\u770C | 35 | \u5C71\u53E3\u770C |
|
|
1040
|
+
| 4 | \u5BAE\u57CE\u770C | 20 | \u9577\u91CE\u770C | 36 | \u5FB3\u5CF6\u770C |
|
|
1041
|
+
| 5 | \u79CB\u7530\u770C | 21 | \u5C90\u961C\u770C | 37 | \u9999\u5DDD\u770C |
|
|
1042
|
+
| 6 | \u5C71\u5F62\u770C | 22 | \u9759\u5CA1\u770C | 38 | \u611B\u5A9B\u770C |
|
|
1043
|
+
| 7 | \u798F\u5CF6\u770C | 23 | \u611B\u77E5\u770C | 39 | \u9AD8\u77E5\u770C |
|
|
1044
|
+
| 8 | \u8328\u57CE\u770C | 24 | \u4E09\u91CD\u770C | 40 | \u798F\u5CA1\u770C |
|
|
1045
|
+
| 9 | \u6803\u6728\u770C | 25 | \u6ECB\u8CC0\u770C | 41 | \u4F50\u8CC0\u770C |
|
|
1046
|
+
| 10 | \u7FA4\u99AC\u770C | 26 | \u4EAC\u90FD\u5E9C | 42 | \u9577\u5D0E\u770C |
|
|
1047
|
+
| 11 | \u57FC\u7389\u770C | 27 | \u5927\u962A\u5E9C | 43 | \u718A\u672C\u770C |
|
|
1048
|
+
| 12 | \u5343\u8449\u770C | 28 | \u5175\u5EAB\u770C | 44 | \u5927\u5206\u770C |
|
|
1049
|
+
| 13 | \u6771\u4EAC\u90FD | 29 | \u5948\u826F\u770C | 45 | \u5BAE\u5D0E\u770C |
|
|
1050
|
+
| 14 | \u795E\u5948\u5DDD\u770C | 30 | \u548C\u6B4C\u5C71\u770C | 46 | \u9E7F\u5150\u5CF6\u770C |
|
|
1051
|
+
| 15 | \u65B0\u6F5F\u770C | 31 | \u9CE5\u53D6\u770C | 47 | \u6C96\u7E04\u770C |
|
|
1052
|
+
| 16 | \u5BCC\u5C71\u770C | 32 | \u5CF6\u6839\u770C | | |
|
|
1053
|
+
|
|
1054
|
+
#### JapanBankAccount
|
|
1055
|
+
Japanese bank account information.
|
|
1056
|
+
|
|
1057
|
+
**Expands to 5 columns:**
|
|
1058
|
+
- \`{property}_bank_code\` - VARCHAR(4) - Bank code (\u9280\u884C\u30B3\u30FC\u30C9)
|
|
1059
|
+
- \`{property}_branch_code\` - VARCHAR(3) - Branch code (\u652F\u5E97\u30B3\u30FC\u30C9)
|
|
1060
|
+
- \`{property}_account_type\` - ENUM - Account type: 1=\u666E\u901A, 2=\u5F53\u5EA7, 4=\u8CAF\u84C4
|
|
1061
|
+
- \`{property}_account_number\` - VARCHAR(7) - Account number (\u53E3\u5EA7\u756A\u53F7)
|
|
1062
|
+
- \`{property}_account_holder\` - VARCHAR(100) - Account holder name (\u53E3\u5EA7\u540D\u7FA9)
|
|
1063
|
+
|
|
1064
|
+
\`\`\`yaml
|
|
1065
|
+
bank_account:
|
|
1066
|
+
type: JapanBankAccount
|
|
1067
|
+
\`\`\`
|
|
1068
|
+
|
|
1069
|
+
## Per-field Overrides
|
|
1070
|
+
|
|
1071
|
+
All compound types support per-field overrides:
|
|
1072
|
+
|
|
1073
|
+
\`\`\`yaml
|
|
1074
|
+
name:
|
|
1075
|
+
type: JapanName
|
|
1076
|
+
fields:
|
|
1077
|
+
KanaLastname:
|
|
1078
|
+
nullable: true
|
|
1079
|
+
hidden: true
|
|
1080
|
+
KanaFirstname:
|
|
1081
|
+
nullable: true
|
|
1082
|
+
hidden: true
|
|
1083
|
+
\`\`\`
|
|
1084
|
+
|
|
1085
|
+
**Available overrides:**
|
|
1086
|
+
- \`nullable\` - Whether the field can be NULL
|
|
1087
|
+
- \`hidden\` - Exclude from JSON/array output
|
|
1088
|
+
- \`fillable\` - Control mass assignment
|
|
1089
|
+
|
|
1090
|
+
## Factory Examples
|
|
1091
|
+
|
|
1092
|
+
\`\`\`php
|
|
1093
|
+
$faker = fake('ja_JP');
|
|
1094
|
+
|
|
1095
|
+
return [
|
|
1096
|
+
// JapanName
|
|
1097
|
+
'name_lastname' => $faker->lastName(),
|
|
1098
|
+
'name_firstname' => $faker->firstName(),
|
|
1099
|
+
'name_kana_lastname' => $faker->lastKanaName(),
|
|
1100
|
+
'name_kana_firstname' => $faker->firstKanaName(),
|
|
1101
|
+
|
|
1102
|
+
// JapanPhone
|
|
1103
|
+
'phone' => $faker->phoneNumber(),
|
|
1104
|
+
|
|
1105
|
+
// JapanPostalCode
|
|
1106
|
+
'postal_code' => $faker->postcode(),
|
|
1107
|
+
|
|
1108
|
+
// JapanAddress
|
|
1109
|
+
'address_postal_code' => $faker->postcode(),
|
|
1110
|
+
'address_prefecture_id' => $faker->numberBetween(1, 47),
|
|
1111
|
+
'address_address1' => $faker->city(),
|
|
1112
|
+
'address_address2' => $faker->streetAddress(),
|
|
1113
|
+
'address_address3' => $faker->optional(0.5)->secondaryAddress(),
|
|
1114
|
+
];
|
|
1115
|
+
\`\`\`
|
|
1116
|
+
|
|
1117
|
+
## Model Accessors
|
|
1118
|
+
|
|
1119
|
+
\`\`\`php
|
|
1120
|
+
// JapanName accessors
|
|
1121
|
+
$customer->name_full_name; // "\u7530\u4E2D \u592A\u90CE"
|
|
1122
|
+
$customer->name_full_name_kana; // "\u30BF\u30CA\u30AB \u30BF\u30ED\u30A6"
|
|
1123
|
+
|
|
1124
|
+
// JapanAddress accessor
|
|
1125
|
+
$customer->address_full_address; // "\u5343\u4EE3\u7530\u533A\u4E38\u306E\u51851-1-1\u30D3\u30EB5F"
|
|
1126
|
+
\`\`\`
|
|
1127
|
+
`;
|
|
1128
|
+
function isJapanPlugin(plugin) {
|
|
1129
|
+
return plugin.name === "@famgia/omnify-japan";
|
|
1130
|
+
}
|
|
1131
|
+
function isLaravelPlugin(plugin) {
|
|
1132
|
+
return plugin.name === "@famgia/omnify-laravel";
|
|
1133
|
+
}
|
|
1134
|
+
function isTypeScriptPlugin(plugin) {
|
|
1135
|
+
return plugin.name === "@famgia/omnify-typescript";
|
|
1136
|
+
}
|
|
1137
|
+
function generateAIGuides(rootDir, plugins) {
|
|
1138
|
+
const guidesDir = resolve5(rootDir, ".claude/omnify");
|
|
1139
|
+
let filesWritten = 0;
|
|
1140
|
+
if (!existsSync3(guidesDir)) {
|
|
1141
|
+
mkdirSync2(guidesDir, { recursive: true });
|
|
1142
|
+
}
|
|
1143
|
+
const claudeMdPath = resolve5(rootDir, "CLAUDE.md");
|
|
1144
|
+
if (!existsSync3(claudeMdPath)) {
|
|
1145
|
+
writeFileSync2(claudeMdPath, CLAUDE_MD);
|
|
1146
|
+
filesWritten++;
|
|
1147
|
+
}
|
|
1148
|
+
const schemaGuidePath = resolve5(guidesDir, "schema-guide.md");
|
|
1149
|
+
writeFileSync2(schemaGuidePath, SCHEMA_GUIDE);
|
|
1150
|
+
filesWritten++;
|
|
1151
|
+
for (const plugin of plugins) {
|
|
1152
|
+
if (isLaravelPlugin(plugin)) {
|
|
1153
|
+
const laravelGuidePath = resolve5(guidesDir, "laravel-guide.md");
|
|
1154
|
+
writeFileSync2(laravelGuidePath, LARAVEL_GUIDE);
|
|
1155
|
+
filesWritten++;
|
|
1156
|
+
}
|
|
1157
|
+
if (isTypeScriptPlugin(plugin)) {
|
|
1158
|
+
const tsGuidePath = resolve5(guidesDir, "typescript-guide.md");
|
|
1159
|
+
writeFileSync2(tsGuidePath, TYPESCRIPT_GUIDE);
|
|
1160
|
+
filesWritten++;
|
|
1161
|
+
}
|
|
1162
|
+
if (isJapanPlugin(plugin)) {
|
|
1163
|
+
const japanGuidePath = resolve5(guidesDir, "japan-guide.md");
|
|
1164
|
+
writeFileSync2(japanGuidePath, JAPAN_GUIDE);
|
|
1165
|
+
filesWritten++;
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
return filesWritten;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
// src/commands/generate.ts
|
|
697
1172
|
function hasPluginGenerators(plugins) {
|
|
698
1173
|
return plugins.some((p) => p.generators && p.generators.length > 0);
|
|
699
1174
|
}
|
|
@@ -874,17 +1349,17 @@ function schemaChangeToVersionChange(change) {
|
|
|
874
1349
|
function writeGeneratorOutputs(outputs, rootDir) {
|
|
875
1350
|
const counts = { migrations: 0, types: 0, models: 0, factories: 0, other: 0 };
|
|
876
1351
|
for (const output of outputs) {
|
|
877
|
-
const filePath =
|
|
1352
|
+
const filePath = resolve6(rootDir, output.path);
|
|
878
1353
|
const dir = dirname4(filePath);
|
|
879
|
-
if (!
|
|
880
|
-
|
|
1354
|
+
if (!existsSync4(dir)) {
|
|
1355
|
+
mkdirSync3(dir, { recursive: true });
|
|
881
1356
|
logger.debug(`Created directory: ${dir}`);
|
|
882
1357
|
}
|
|
883
|
-
if (output.skipIfExists &&
|
|
1358
|
+
if (output.skipIfExists && existsSync4(filePath)) {
|
|
884
1359
|
logger.debug(`Skipped (exists): ${output.path}`);
|
|
885
1360
|
continue;
|
|
886
1361
|
}
|
|
887
|
-
|
|
1362
|
+
writeFileSync3(filePath, output.content);
|
|
888
1363
|
logger.debug(`Created: ${output.path}`);
|
|
889
1364
|
if (output.type === "migration") counts.migrations++;
|
|
890
1365
|
else if (output.type === "type") counts.types++;
|
|
@@ -923,9 +1398,9 @@ function runDirectGeneration(schemas, config, rootDir, options, changes) {
|
|
|
923
1398
|
let modelsGenerated = 0;
|
|
924
1399
|
if (!options.typesOnly && config.output.laravel) {
|
|
925
1400
|
logger.step("Generating Laravel migrations...");
|
|
926
|
-
const migrationsDir =
|
|
927
|
-
if (!
|
|
928
|
-
|
|
1401
|
+
const migrationsDir = resolve6(rootDir, config.output.laravel.migrationsPath);
|
|
1402
|
+
if (!existsSync4(migrationsDir)) {
|
|
1403
|
+
mkdirSync3(migrationsDir, { recursive: true });
|
|
929
1404
|
logger.debug(`Created directory: ${migrationsDir}`);
|
|
930
1405
|
}
|
|
931
1406
|
const addedSchemaNames = new Set(
|
|
@@ -940,8 +1415,8 @@ function runDirectGeneration(schemas, config, rootDir, options, changes) {
|
|
|
940
1415
|
);
|
|
941
1416
|
const createMigrations = generateMigrations(addedSchemas);
|
|
942
1417
|
for (const migration of createMigrations) {
|
|
943
|
-
const filePath =
|
|
944
|
-
|
|
1418
|
+
const filePath = resolve6(migrationsDir, migration.fileName);
|
|
1419
|
+
writeFileSync3(filePath, migration.content);
|
|
945
1420
|
logger.debug(`Created: ${migration.fileName}`);
|
|
946
1421
|
migrationsGenerated++;
|
|
947
1422
|
}
|
|
@@ -949,8 +1424,8 @@ function runDirectGeneration(schemas, config, rootDir, options, changes) {
|
|
|
949
1424
|
if (alterChanges.length > 0) {
|
|
950
1425
|
const alterMigrations = generateMigrationsFromChanges(alterChanges);
|
|
951
1426
|
for (const migration of alterMigrations) {
|
|
952
|
-
const filePath =
|
|
953
|
-
|
|
1427
|
+
const filePath = resolve6(migrationsDir, migration.fileName);
|
|
1428
|
+
writeFileSync3(filePath, migration.content);
|
|
954
1429
|
logger.debug(`Created: ${migration.fileName}`);
|
|
955
1430
|
migrationsGenerated++;
|
|
956
1431
|
}
|
|
@@ -961,29 +1436,29 @@ function runDirectGeneration(schemas, config, rootDir, options, changes) {
|
|
|
961
1436
|
logger.step("Generating Laravel models...");
|
|
962
1437
|
const modelsPath = config.output.laravel.modelsPath;
|
|
963
1438
|
const baseModelsPath = config.output.laravel.baseModelsPath ?? `${modelsPath}/OmnifyBase`;
|
|
964
|
-
const modelsDir =
|
|
965
|
-
const baseModelsDir =
|
|
966
|
-
if (!
|
|
967
|
-
|
|
1439
|
+
const modelsDir = resolve6(rootDir, modelsPath);
|
|
1440
|
+
const baseModelsDir = resolve6(rootDir, baseModelsPath);
|
|
1441
|
+
if (!existsSync4(modelsDir)) {
|
|
1442
|
+
mkdirSync3(modelsDir, { recursive: true });
|
|
968
1443
|
}
|
|
969
|
-
if (!
|
|
970
|
-
|
|
1444
|
+
if (!existsSync4(baseModelsDir)) {
|
|
1445
|
+
mkdirSync3(baseModelsDir, { recursive: true });
|
|
971
1446
|
}
|
|
972
1447
|
const models = generateModels(schemas, {
|
|
973
1448
|
modelPath: modelsPath,
|
|
974
1449
|
baseModelPath: baseModelsPath
|
|
975
1450
|
});
|
|
976
1451
|
for (const model of models) {
|
|
977
|
-
const filePath =
|
|
1452
|
+
const filePath = resolve6(rootDir, getModelPath(model));
|
|
978
1453
|
const fileDir = dirname4(filePath);
|
|
979
|
-
if (!
|
|
980
|
-
|
|
1454
|
+
if (!existsSync4(fileDir)) {
|
|
1455
|
+
mkdirSync3(fileDir, { recursive: true });
|
|
981
1456
|
}
|
|
982
|
-
if (!model.overwrite &&
|
|
1457
|
+
if (!model.overwrite && existsSync4(filePath)) {
|
|
983
1458
|
logger.debug(`Skipped (exists): ${getModelPath(model)}`);
|
|
984
1459
|
continue;
|
|
985
1460
|
}
|
|
986
|
-
|
|
1461
|
+
writeFileSync3(filePath, model.content);
|
|
987
1462
|
logger.debug(`Created: ${getModelPath(model)}`);
|
|
988
1463
|
modelsGenerated++;
|
|
989
1464
|
}
|
|
@@ -991,23 +1466,23 @@ function runDirectGeneration(schemas, config, rootDir, options, changes) {
|
|
|
991
1466
|
}
|
|
992
1467
|
if (!options.migrationsOnly && config.output.typescript) {
|
|
993
1468
|
logger.step("Generating TypeScript types...");
|
|
994
|
-
const typesDir =
|
|
995
|
-
if (!
|
|
996
|
-
|
|
1469
|
+
const typesDir = resolve6(rootDir, config.output.typescript.path);
|
|
1470
|
+
if (!existsSync4(typesDir)) {
|
|
1471
|
+
mkdirSync3(typesDir, { recursive: true });
|
|
997
1472
|
logger.debug(`Created directory: ${typesDir}`);
|
|
998
1473
|
}
|
|
999
1474
|
const typeFiles = generateTypeScript(schemas);
|
|
1000
1475
|
for (const file of typeFiles) {
|
|
1001
|
-
const filePath =
|
|
1476
|
+
const filePath = resolve6(typesDir, file.filePath);
|
|
1002
1477
|
const fileDir = dirname4(filePath);
|
|
1003
|
-
if (!
|
|
1004
|
-
|
|
1478
|
+
if (!existsSync4(fileDir)) {
|
|
1479
|
+
mkdirSync3(fileDir, { recursive: true });
|
|
1005
1480
|
}
|
|
1006
|
-
if (!file.overwrite &&
|
|
1481
|
+
if (!file.overwrite && existsSync4(filePath)) {
|
|
1007
1482
|
logger.debug(`Skipped (exists): ${file.filePath}`);
|
|
1008
1483
|
continue;
|
|
1009
1484
|
}
|
|
1010
|
-
|
|
1485
|
+
writeFileSync3(filePath, file.content);
|
|
1011
1486
|
logger.debug(`Created: ${file.filePath}`);
|
|
1012
1487
|
typesGenerated++;
|
|
1013
1488
|
}
|
|
@@ -1022,7 +1497,7 @@ async function runGenerate(options) {
|
|
|
1022
1497
|
const { config, configPath: configPath2 } = await loadConfig();
|
|
1023
1498
|
const rootDir = configPath2 ? dirname4(configPath2) : process.cwd();
|
|
1024
1499
|
validateConfig(config, rootDir);
|
|
1025
|
-
const schemaPath =
|
|
1500
|
+
const schemaPath = resolve6(rootDir, config.schemasDir);
|
|
1026
1501
|
logger.step(`Loading schemas from ${schemaPath}`);
|
|
1027
1502
|
const schemas = await loadSchemas3(schemaPath);
|
|
1028
1503
|
const schemaCount = Object.keys(schemas).length;
|
|
@@ -1052,7 +1527,7 @@ async function runGenerate(options) {
|
|
|
1052
1527
|
process.exit(2);
|
|
1053
1528
|
}
|
|
1054
1529
|
logger.step("Checking for changes...");
|
|
1055
|
-
const lockPath =
|
|
1530
|
+
const lockPath = resolve6(rootDir, config.lockFilePath);
|
|
1056
1531
|
const existingLock = await readLockFile(lockPath);
|
|
1057
1532
|
const currentSnapshots = await buildSchemaSnapshots(schemas);
|
|
1058
1533
|
const v2Lock = existingLock && isLockFileV2(existingLock) ? existingLock : null;
|
|
@@ -1126,6 +1601,14 @@ async function runGenerate(options) {
|
|
|
1126
1601
|
} catch (versionError) {
|
|
1127
1602
|
logger.debug(`Could not save version history: ${versionError.message}`);
|
|
1128
1603
|
}
|
|
1604
|
+
try {
|
|
1605
|
+
const guidesWritten = generateAIGuides(rootDir, config.plugins);
|
|
1606
|
+
if (guidesWritten > 0) {
|
|
1607
|
+
logger.debug(`Updated ${guidesWritten} AI guide file(s)`);
|
|
1608
|
+
}
|
|
1609
|
+
} catch (guideError) {
|
|
1610
|
+
logger.debug(`Could not generate AI guides: ${guideError.message}`);
|
|
1611
|
+
}
|
|
1129
1612
|
logger.newline();
|
|
1130
1613
|
logger.success("Generation complete!");
|
|
1131
1614
|
if (migrationsGenerated > 0 && config.output.laravel) {
|
|
@@ -1156,23 +1639,23 @@ function registerGenerateCommand(program2) {
|
|
|
1156
1639
|
}
|
|
1157
1640
|
|
|
1158
1641
|
// src/commands/reset.ts
|
|
1159
|
-
import { existsSync as
|
|
1160
|
-
import { resolve as
|
|
1642
|
+
import { existsSync as existsSync5, readdirSync, rmSync, statSync } from "fs";
|
|
1643
|
+
import { resolve as resolve7, dirname as dirname5, join } from "path";
|
|
1161
1644
|
import { createInterface } from "readline";
|
|
1162
1645
|
async function confirm2(message) {
|
|
1163
1646
|
const rl = createInterface({
|
|
1164
1647
|
input: process.stdin,
|
|
1165
1648
|
output: process.stdout
|
|
1166
1649
|
});
|
|
1167
|
-
return new Promise((
|
|
1650
|
+
return new Promise((resolve9) => {
|
|
1168
1651
|
rl.question(`${message} (y/N) `, (answer) => {
|
|
1169
1652
|
rl.close();
|
|
1170
|
-
|
|
1653
|
+
resolve9(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
1171
1654
|
});
|
|
1172
1655
|
});
|
|
1173
1656
|
}
|
|
1174
1657
|
function countFiles(dir) {
|
|
1175
|
-
if (!
|
|
1658
|
+
if (!existsSync5(dir)) return 0;
|
|
1176
1659
|
let count = 0;
|
|
1177
1660
|
const entries = readdirSync(dir);
|
|
1178
1661
|
for (const entry of entries) {
|
|
@@ -1187,7 +1670,7 @@ function countFiles(dir) {
|
|
|
1187
1670
|
return count;
|
|
1188
1671
|
}
|
|
1189
1672
|
function deleteDir(dir, verbose) {
|
|
1190
|
-
if (!
|
|
1673
|
+
if (!existsSync5(dir)) return 0;
|
|
1191
1674
|
const count = countFiles(dir);
|
|
1192
1675
|
rmSync(dir, { recursive: true, force: true });
|
|
1193
1676
|
if (verbose) {
|
|
@@ -1196,7 +1679,7 @@ function deleteDir(dir, verbose) {
|
|
|
1196
1679
|
return count;
|
|
1197
1680
|
}
|
|
1198
1681
|
function deleteFilesInDir(dir, pattern, verbose) {
|
|
1199
|
-
if (!
|
|
1682
|
+
if (!existsSync5(dir)) return 0;
|
|
1200
1683
|
let count = 0;
|
|
1201
1684
|
const entries = readdirSync(dir);
|
|
1202
1685
|
for (const entry of entries) {
|
|
@@ -1225,8 +1708,8 @@ async function runReset(options) {
|
|
|
1225
1708
|
"src/Models/OmnifyBase"
|
|
1226
1709
|
];
|
|
1227
1710
|
for (const relPath of omnifyBasePaths) {
|
|
1228
|
-
const omnifyBasePath =
|
|
1229
|
-
if (
|
|
1711
|
+
const omnifyBasePath = resolve7(rootDir, relPath);
|
|
1712
|
+
if (existsSync5(omnifyBasePath)) {
|
|
1230
1713
|
paths.push({ name: "OmnifyBase models", path: omnifyBasePath, type: "dir" });
|
|
1231
1714
|
break;
|
|
1232
1715
|
}
|
|
@@ -1236,8 +1719,8 @@ async function runReset(options) {
|
|
|
1236
1719
|
"backend/database/migrations/omnify"
|
|
1237
1720
|
];
|
|
1238
1721
|
for (const relPath of migrationPaths) {
|
|
1239
|
-
const migrationsPath =
|
|
1240
|
-
if (
|
|
1722
|
+
const migrationsPath = resolve7(rootDir, relPath);
|
|
1723
|
+
if (existsSync5(migrationsPath)) {
|
|
1241
1724
|
paths.push({
|
|
1242
1725
|
name: "Omnify migrations",
|
|
1243
1726
|
path: migrationsPath,
|
|
@@ -1249,15 +1732,15 @@ async function runReset(options) {
|
|
|
1249
1732
|
}
|
|
1250
1733
|
const laravelConfig = config.output.laravel;
|
|
1251
1734
|
if (laravelConfig?.modelsPath) {
|
|
1252
|
-
const modelsPath =
|
|
1735
|
+
const modelsPath = resolve7(rootDir, laravelConfig.modelsPath);
|
|
1253
1736
|
const omnifyBasePath = join(modelsPath, "OmnifyBase");
|
|
1254
|
-
if (
|
|
1737
|
+
if (existsSync5(omnifyBasePath) && !paths.some((p) => p.path === omnifyBasePath)) {
|
|
1255
1738
|
paths.push({ name: "OmnifyBase models", path: omnifyBasePath, type: "dir" });
|
|
1256
1739
|
}
|
|
1257
1740
|
}
|
|
1258
1741
|
if (laravelConfig?.migrationsPath) {
|
|
1259
|
-
const migrationsPath =
|
|
1260
|
-
if (
|
|
1742
|
+
const migrationsPath = resolve7(rootDir, laravelConfig.migrationsPath);
|
|
1743
|
+
if (existsSync5(migrationsPath) && !paths.some((p) => p.path === migrationsPath)) {
|
|
1261
1744
|
paths.push({
|
|
1262
1745
|
name: "Omnify migrations",
|
|
1263
1746
|
path: migrationsPath,
|
|
@@ -1266,12 +1749,12 @@ async function runReset(options) {
|
|
|
1266
1749
|
});
|
|
1267
1750
|
}
|
|
1268
1751
|
}
|
|
1269
|
-
const lockFilePath =
|
|
1270
|
-
if (
|
|
1752
|
+
const lockFilePath = resolve7(rootDir, config.lockFilePath);
|
|
1753
|
+
if (existsSync5(lockFilePath)) {
|
|
1271
1754
|
paths.push({ name: "Lock file", path: lockFilePath, type: "file" });
|
|
1272
1755
|
}
|
|
1273
|
-
const omnifyDir =
|
|
1274
|
-
if (
|
|
1756
|
+
const omnifyDir = resolve7(rootDir, ".omnify");
|
|
1757
|
+
if (existsSync5(omnifyDir)) {
|
|
1275
1758
|
paths.push({ name: ".omnify directory", path: omnifyDir, type: "dir" });
|
|
1276
1759
|
}
|
|
1277
1760
|
if (paths.length === 0) {
|
|
@@ -1372,8 +1855,8 @@ process.on("unhandledRejection", (reason) => {
|
|
|
1372
1855
|
var args = process.argv.slice(2);
|
|
1373
1856
|
var firstArg = args[0];
|
|
1374
1857
|
var hasCommand = firstArg !== void 0 && !firstArg.startsWith("-");
|
|
1375
|
-
var configPath =
|
|
1376
|
-
var hasConfig =
|
|
1858
|
+
var configPath = resolve8(process.cwd(), "omnify.config.ts");
|
|
1859
|
+
var hasConfig = existsSync6(configPath);
|
|
1377
1860
|
if (!hasCommand && !hasConfig) {
|
|
1378
1861
|
runInit({}).catch((error) => {
|
|
1379
1862
|
if (error instanceof Error) {
|