@famgia/omnify-cli 0.0.25 → 0.0.27
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 +13 -13
package/dist/index.js
CHANGED
|
@@ -663,8 +663,8 @@ function registerDiffCommand(program) {
|
|
|
663
663
|
}
|
|
664
664
|
|
|
665
665
|
// src/commands/generate.ts
|
|
666
|
-
import { existsSync as
|
|
667
|
-
import { resolve as
|
|
666
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
667
|
+
import { resolve as resolve6, dirname as dirname4 } from "path";
|
|
668
668
|
import {
|
|
669
669
|
loadSchemas as loadSchemas3,
|
|
670
670
|
validateSchemas as validateSchemas3,
|
|
@@ -687,6 +687,481 @@ import {
|
|
|
687
687
|
getModelPath
|
|
688
688
|
} from "@famgia/omnify-laravel";
|
|
689
689
|
import { generateTypeScript } from "@famgia/omnify-typescript";
|
|
690
|
+
|
|
691
|
+
// src/guides/index.ts
|
|
692
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
693
|
+
import { resolve as resolve5 } from "path";
|
|
694
|
+
var CLAUDE_MD = `## Omnify
|
|
695
|
+
|
|
696
|
+
This project uses Omnify for schema-driven code generation.
|
|
697
|
+
|
|
698
|
+
**Documentation**: \`.claude/omnify/\`
|
|
699
|
+
- \`schema-guide.md\` - Schema format and property types
|
|
700
|
+
- \`laravel-guide.md\` - Laravel generator (if installed)
|
|
701
|
+
- \`typescript-guide.md\` - TypeScript generator (if installed)
|
|
702
|
+
- \`japan-guide.md\` - Japan plugin types (if installed)
|
|
703
|
+
|
|
704
|
+
**Commands**:
|
|
705
|
+
- \`npx omnify generate\` - Generate code from schemas
|
|
706
|
+
- \`npx omnify validate\` - Validate schemas
|
|
707
|
+
`;
|
|
708
|
+
var SCHEMA_GUIDE = `# Omnify Schema Guide
|
|
709
|
+
|
|
710
|
+
## Schema File Format
|
|
711
|
+
|
|
712
|
+
Schemas are YAML files defining data models. Each file represents one entity.
|
|
713
|
+
|
|
714
|
+
\`\`\`yaml
|
|
715
|
+
name: User
|
|
716
|
+
displayName:
|
|
717
|
+
ja: \u30E6\u30FC\u30B6\u30FC
|
|
718
|
+
en: User
|
|
719
|
+
kind: object
|
|
720
|
+
|
|
721
|
+
properties:
|
|
722
|
+
email:
|
|
723
|
+
type: Email
|
|
724
|
+
unique: true
|
|
725
|
+
name:
|
|
726
|
+
type: String
|
|
727
|
+
bio:
|
|
728
|
+
type: Text
|
|
729
|
+
nullable: true
|
|
730
|
+
|
|
731
|
+
options:
|
|
732
|
+
timestamps: true
|
|
733
|
+
softDelete: true
|
|
734
|
+
\`\`\`
|
|
735
|
+
|
|
736
|
+
## Property Types
|
|
737
|
+
|
|
738
|
+
### String Types
|
|
739
|
+
- \`String\` - VARCHAR(255)
|
|
740
|
+
- \`Text\` - TEXT
|
|
741
|
+
- \`LongText\` - LONGTEXT
|
|
742
|
+
- \`Email\` - VARCHAR(255) for email addresses
|
|
743
|
+
- \`Password\` - VARCHAR(255), hidden in serialization
|
|
744
|
+
|
|
745
|
+
### Numeric Types
|
|
746
|
+
- \`Int\` - INTEGER
|
|
747
|
+
- \`BigInt\` - BIGINT
|
|
748
|
+
- \`TinyInt\` - TINYINT
|
|
749
|
+
- \`Float\` - DOUBLE
|
|
750
|
+
- \`Decimal\` - DECIMAL(precision, scale)
|
|
751
|
+
|
|
752
|
+
### Date/Time Types
|
|
753
|
+
- \`Date\` - DATE
|
|
754
|
+
- \`Time\` - TIME
|
|
755
|
+
- \`DateTime\` - DATETIME
|
|
756
|
+
- \`Timestamp\` - TIMESTAMP
|
|
757
|
+
|
|
758
|
+
### Other Types
|
|
759
|
+
- \`Boolean\` - BOOLEAN
|
|
760
|
+
- \`Json\` - JSON
|
|
761
|
+
- \`Enum\` - ENUM with values
|
|
762
|
+
- \`EnumRef\` - Reference to enum schema
|
|
763
|
+
|
|
764
|
+
## Property Options
|
|
765
|
+
|
|
766
|
+
\`\`\`yaml
|
|
767
|
+
propertyName:
|
|
768
|
+
type: String
|
|
769
|
+
nullable: true # Can be NULL
|
|
770
|
+
unique: true # Unique constraint
|
|
771
|
+
default: "value" # Default value
|
|
772
|
+
length: 100 # VARCHAR length
|
|
773
|
+
hidden: true # Hide from JSON output
|
|
774
|
+
fillable: false # Exclude from mass assignment
|
|
775
|
+
\`\`\`
|
|
776
|
+
|
|
777
|
+
## Associations
|
|
778
|
+
|
|
779
|
+
\`\`\`yaml
|
|
780
|
+
# Many-to-One (belongsTo)
|
|
781
|
+
author:
|
|
782
|
+
type: Association
|
|
783
|
+
relation: ManyToOne
|
|
784
|
+
target: User
|
|
785
|
+
onDelete: CASCADE
|
|
786
|
+
|
|
787
|
+
# One-to-Many (hasMany)
|
|
788
|
+
posts:
|
|
789
|
+
type: Association
|
|
790
|
+
relation: OneToMany
|
|
791
|
+
target: Post
|
|
792
|
+
|
|
793
|
+
# Many-to-Many (belongsToMany)
|
|
794
|
+
tags:
|
|
795
|
+
type: Association
|
|
796
|
+
relation: ManyToMany
|
|
797
|
+
target: Tag
|
|
798
|
+
joinTable: post_tags
|
|
799
|
+
|
|
800
|
+
# Polymorphic
|
|
801
|
+
commentable:
|
|
802
|
+
type: Association
|
|
803
|
+
relation: MorphTo
|
|
804
|
+
\`\`\`
|
|
805
|
+
|
|
806
|
+
## Indexes
|
|
807
|
+
|
|
808
|
+
\`\`\`yaml
|
|
809
|
+
indexes:
|
|
810
|
+
- columns: [status, published_at]
|
|
811
|
+
- columns: [email]
|
|
812
|
+
unique: true
|
|
813
|
+
\`\`\`
|
|
814
|
+
|
|
815
|
+
## Schema Options
|
|
816
|
+
|
|
817
|
+
\`\`\`yaml
|
|
818
|
+
options:
|
|
819
|
+
timestamps: true # Add created_at, updated_at
|
|
820
|
+
softDelete: true # Add deleted_at
|
|
821
|
+
idType: BigInt # Primary key type (Int, BigInt, Uuid)
|
|
822
|
+
\`\`\`
|
|
823
|
+
`;
|
|
824
|
+
var LARAVEL_GUIDE = `# Laravel Generator Guide
|
|
825
|
+
|
|
826
|
+
## Generated Files
|
|
827
|
+
|
|
828
|
+
### Migrations
|
|
829
|
+
Located in \`database/migrations/omnify/\`
|
|
830
|
+
- Auto-generated from schema changes
|
|
831
|
+
- Handles column additions, modifications, removals
|
|
832
|
+
- Preserves manual migrations outside omnify folder
|
|
833
|
+
|
|
834
|
+
### Models
|
|
835
|
+
Two-tier model structure:
|
|
836
|
+
- \`app/Models/OmnifyBase/*BaseModel.php\` - Auto-generated, DO NOT EDIT
|
|
837
|
+
- \`app/Models/*.php\` - User models, extend base models, safe to customize
|
|
838
|
+
|
|
839
|
+
### Factories
|
|
840
|
+
Located in \`database/factories/\`
|
|
841
|
+
- Generated once, safe to customize
|
|
842
|
+
- Uses appropriate Faker methods for each type
|
|
843
|
+
|
|
844
|
+
## Model Features
|
|
845
|
+
|
|
846
|
+
### Fillable
|
|
847
|
+
All schema properties are mass-assignable by default.
|
|
848
|
+
Use \`fillable: false\` to exclude.
|
|
849
|
+
|
|
850
|
+
### Hidden
|
|
851
|
+
Use \`hidden: true\` to exclude from JSON/array output.
|
|
852
|
+
|
|
853
|
+
\`\`\`yaml
|
|
854
|
+
password:
|
|
855
|
+
type: Password
|
|
856
|
+
hidden: true
|
|
857
|
+
\`\`\`
|
|
858
|
+
|
|
859
|
+
### Casts
|
|
860
|
+
Auto-generated based on property types:
|
|
861
|
+
- \`Boolean\` \u2192 \`'boolean'\`
|
|
862
|
+
- \`Json\` \u2192 \`'array'\`
|
|
863
|
+
- \`Timestamp\` \u2192 \`'datetime'\`
|
|
864
|
+
|
|
865
|
+
### Relationships
|
|
866
|
+
Generated from Association properties:
|
|
867
|
+
- \`ManyToOne\` \u2192 \`belongsTo()\`
|
|
868
|
+
- \`OneToMany\` \u2192 \`hasMany()\`
|
|
869
|
+
- \`ManyToMany\` \u2192 \`belongsToMany()\`
|
|
870
|
+
- \`MorphTo\` \u2192 \`morphTo()\`
|
|
871
|
+
- \`MorphMany\` \u2192 \`morphMany()\`
|
|
872
|
+
|
|
873
|
+
## Commands
|
|
874
|
+
|
|
875
|
+
\`\`\`bash
|
|
876
|
+
# Generate migrations and models
|
|
877
|
+
npx omnify generate
|
|
878
|
+
|
|
879
|
+
# Force regeneration
|
|
880
|
+
npx omnify generate --force
|
|
881
|
+
|
|
882
|
+
# Validate schemas
|
|
883
|
+
npx omnify validate
|
|
884
|
+
\`\`\`
|
|
885
|
+
`;
|
|
886
|
+
var TYPESCRIPT_GUIDE = `# TypeScript Generator Guide
|
|
887
|
+
|
|
888
|
+
## Generated Types
|
|
889
|
+
|
|
890
|
+
Types are generated in the configured output directory.
|
|
891
|
+
|
|
892
|
+
### Interface Generation
|
|
893
|
+
|
|
894
|
+
Each schema generates a TypeScript interface:
|
|
895
|
+
|
|
896
|
+
\`\`\`typescript
|
|
897
|
+
export interface User {
|
|
898
|
+
id: number;
|
|
899
|
+
email: string;
|
|
900
|
+
name: string;
|
|
901
|
+
bio: string | null;
|
|
902
|
+
created_at: string;
|
|
903
|
+
updated_at: string;
|
|
904
|
+
}
|
|
905
|
+
\`\`\`
|
|
906
|
+
|
|
907
|
+
### Type Mappings
|
|
908
|
+
|
|
909
|
+
| Omnify Type | TypeScript Type |
|
|
910
|
+
|-------------|-----------------|
|
|
911
|
+
| String, Text | string |
|
|
912
|
+
| Int, BigInt | number |
|
|
913
|
+
| Float, Decimal | number |
|
|
914
|
+
| Boolean | boolean |
|
|
915
|
+
| Date, DateTime | string |
|
|
916
|
+
| Json | Record<string, unknown> |
|
|
917
|
+
| Enum | union of literals |
|
|
918
|
+
|
|
919
|
+
### Nullable Types
|
|
920
|
+
|
|
921
|
+
Nullable properties become \`T | null\`:
|
|
922
|
+
|
|
923
|
+
\`\`\`typescript
|
|
924
|
+
bio: string | null;
|
|
925
|
+
\`\`\`
|
|
926
|
+
|
|
927
|
+
### Associations
|
|
928
|
+
|
|
929
|
+
Associations generate optional relation properties:
|
|
930
|
+
|
|
931
|
+
\`\`\`typescript
|
|
932
|
+
export interface Post {
|
|
933
|
+
id: number;
|
|
934
|
+
title: string;
|
|
935
|
+
author_id: number;
|
|
936
|
+
author?: User; // Optional relation
|
|
937
|
+
comments?: Comment[]; // Optional array relation
|
|
938
|
+
}
|
|
939
|
+
\`\`\`
|
|
940
|
+
`;
|
|
941
|
+
var JAPAN_GUIDE = `# Japan Plugin Types Guide
|
|
942
|
+
|
|
943
|
+
This project uses \`@famgia/omnify-japan\` plugin which provides Japan-specific types.
|
|
944
|
+
|
|
945
|
+
## Available Types
|
|
946
|
+
|
|
947
|
+
### Simple Types
|
|
948
|
+
|
|
949
|
+
#### JapanPhone
|
|
950
|
+
Japanese phone number format (e.g., \`090-1234-5678\`, \`03-1234-5678\`)
|
|
951
|
+
- SQL: \`VARCHAR(15)\`
|
|
952
|
+
- Accepts with or without hyphens
|
|
953
|
+
|
|
954
|
+
\`\`\`yaml
|
|
955
|
+
phone:
|
|
956
|
+
type: JapanPhone
|
|
957
|
+
\`\`\`
|
|
958
|
+
|
|
959
|
+
#### JapanPostalCode
|
|
960
|
+
Japanese postal code format (e.g., \`123-4567\`)
|
|
961
|
+
- SQL: \`VARCHAR(8)\`
|
|
962
|
+
- Accepts with or without hyphen
|
|
963
|
+
|
|
964
|
+
\`\`\`yaml
|
|
965
|
+
postal_code:
|
|
966
|
+
type: JapanPostalCode
|
|
967
|
+
nullable: true
|
|
968
|
+
\`\`\`
|
|
969
|
+
|
|
970
|
+
### Compound Types
|
|
971
|
+
|
|
972
|
+
Compound types expand into multiple database columns automatically.
|
|
973
|
+
|
|
974
|
+
#### JapanName
|
|
975
|
+
Japanese name with kanji and kana variants.
|
|
976
|
+
|
|
977
|
+
**Expands to 4 columns:**
|
|
978
|
+
- \`{property}_lastname\` - VARCHAR(50) - Family name (\u59D3)
|
|
979
|
+
- \`{property}_firstname\` - VARCHAR(50) - Given name (\u540D)
|
|
980
|
+
- \`{property}_kana_lastname\` - VARCHAR(100) - Family name in katakana
|
|
981
|
+
- \`{property}_kana_firstname\` - VARCHAR(100) - Given name in katakana
|
|
982
|
+
|
|
983
|
+
**Accessors generated:**
|
|
984
|
+
- \`{property}_full_name\` - "\u59D3 \u540D" (space-separated)
|
|
985
|
+
- \`{property}_full_name_kana\` - "\u30BB\u30A4 \u30E1\u30A4" (space-separated)
|
|
986
|
+
|
|
987
|
+
\`\`\`yaml
|
|
988
|
+
name:
|
|
989
|
+
type: JapanName
|
|
990
|
+
displayName:
|
|
991
|
+
ja: \u6C0F\u540D
|
|
992
|
+
en: Full Name
|
|
993
|
+
# Per-field overrides
|
|
994
|
+
fields:
|
|
995
|
+
KanaLastname:
|
|
996
|
+
nullable: true
|
|
997
|
+
hidden: true
|
|
998
|
+
KanaFirstname:
|
|
999
|
+
nullable: true
|
|
1000
|
+
hidden: true
|
|
1001
|
+
\`\`\`
|
|
1002
|
+
|
|
1003
|
+
#### JapanAddress
|
|
1004
|
+
Japanese address with postal code and prefecture ID.
|
|
1005
|
+
|
|
1006
|
+
**Expands to 5 columns:**
|
|
1007
|
+
- \`{property}_postal_code\` - VARCHAR(8) - Postal code (\u90F5\u4FBF\u756A\u53F7)
|
|
1008
|
+
- \`{property}_prefecture_id\` - TINYINT UNSIGNED - Prefecture ID 1-47 (\u90FD\u9053\u5E9C\u770C)
|
|
1009
|
+
- \`{property}_address1\` - VARCHAR(255) - City/Ward (\u5E02\u533A\u753A\u6751)
|
|
1010
|
+
- \`{property}_address2\` - VARCHAR(255) - Street address (\u4E01\u76EE\u756A\u5730\u53F7)
|
|
1011
|
+
- \`{property}_address3\` - VARCHAR(255) NULLABLE - Building name (\u30D3\u30EB\u30FB\u30DE\u30F3\u30B7\u30E7\u30F3\u540D)
|
|
1012
|
+
|
|
1013
|
+
**Accessors generated:**
|
|
1014
|
+
- \`{property}_full_address\` - Concatenation of address1 + address2 + address3
|
|
1015
|
+
|
|
1016
|
+
\`\`\`yaml
|
|
1017
|
+
address:
|
|
1018
|
+
type: JapanAddress
|
|
1019
|
+
displayName:
|
|
1020
|
+
ja: \u4F4F\u6240
|
|
1021
|
+
en: Address
|
|
1022
|
+
fields:
|
|
1023
|
+
Address3:
|
|
1024
|
+
nullable: true
|
|
1025
|
+
\`\`\`
|
|
1026
|
+
|
|
1027
|
+
**Prefecture IDs (JIS X 0401):**
|
|
1028
|
+
| ID | Prefecture | ID | Prefecture | ID | Prefecture |
|
|
1029
|
+
|----|-----------|----|-----------|----|-----------|
|
|
1030
|
+
| 1 | \u5317\u6D77\u9053 | 17 | \u77F3\u5DDD\u770C | 33 | \u5CA1\u5C71\u770C |
|
|
1031
|
+
| 2 | \u9752\u68EE\u770C | 18 | \u798F\u4E95\u770C | 34 | \u5E83\u5CF6\u770C |
|
|
1032
|
+
| 3 | \u5CA9\u624B\u770C | 19 | \u5C71\u68A8\u770C | 35 | \u5C71\u53E3\u770C |
|
|
1033
|
+
| 4 | \u5BAE\u57CE\u770C | 20 | \u9577\u91CE\u770C | 36 | \u5FB3\u5CF6\u770C |
|
|
1034
|
+
| 5 | \u79CB\u7530\u770C | 21 | \u5C90\u961C\u770C | 37 | \u9999\u5DDD\u770C |
|
|
1035
|
+
| 6 | \u5C71\u5F62\u770C | 22 | \u9759\u5CA1\u770C | 38 | \u611B\u5A9B\u770C |
|
|
1036
|
+
| 7 | \u798F\u5CF6\u770C | 23 | \u611B\u77E5\u770C | 39 | \u9AD8\u77E5\u770C |
|
|
1037
|
+
| 8 | \u8328\u57CE\u770C | 24 | \u4E09\u91CD\u770C | 40 | \u798F\u5CA1\u770C |
|
|
1038
|
+
| 9 | \u6803\u6728\u770C | 25 | \u6ECB\u8CC0\u770C | 41 | \u4F50\u8CC0\u770C |
|
|
1039
|
+
| 10 | \u7FA4\u99AC\u770C | 26 | \u4EAC\u90FD\u5E9C | 42 | \u9577\u5D0E\u770C |
|
|
1040
|
+
| 11 | \u57FC\u7389\u770C | 27 | \u5927\u962A\u5E9C | 43 | \u718A\u672C\u770C |
|
|
1041
|
+
| 12 | \u5343\u8449\u770C | 28 | \u5175\u5EAB\u770C | 44 | \u5927\u5206\u770C |
|
|
1042
|
+
| 13 | \u6771\u4EAC\u90FD | 29 | \u5948\u826F\u770C | 45 | \u5BAE\u5D0E\u770C |
|
|
1043
|
+
| 14 | \u795E\u5948\u5DDD\u770C | 30 | \u548C\u6B4C\u5C71\u770C | 46 | \u9E7F\u5150\u5CF6\u770C |
|
|
1044
|
+
| 15 | \u65B0\u6F5F\u770C | 31 | \u9CE5\u53D6\u770C | 47 | \u6C96\u7E04\u770C |
|
|
1045
|
+
| 16 | \u5BCC\u5C71\u770C | 32 | \u5CF6\u6839\u770C | | |
|
|
1046
|
+
|
|
1047
|
+
#### JapanBankAccount
|
|
1048
|
+
Japanese bank account information.
|
|
1049
|
+
|
|
1050
|
+
**Expands to 5 columns:**
|
|
1051
|
+
- \`{property}_bank_code\` - VARCHAR(4) - Bank code (\u9280\u884C\u30B3\u30FC\u30C9)
|
|
1052
|
+
- \`{property}_branch_code\` - VARCHAR(3) - Branch code (\u652F\u5E97\u30B3\u30FC\u30C9)
|
|
1053
|
+
- \`{property}_account_type\` - ENUM - Account type: 1=\u666E\u901A, 2=\u5F53\u5EA7, 4=\u8CAF\u84C4
|
|
1054
|
+
- \`{property}_account_number\` - VARCHAR(7) - Account number (\u53E3\u5EA7\u756A\u53F7)
|
|
1055
|
+
- \`{property}_account_holder\` - VARCHAR(100) - Account holder name (\u53E3\u5EA7\u540D\u7FA9)
|
|
1056
|
+
|
|
1057
|
+
\`\`\`yaml
|
|
1058
|
+
bank_account:
|
|
1059
|
+
type: JapanBankAccount
|
|
1060
|
+
\`\`\`
|
|
1061
|
+
|
|
1062
|
+
## Per-field Overrides
|
|
1063
|
+
|
|
1064
|
+
All compound types support per-field overrides:
|
|
1065
|
+
|
|
1066
|
+
\`\`\`yaml
|
|
1067
|
+
name:
|
|
1068
|
+
type: JapanName
|
|
1069
|
+
fields:
|
|
1070
|
+
KanaLastname:
|
|
1071
|
+
nullable: true
|
|
1072
|
+
hidden: true
|
|
1073
|
+
KanaFirstname:
|
|
1074
|
+
nullable: true
|
|
1075
|
+
hidden: true
|
|
1076
|
+
\`\`\`
|
|
1077
|
+
|
|
1078
|
+
**Available overrides:**
|
|
1079
|
+
- \`nullable\` - Whether the field can be NULL
|
|
1080
|
+
- \`hidden\` - Exclude from JSON/array output
|
|
1081
|
+
- \`fillable\` - Control mass assignment
|
|
1082
|
+
|
|
1083
|
+
## Factory Examples
|
|
1084
|
+
|
|
1085
|
+
\`\`\`php
|
|
1086
|
+
$faker = fake('ja_JP');
|
|
1087
|
+
|
|
1088
|
+
return [
|
|
1089
|
+
// JapanName
|
|
1090
|
+
'name_lastname' => $faker->lastName(),
|
|
1091
|
+
'name_firstname' => $faker->firstName(),
|
|
1092
|
+
'name_kana_lastname' => $faker->lastKanaName(),
|
|
1093
|
+
'name_kana_firstname' => $faker->firstKanaName(),
|
|
1094
|
+
|
|
1095
|
+
// JapanPhone
|
|
1096
|
+
'phone' => $faker->phoneNumber(),
|
|
1097
|
+
|
|
1098
|
+
// JapanPostalCode
|
|
1099
|
+
'postal_code' => $faker->postcode(),
|
|
1100
|
+
|
|
1101
|
+
// JapanAddress
|
|
1102
|
+
'address_postal_code' => $faker->postcode(),
|
|
1103
|
+
'address_prefecture_id' => $faker->numberBetween(1, 47),
|
|
1104
|
+
'address_address1' => $faker->city(),
|
|
1105
|
+
'address_address2' => $faker->streetAddress(),
|
|
1106
|
+
'address_address3' => $faker->optional(0.5)->secondaryAddress(),
|
|
1107
|
+
];
|
|
1108
|
+
\`\`\`
|
|
1109
|
+
|
|
1110
|
+
## Model Accessors
|
|
1111
|
+
|
|
1112
|
+
\`\`\`php
|
|
1113
|
+
// JapanName accessors
|
|
1114
|
+
$customer->name_full_name; // "\u7530\u4E2D \u592A\u90CE"
|
|
1115
|
+
$customer->name_full_name_kana; // "\u30BF\u30CA\u30AB \u30BF\u30ED\u30A6"
|
|
1116
|
+
|
|
1117
|
+
// JapanAddress accessor
|
|
1118
|
+
$customer->address_full_address; // "\u5343\u4EE3\u7530\u533A\u4E38\u306E\u51851-1-1\u30D3\u30EB5F"
|
|
1119
|
+
\`\`\`
|
|
1120
|
+
`;
|
|
1121
|
+
function isJapanPlugin(plugin) {
|
|
1122
|
+
return plugin.name === "@famgia/omnify-japan";
|
|
1123
|
+
}
|
|
1124
|
+
function isLaravelPlugin(plugin) {
|
|
1125
|
+
return plugin.name === "@famgia/omnify-laravel";
|
|
1126
|
+
}
|
|
1127
|
+
function isTypeScriptPlugin(plugin) {
|
|
1128
|
+
return plugin.name === "@famgia/omnify-typescript";
|
|
1129
|
+
}
|
|
1130
|
+
function generateAIGuides(rootDir, plugins) {
|
|
1131
|
+
const guidesDir = resolve5(rootDir, ".claude/omnify");
|
|
1132
|
+
let filesWritten = 0;
|
|
1133
|
+
if (!existsSync3(guidesDir)) {
|
|
1134
|
+
mkdirSync2(guidesDir, { recursive: true });
|
|
1135
|
+
}
|
|
1136
|
+
const claudeMdPath = resolve5(rootDir, "CLAUDE.md");
|
|
1137
|
+
if (!existsSync3(claudeMdPath)) {
|
|
1138
|
+
writeFileSync2(claudeMdPath, CLAUDE_MD);
|
|
1139
|
+
filesWritten++;
|
|
1140
|
+
}
|
|
1141
|
+
const schemaGuidePath = resolve5(guidesDir, "schema-guide.md");
|
|
1142
|
+
writeFileSync2(schemaGuidePath, SCHEMA_GUIDE);
|
|
1143
|
+
filesWritten++;
|
|
1144
|
+
for (const plugin of plugins) {
|
|
1145
|
+
if (isLaravelPlugin(plugin)) {
|
|
1146
|
+
const laravelGuidePath = resolve5(guidesDir, "laravel-guide.md");
|
|
1147
|
+
writeFileSync2(laravelGuidePath, LARAVEL_GUIDE);
|
|
1148
|
+
filesWritten++;
|
|
1149
|
+
}
|
|
1150
|
+
if (isTypeScriptPlugin(plugin)) {
|
|
1151
|
+
const tsGuidePath = resolve5(guidesDir, "typescript-guide.md");
|
|
1152
|
+
writeFileSync2(tsGuidePath, TYPESCRIPT_GUIDE);
|
|
1153
|
+
filesWritten++;
|
|
1154
|
+
}
|
|
1155
|
+
if (isJapanPlugin(plugin)) {
|
|
1156
|
+
const japanGuidePath = resolve5(guidesDir, "japan-guide.md");
|
|
1157
|
+
writeFileSync2(japanGuidePath, JAPAN_GUIDE);
|
|
1158
|
+
filesWritten++;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
return filesWritten;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// src/commands/generate.ts
|
|
690
1165
|
function hasPluginGenerators(plugins) {
|
|
691
1166
|
return plugins.some((p) => p.generators && p.generators.length > 0);
|
|
692
1167
|
}
|
|
@@ -867,17 +1342,17 @@ function schemaChangeToVersionChange(change) {
|
|
|
867
1342
|
function writeGeneratorOutputs(outputs, rootDir) {
|
|
868
1343
|
const counts = { migrations: 0, types: 0, models: 0, factories: 0, other: 0 };
|
|
869
1344
|
for (const output of outputs) {
|
|
870
|
-
const filePath =
|
|
1345
|
+
const filePath = resolve6(rootDir, output.path);
|
|
871
1346
|
const dir = dirname4(filePath);
|
|
872
|
-
if (!
|
|
873
|
-
|
|
1347
|
+
if (!existsSync4(dir)) {
|
|
1348
|
+
mkdirSync3(dir, { recursive: true });
|
|
874
1349
|
logger.debug(`Created directory: ${dir}`);
|
|
875
1350
|
}
|
|
876
|
-
if (output.skipIfExists &&
|
|
1351
|
+
if (output.skipIfExists && existsSync4(filePath)) {
|
|
877
1352
|
logger.debug(`Skipped (exists): ${output.path}`);
|
|
878
1353
|
continue;
|
|
879
1354
|
}
|
|
880
|
-
|
|
1355
|
+
writeFileSync3(filePath, output.content);
|
|
881
1356
|
logger.debug(`Created: ${output.path}`);
|
|
882
1357
|
if (output.type === "migration") counts.migrations++;
|
|
883
1358
|
else if (output.type === "type") counts.types++;
|
|
@@ -916,9 +1391,9 @@ function runDirectGeneration(schemas, config, rootDir, options, changes) {
|
|
|
916
1391
|
let modelsGenerated = 0;
|
|
917
1392
|
if (!options.typesOnly && config.output.laravel) {
|
|
918
1393
|
logger.step("Generating Laravel migrations...");
|
|
919
|
-
const migrationsDir =
|
|
920
|
-
if (!
|
|
921
|
-
|
|
1394
|
+
const migrationsDir = resolve6(rootDir, config.output.laravel.migrationsPath);
|
|
1395
|
+
if (!existsSync4(migrationsDir)) {
|
|
1396
|
+
mkdirSync3(migrationsDir, { recursive: true });
|
|
922
1397
|
logger.debug(`Created directory: ${migrationsDir}`);
|
|
923
1398
|
}
|
|
924
1399
|
const addedSchemaNames = new Set(
|
|
@@ -933,8 +1408,8 @@ function runDirectGeneration(schemas, config, rootDir, options, changes) {
|
|
|
933
1408
|
);
|
|
934
1409
|
const createMigrations = generateMigrations(addedSchemas);
|
|
935
1410
|
for (const migration of createMigrations) {
|
|
936
|
-
const filePath =
|
|
937
|
-
|
|
1411
|
+
const filePath = resolve6(migrationsDir, migration.fileName);
|
|
1412
|
+
writeFileSync3(filePath, migration.content);
|
|
938
1413
|
logger.debug(`Created: ${migration.fileName}`);
|
|
939
1414
|
migrationsGenerated++;
|
|
940
1415
|
}
|
|
@@ -942,8 +1417,8 @@ function runDirectGeneration(schemas, config, rootDir, options, changes) {
|
|
|
942
1417
|
if (alterChanges.length > 0) {
|
|
943
1418
|
const alterMigrations = generateMigrationsFromChanges(alterChanges);
|
|
944
1419
|
for (const migration of alterMigrations) {
|
|
945
|
-
const filePath =
|
|
946
|
-
|
|
1420
|
+
const filePath = resolve6(migrationsDir, migration.fileName);
|
|
1421
|
+
writeFileSync3(filePath, migration.content);
|
|
947
1422
|
logger.debug(`Created: ${migration.fileName}`);
|
|
948
1423
|
migrationsGenerated++;
|
|
949
1424
|
}
|
|
@@ -954,29 +1429,29 @@ function runDirectGeneration(schemas, config, rootDir, options, changes) {
|
|
|
954
1429
|
logger.step("Generating Laravel models...");
|
|
955
1430
|
const modelsPath = config.output.laravel.modelsPath;
|
|
956
1431
|
const baseModelsPath = config.output.laravel.baseModelsPath ?? `${modelsPath}/OmnifyBase`;
|
|
957
|
-
const modelsDir =
|
|
958
|
-
const baseModelsDir =
|
|
959
|
-
if (!
|
|
960
|
-
|
|
1432
|
+
const modelsDir = resolve6(rootDir, modelsPath);
|
|
1433
|
+
const baseModelsDir = resolve6(rootDir, baseModelsPath);
|
|
1434
|
+
if (!existsSync4(modelsDir)) {
|
|
1435
|
+
mkdirSync3(modelsDir, { recursive: true });
|
|
961
1436
|
}
|
|
962
|
-
if (!
|
|
963
|
-
|
|
1437
|
+
if (!existsSync4(baseModelsDir)) {
|
|
1438
|
+
mkdirSync3(baseModelsDir, { recursive: true });
|
|
964
1439
|
}
|
|
965
1440
|
const models = generateModels(schemas, {
|
|
966
1441
|
modelPath: modelsPath,
|
|
967
1442
|
baseModelPath: baseModelsPath
|
|
968
1443
|
});
|
|
969
1444
|
for (const model of models) {
|
|
970
|
-
const filePath =
|
|
1445
|
+
const filePath = resolve6(rootDir, getModelPath(model));
|
|
971
1446
|
const fileDir = dirname4(filePath);
|
|
972
|
-
if (!
|
|
973
|
-
|
|
1447
|
+
if (!existsSync4(fileDir)) {
|
|
1448
|
+
mkdirSync3(fileDir, { recursive: true });
|
|
974
1449
|
}
|
|
975
|
-
if (!model.overwrite &&
|
|
1450
|
+
if (!model.overwrite && existsSync4(filePath)) {
|
|
976
1451
|
logger.debug(`Skipped (exists): ${getModelPath(model)}`);
|
|
977
1452
|
continue;
|
|
978
1453
|
}
|
|
979
|
-
|
|
1454
|
+
writeFileSync3(filePath, model.content);
|
|
980
1455
|
logger.debug(`Created: ${getModelPath(model)}`);
|
|
981
1456
|
modelsGenerated++;
|
|
982
1457
|
}
|
|
@@ -984,23 +1459,23 @@ function runDirectGeneration(schemas, config, rootDir, options, changes) {
|
|
|
984
1459
|
}
|
|
985
1460
|
if (!options.migrationsOnly && config.output.typescript) {
|
|
986
1461
|
logger.step("Generating TypeScript types...");
|
|
987
|
-
const typesDir =
|
|
988
|
-
if (!
|
|
989
|
-
|
|
1462
|
+
const typesDir = resolve6(rootDir, config.output.typescript.path);
|
|
1463
|
+
if (!existsSync4(typesDir)) {
|
|
1464
|
+
mkdirSync3(typesDir, { recursive: true });
|
|
990
1465
|
logger.debug(`Created directory: ${typesDir}`);
|
|
991
1466
|
}
|
|
992
1467
|
const typeFiles = generateTypeScript(schemas);
|
|
993
1468
|
for (const file of typeFiles) {
|
|
994
|
-
const filePath =
|
|
1469
|
+
const filePath = resolve6(typesDir, file.filePath);
|
|
995
1470
|
const fileDir = dirname4(filePath);
|
|
996
|
-
if (!
|
|
997
|
-
|
|
1471
|
+
if (!existsSync4(fileDir)) {
|
|
1472
|
+
mkdirSync3(fileDir, { recursive: true });
|
|
998
1473
|
}
|
|
999
|
-
if (!file.overwrite &&
|
|
1474
|
+
if (!file.overwrite && existsSync4(filePath)) {
|
|
1000
1475
|
logger.debug(`Skipped (exists): ${file.filePath}`);
|
|
1001
1476
|
continue;
|
|
1002
1477
|
}
|
|
1003
|
-
|
|
1478
|
+
writeFileSync3(filePath, file.content);
|
|
1004
1479
|
logger.debug(`Created: ${file.filePath}`);
|
|
1005
1480
|
typesGenerated++;
|
|
1006
1481
|
}
|
|
@@ -1015,7 +1490,7 @@ async function runGenerate(options) {
|
|
|
1015
1490
|
const { config, configPath } = await loadConfig();
|
|
1016
1491
|
const rootDir = configPath ? dirname4(configPath) : process.cwd();
|
|
1017
1492
|
validateConfig(config, rootDir);
|
|
1018
|
-
const schemaPath =
|
|
1493
|
+
const schemaPath = resolve6(rootDir, config.schemasDir);
|
|
1019
1494
|
logger.step(`Loading schemas from ${schemaPath}`);
|
|
1020
1495
|
const schemas = await loadSchemas3(schemaPath);
|
|
1021
1496
|
const schemaCount = Object.keys(schemas).length;
|
|
@@ -1045,7 +1520,7 @@ async function runGenerate(options) {
|
|
|
1045
1520
|
process.exit(2);
|
|
1046
1521
|
}
|
|
1047
1522
|
logger.step("Checking for changes...");
|
|
1048
|
-
const lockPath =
|
|
1523
|
+
const lockPath = resolve6(rootDir, config.lockFilePath);
|
|
1049
1524
|
const existingLock = await readLockFile(lockPath);
|
|
1050
1525
|
const currentSnapshots = await buildSchemaSnapshots(schemas);
|
|
1051
1526
|
const v2Lock = existingLock && isLockFileV2(existingLock) ? existingLock : null;
|
|
@@ -1119,6 +1594,14 @@ async function runGenerate(options) {
|
|
|
1119
1594
|
} catch (versionError) {
|
|
1120
1595
|
logger.debug(`Could not save version history: ${versionError.message}`);
|
|
1121
1596
|
}
|
|
1597
|
+
try {
|
|
1598
|
+
const guidesWritten = generateAIGuides(rootDir, config.plugins);
|
|
1599
|
+
if (guidesWritten > 0) {
|
|
1600
|
+
logger.debug(`Updated ${guidesWritten} AI guide file(s)`);
|
|
1601
|
+
}
|
|
1602
|
+
} catch (guideError) {
|
|
1603
|
+
logger.debug(`Could not generate AI guides: ${guideError.message}`);
|
|
1604
|
+
}
|
|
1122
1605
|
logger.newline();
|
|
1123
1606
|
logger.success("Generation complete!");
|
|
1124
1607
|
if (migrationsGenerated > 0 && config.output.laravel) {
|