@famgia/omnify-typescript 0.0.67 → 0.0.69

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.
Files changed (43) hide show
  1. package/dist/{chunk-4L77AHAC.js → chunk-6I4O23X6.js} +521 -66
  2. package/dist/chunk-6I4O23X6.js.map +1 -0
  3. package/dist/index.cjs +761 -65
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +138 -2
  6. package/dist/index.d.ts +138 -2
  7. package/dist/index.js +227 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/plugin.cjs +624 -75
  10. package/dist/plugin.cjs.map +1 -1
  11. package/dist/plugin.d.cts +6 -0
  12. package/dist/plugin.d.ts +6 -0
  13. package/dist/plugin.js +96 -11
  14. package/dist/plugin.js.map +1 -1
  15. package/package.json +3 -3
  16. package/scripts/postinstall.js +29 -40
  17. package/stubs/JapaneseAddressField.tsx.stub +289 -0
  18. package/stubs/JapaneseBankField.tsx.stub +212 -0
  19. package/stubs/JapaneseNameField.tsx.stub +194 -0
  20. package/stubs/ai-guides/checklists/react.md.stub +108 -0
  21. package/stubs/ai-guides/cursor/react-design.mdc.stub +289 -0
  22. package/stubs/ai-guides/cursor/react-form.mdc.stub +277 -0
  23. package/stubs/ai-guides/cursor/react-services.mdc.stub +304 -0
  24. package/stubs/ai-guides/cursor/react.mdc.stub +254 -0
  25. package/stubs/ai-guides/react/README.md.stub +221 -0
  26. package/stubs/ai-guides/react/antd-guide.md.stub +294 -0
  27. package/stubs/ai-guides/react/checklist.md.stub +108 -0
  28. package/stubs/ai-guides/react/datetime-guide.md.stub +137 -0
  29. package/stubs/ai-guides/react/design-philosophy.md.stub +363 -0
  30. package/stubs/ai-guides/react/i18n-guide.md.stub +211 -0
  31. package/stubs/ai-guides/react/laravel-integration.md.stub +181 -0
  32. package/stubs/ai-guides/react/service-pattern.md.stub +180 -0
  33. package/stubs/ai-guides/react/tanstack-query.md.stub +339 -0
  34. package/stubs/ai-guides/react/types-guide.md.stub +524 -0
  35. package/stubs/components-index.ts.stub +13 -0
  36. package/stubs/form-validation.ts.stub +106 -0
  37. package/stubs/rules/index.ts.stub +48 -0
  38. package/stubs/rules/kana.ts.stub +291 -0
  39. package/stubs/use-form-mutation.ts.stub +117 -0
  40. package/stubs/zod-i18n.ts.stub +32 -0
  41. package/ai-guides/antdesign-guide.md +0 -401
  42. package/ai-guides/typescript-guide.md +0 -310
  43. package/dist/chunk-4L77AHAC.js.map +0 -1
package/dist/plugin.d.cts CHANGED
@@ -41,6 +41,12 @@ interface TypeScriptPluginOptions {
41
41
  * @default true
42
42
  */
43
43
  generateZodSchemas?: boolean;
44
+ /**
45
+ * Path for React utility stubs (hooks, components, lib).
46
+ * Set to false to disable stub generation.
47
+ * @default 'omnify'
48
+ */
49
+ stubsPath?: string | false;
44
50
  }
45
51
  /**
46
52
  * Creates the TypeScript plugin with the specified options.
package/dist/plugin.d.ts CHANGED
@@ -41,6 +41,12 @@ interface TypeScriptPluginOptions {
41
41
  * @default true
42
42
  */
43
43
  generateZodSchemas?: boolean;
44
+ /**
45
+ * Path for React utility stubs (hooks, components, lib).
46
+ * Set to false to disable stub generation.
47
+ * @default 'omnify'
48
+ */
49
+ stubsPath?: string | false;
44
50
  }
45
51
  /**
46
52
  * Creates the TypeScript plugin with the specified options.
package/dist/plugin.js CHANGED
@@ -1,8 +1,13 @@
1
1
  import {
2
2
  generateTypeScript
3
- } from "./chunk-4L77AHAC.js";
3
+ } from "./chunk-6I4O23X6.js";
4
4
 
5
5
  // src/plugin.ts
6
+ import fs from "fs";
7
+ import path from "path";
8
+ import { fileURLToPath } from "url";
9
+ var __filename = fileURLToPath(import.meta.url);
10
+ var __dirname = path.dirname(__filename);
6
11
  var TYPESCRIPT_CONFIG_SCHEMA = {
7
12
  fields: [
8
13
  {
@@ -20,15 +25,38 @@ var TYPESCRIPT_CONFIG_SCHEMA = {
20
25
  description: "Generate Zod schemas alongside TypeScript types for form validation",
21
26
  default: true,
22
27
  group: "output"
28
+ },
29
+ {
30
+ key: "stubsPath",
31
+ type: "path",
32
+ label: "React Stubs Path",
33
+ description: "Directory for React utility stubs (hooks, components). Leave empty to disable.",
34
+ default: "omnify",
35
+ group: "output"
23
36
  }
24
37
  ]
25
38
  };
26
39
  function resolveOptions(options) {
27
40
  return {
28
41
  modelsPath: options?.modelsPath ?? "types/schemas",
29
- generateZodSchemas: options?.generateZodSchemas ?? true
42
+ generateZodSchemas: options?.generateZodSchemas ?? true,
43
+ stubsPath: options?.stubsPath ?? "omnify"
30
44
  };
31
45
  }
46
+ var STUB_FILES = [
47
+ {
48
+ stub: "CompoundField.tsx.stub",
49
+ output: "components/CompoundField.tsx"
50
+ },
51
+ {
52
+ stub: "use-form-mutation.ts.stub",
53
+ output: "hooks/use-form-mutation.ts"
54
+ },
55
+ {
56
+ stub: "form-validation.ts.stub",
57
+ output: "lib/form-validation.ts"
58
+ }
59
+ ];
32
60
  function typescriptPlugin(options) {
33
61
  const resolved = resolveOptions(options);
34
62
  return {
@@ -43,17 +71,74 @@ function typescriptPlugin(options) {
43
71
  const files = generateTypeScript(ctx.schemas, {
44
72
  generateZodSchemas: resolved.generateZodSchemas,
45
73
  localeConfig: ctx.localeConfig,
46
- customTypes: ctx.customTypes
74
+ customTypes: ctx.customTypes,
75
+ pluginEnums: ctx.pluginEnums
47
76
  });
48
- return files.map((file) => ({
49
- path: `${resolved.modelsPath}/${file.filePath}`,
50
- content: file.content,
51
- type: "type",
52
- overwrite: file.overwrite,
53
- metadata: {
54
- types: file.types
77
+ return files.map((file) => {
78
+ let outputPath;
79
+ if (file.category === "enum") {
80
+ const enumPath = resolved.modelsPath.replace(/\/schemas\/?$/, "/enum");
81
+ outputPath = `${enumPath}/${file.filePath}`;
82
+ } else {
83
+ outputPath = `${resolved.modelsPath}/${file.filePath}`;
55
84
  }
56
- }));
85
+ return {
86
+ path: outputPath,
87
+ content: file.content,
88
+ type: "type",
89
+ skipIfExists: !file.overwrite,
90
+ // Invert: overwrite=true means skipIfExists=false
91
+ metadata: {
92
+ types: file.types
93
+ }
94
+ };
95
+ });
96
+ }
97
+ },
98
+ {
99
+ name: "typescript-stubs",
100
+ description: "Generate React utility stubs (hooks, components)",
101
+ generate: async () => {
102
+ if (resolved.stubsPath === false) {
103
+ return [];
104
+ }
105
+ const outputs = [];
106
+ const stubsDir = path.join(__dirname, "..", "stubs");
107
+ for (const { stub, output } of STUB_FILES) {
108
+ const stubPath = path.join(stubsDir, stub);
109
+ if (fs.existsSync(stubPath)) {
110
+ const content = fs.readFileSync(stubPath, "utf-8");
111
+ outputs.push({
112
+ path: `${resolved.stubsPath}/${output}`,
113
+ content,
114
+ type: "other",
115
+ skipIfExists: true
116
+ // Never overwrite - user can customize
117
+ });
118
+ }
119
+ }
120
+ outputs.push({
121
+ path: `${resolved.stubsPath}/components/index.ts`,
122
+ content: `export { CompoundField, type FieldConfig } from './CompoundField';
123
+ `,
124
+ type: "other",
125
+ skipIfExists: true
126
+ });
127
+ outputs.push({
128
+ path: `${resolved.stubsPath}/hooks/index.ts`,
129
+ content: `export { useFormMutation } from './use-form-mutation';
130
+ `,
131
+ type: "other",
132
+ skipIfExists: true
133
+ });
134
+ outputs.push({
135
+ path: `${resolved.stubsPath}/lib/index.ts`,
136
+ content: `export { zodRule, requiredRule } from './form-validation';
137
+ `,
138
+ type: "other",
139
+ skipIfExists: true
140
+ });
141
+ return outputs;
57
142
  }
58
143
  }
59
144
  ]
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["/**\n * @famgia/omnify-typescript - Plugin\n *\n * Plugin for generating TypeScript type definitions and Zod schemas from Omnify schemas.\n *\n * Output structure:\n * - types/schemas/base/[SchemaName].ts - Auto-generated base interfaces + Zod schemas, always overwritten\n * - types/schemas/enum/[EnumName].ts - Auto-generated enums/type aliases, always overwritten\n * - types/schemas/[SchemaName].ts - Extends base, user can customize, never overwritten\n * - types/schemas/index.ts - Re-exports, always overwritten\n *\n * @example\n * ```typescript\n * import { defineConfig } from '@famgia/omnify';\n * import typescript from '@famgia/omnify-typescript/plugin';\n *\n * export default defineConfig({\n * plugins: [\n * typescript({\n * modelsPath: 'types/schemas', // default\n * generateZodSchemas: true, // default\n * }),\n * ],\n * });\n * ```\n */\n\nimport type { OmnifyPlugin, GeneratorOutput, GeneratorContext, PluginConfigSchema } from '@famgia/omnify-types';\nimport { generateTypeScript } from './generator.js';\n\n/**\n * Configuration schema for TypeScript plugin UI settings\n */\nconst TYPESCRIPT_CONFIG_SCHEMA: PluginConfigSchema = {\n fields: [\n {\n key: 'modelsPath',\n type: 'path',\n label: 'Schemas Output Path',\n description: 'Directory for generated TypeScript types and Zod schemas',\n default: 'types/schemas',\n group: 'output',\n },\n {\n key: 'generateZodSchemas',\n type: 'boolean',\n label: 'Generate Zod Schemas',\n description: 'Generate Zod schemas alongside TypeScript types for form validation',\n default: true,\n group: 'output',\n },\n ],\n};\n\n/**\n * Options for the TypeScript plugin.\n */\nexport interface TypeScriptPluginOptions {\n /**\n * Path for TypeScript model files.\n * @default 'types/schemas'\n */\n modelsPath?: string;\n /**\n * Generate Zod schemas alongside TypeScript types.\n * @default true\n */\n generateZodSchemas?: boolean;\n}\n\n/**\n * Resolved options with defaults applied.\n */\ninterface ResolvedOptions {\n modelsPath: string;\n generateZodSchemas: boolean;\n}\n\n/**\n * Resolves options with defaults.\n */\nfunction resolveOptions(options?: TypeScriptPluginOptions): ResolvedOptions {\n return {\n modelsPath: options?.modelsPath ?? 'types/schemas',\n generateZodSchemas: options?.generateZodSchemas ?? true,\n };\n}\n\n/**\n * Creates the TypeScript plugin with the specified options.\n *\n * @param options - Plugin configuration options\n * @returns OmnifyPlugin configured for TypeScript generation\n */\nexport default function typescriptPlugin(options?: TypeScriptPluginOptions): OmnifyPlugin {\n const resolved = resolveOptions(options);\n\n return {\n name: '@famgia/omnify-typescript',\n version: '0.0.1',\n configSchema: TYPESCRIPT_CONFIG_SCHEMA,\n\n generators: [\n {\n name: 'typescript-models',\n description: 'Generate TypeScript model definitions',\n\n generate: async (ctx: GeneratorContext): Promise<GeneratorOutput[]> => {\n const files = generateTypeScript(ctx.schemas, {\n generateZodSchemas: resolved.generateZodSchemas,\n localeConfig: ctx.localeConfig,\n customTypes: ctx.customTypes,\n });\n\n return files.map((file) => ({\n path: `${resolved.modelsPath}/${file.filePath}`,\n content: file.content,\n type: 'type' as const,\n overwrite: file.overwrite,\n metadata: {\n types: file.types,\n },\n }));\n },\n },\n ],\n };\n}\n\n// Named export for flexibility\nexport { typescriptPlugin };\n"],"mappings":";;;;;AAiCA,IAAM,2BAA+C;AAAA,EACnD,QAAQ;AAAA,IACN;AAAA,MACE,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,EACF;AACF;AA6BA,SAAS,eAAe,SAAoD;AAC1E,SAAO;AAAA,IACL,YAAY,SAAS,cAAc;AAAA,IACnC,oBAAoB,SAAS,sBAAsB;AAAA,EACrD;AACF;AAQe,SAAR,iBAAkC,SAAiD;AACxF,QAAM,WAAW,eAAe,OAAO;AAEvC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IACT,cAAc;AAAA,IAEd,YAAY;AAAA,MACV;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QAEb,UAAU,OAAO,QAAsD;AACrE,gBAAM,QAAQ,mBAAmB,IAAI,SAAS;AAAA,YAC5C,oBAAoB,SAAS;AAAA,YAC7B,cAAc,IAAI;AAAA,YAClB,aAAa,IAAI;AAAA,UACnB,CAAC;AAED,iBAAO,MAAM,IAAI,CAAC,UAAU;AAAA,YAC1B,MAAM,GAAG,SAAS,UAAU,IAAI,KAAK,QAAQ;AAAA,YAC7C,SAAS,KAAK;AAAA,YACd,MAAM;AAAA,YACN,WAAW,KAAK;AAAA,YAChB,UAAU;AAAA,cACR,OAAO,KAAK;AAAA,YACd;AAAA,UACF,EAAE;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["/**\n * @famgia/omnify-typescript - Plugin\n *\n * Plugin for generating TypeScript type definitions and Zod schemas from Omnify schemas.\n *\n * Output structure:\n * - types/schemas/base/[SchemaName].ts - Auto-generated base interfaces + Zod schemas, always overwritten\n * - types/schemas/enum/[EnumName].ts - Auto-generated enums/type aliases, always overwritten\n * - types/schemas/[SchemaName].ts - Extends base, user can customize, never overwritten\n * - types/schemas/index.ts - Re-exports, always overwritten\n *\n * @example\n * ```typescript\n * import { defineConfig } from '@famgia/omnify';\n * import typescript from '@famgia/omnify-typescript/plugin';\n *\n * export default defineConfig({\n * plugins: [\n * typescript({\n * modelsPath: 'types/schemas', // default\n * generateZodSchemas: true, // default\n * }),\n * ],\n * });\n * ```\n */\n\nimport type { OmnifyPlugin, GeneratorOutput, GeneratorContext, PluginConfigSchema } from '@famgia/omnify-types';\nimport { generateTypeScript } from './generator.js';\nimport fs from 'fs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n/**\n * Configuration schema for TypeScript plugin UI settings\n */\nconst TYPESCRIPT_CONFIG_SCHEMA: PluginConfigSchema = {\n fields: [\n {\n key: 'modelsPath',\n type: 'path',\n label: 'Schemas Output Path',\n description: 'Directory for generated TypeScript types and Zod schemas',\n default: 'types/schemas',\n group: 'output',\n },\n {\n key: 'generateZodSchemas',\n type: 'boolean',\n label: 'Generate Zod Schemas',\n description: 'Generate Zod schemas alongside TypeScript types for form validation',\n default: true,\n group: 'output',\n },\n {\n key: 'stubsPath',\n type: 'path',\n label: 'React Stubs Path',\n description: 'Directory for React utility stubs (hooks, components). Leave empty to disable.',\n default: 'omnify',\n group: 'output',\n },\n ],\n};\n\n/**\n * Options for the TypeScript plugin.\n */\nexport interface TypeScriptPluginOptions {\n /**\n * Path for TypeScript model files.\n * @default 'types/schemas'\n */\n modelsPath?: string;\n /**\n * Generate Zod schemas alongside TypeScript types.\n * @default true\n */\n generateZodSchemas?: boolean;\n /**\n * Path for React utility stubs (hooks, components, lib).\n * Set to false to disable stub generation.\n * @default 'omnify'\n */\n stubsPath?: string | false;\n}\n\n/**\n * Resolved options with defaults applied.\n */\ninterface ResolvedOptions {\n modelsPath: string;\n generateZodSchemas: boolean;\n stubsPath: string | false;\n}\n\n/**\n * Resolves options with defaults.\n */\nfunction resolveOptions(options?: TypeScriptPluginOptions): ResolvedOptions {\n return {\n modelsPath: options?.modelsPath ?? 'types/schemas',\n generateZodSchemas: options?.generateZodSchemas ?? true,\n stubsPath: options?.stubsPath ?? 'omnify',\n };\n}\n\n/**\n * Stub file definitions for React utilities.\n */\nconst STUB_FILES = [\n {\n stub: 'CompoundField.tsx.stub',\n output: 'components/CompoundField.tsx',\n },\n {\n stub: 'use-form-mutation.ts.stub',\n output: 'hooks/use-form-mutation.ts',\n },\n {\n stub: 'form-validation.ts.stub',\n output: 'lib/form-validation.ts',\n },\n];\n\n/**\n * Creates the TypeScript plugin with the specified options.\n *\n * @param options - Plugin configuration options\n * @returns OmnifyPlugin configured for TypeScript generation\n */\nexport default function typescriptPlugin(options?: TypeScriptPluginOptions): OmnifyPlugin {\n const resolved = resolveOptions(options);\n\n return {\n name: '@famgia/omnify-typescript',\n version: '0.0.1',\n configSchema: TYPESCRIPT_CONFIG_SCHEMA,\n\n generators: [\n {\n name: 'typescript-models',\n description: 'Generate TypeScript model definitions',\n\n generate: async (ctx: GeneratorContext): Promise<GeneratorOutput[]> => {\n const files = generateTypeScript(ctx.schemas, {\n generateZodSchemas: resolved.generateZodSchemas,\n localeConfig: ctx.localeConfig,\n customTypes: ctx.customTypes,\n pluginEnums: ctx.pluginEnums,\n });\n\n return files.map((file) => {\n // Determine output path based on category\n let outputPath: string;\n if (file.category === 'enum') {\n // Enum files go to ../enum/ folder (sibling to schemas)\n const enumPath = resolved.modelsPath.replace(/\\/schemas\\/?$/, '/enum');\n outputPath = `${enumPath}/${file.filePath}`;\n } else {\n outputPath = `${resolved.modelsPath}/${file.filePath}`;\n }\n\n return {\n path: outputPath,\n content: file.content,\n type: 'type' as const,\n skipIfExists: !file.overwrite, // Invert: overwrite=true means skipIfExists=false\n metadata: {\n types: file.types,\n },\n };\n });\n },\n },\n {\n name: 'typescript-stubs',\n description: 'Generate React utility stubs (hooks, components)',\n\n generate: async (): Promise<GeneratorOutput[]> => {\n // Skip if stubs disabled\n if (resolved.stubsPath === false) {\n return [];\n }\n\n const outputs: GeneratorOutput[] = [];\n const stubsDir = path.join(__dirname, '..', 'stubs');\n\n for (const { stub, output } of STUB_FILES) {\n const stubPath = path.join(stubsDir, stub);\n if (fs.existsSync(stubPath)) {\n const content = fs.readFileSync(stubPath, 'utf-8');\n outputs.push({\n path: `${resolved.stubsPath}/${output}`,\n content,\n type: 'other' as const,\n skipIfExists: true, // Never overwrite - user can customize\n });\n }\n }\n\n // Generate index files\n outputs.push({\n path: `${resolved.stubsPath}/components/index.ts`,\n content: `export { CompoundField, type FieldConfig } from './CompoundField';\\n`,\n type: 'other' as const,\n skipIfExists: true,\n });\n\n outputs.push({\n path: `${resolved.stubsPath}/hooks/index.ts`,\n content: `export { useFormMutation } from './use-form-mutation';\\n`,\n type: 'other' as const,\n skipIfExists: true,\n });\n\n outputs.push({\n path: `${resolved.stubsPath}/lib/index.ts`,\n content: `export { zodRule, requiredRule } from './form-validation';\\n`,\n type: 'other' as const,\n skipIfExists: true,\n });\n\n return outputs;\n },\n },\n ],\n };\n}\n\n// Named export for flexibility\nexport { typescriptPlugin };\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@famgia/omnify-typescript",
3
- "version": "0.0.67",
3
+ "version": "0.0.69",
4
4
  "description": "TypeScript type definitions generator for Omnify schemas",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -31,7 +31,7 @@
31
31
  "files": [
32
32
  "dist",
33
33
  "scripts",
34
- "ai-guides"
34
+ "stubs"
35
35
  ],
36
36
  "keywords": [
37
37
  "omnify",
@@ -48,7 +48,7 @@
48
48
  "directory": "packages/typescript-generator"
49
49
  },
50
50
  "dependencies": {
51
- "@famgia/omnify-types": "0.0.77"
51
+ "@famgia/omnify-types": "0.0.79"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "zod": "^3.0.0"
@@ -1,5 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ /**
4
+ * @famgia/omnify-typescript postinstall
5
+ * Copies TypeScript AI guides to .claude/omnify/
6
+ */
7
+
3
8
  import fs from 'fs';
4
9
  import path from 'path';
5
10
  import { fileURLToPath } from 'url';
@@ -7,64 +12,48 @@ import { fileURLToPath } from 'url';
7
12
  const __filename = fileURLToPath(import.meta.url);
8
13
  const __dirname = path.dirname(__filename);
9
14
 
10
- function findProjectRoot() {
11
- // npm/pnpm set INIT_CWD to the directory where the install was run
12
- let dir = process.env.INIT_CWD || process.cwd();
15
+ function main() {
16
+ // Skip in CI
17
+ if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) {
18
+ return;
19
+ }
20
+
21
+ // Skip in monorepo source (but allow examples/)
22
+ const projectDir = process.env.INIT_CWD || process.cwd();
23
+ if (projectDir.includes('omnify-ts') && !projectDir.includes('omnify-ts/examples')) {
24
+ return;
25
+ }
26
+
27
+ // Find project root
28
+ let dir = projectDir;
13
29
  const nodeModulesIndex = dir.indexOf('node_modules');
14
30
  if (nodeModulesIndex !== -1) {
15
31
  dir = dir.substring(0, nodeModulesIndex - 1);
16
32
  }
17
- const packageJsonPath = path.join(dir, 'package.json');
18
- if (fs.existsSync(packageJsonPath)) {
19
- return dir;
33
+ if (!fs.existsSync(path.join(dir, 'package.json'))) {
34
+ return;
20
35
  }
21
- return null;
22
- }
23
36
 
24
- function copyAiGuidesToProject(projectRoot) {
25
- const omnifyDir = path.join(projectRoot, '.claude', 'omnify');
37
+ // Copy ai-guides to .claude/omnify/
38
+ const omnifyDir = path.join(dir, '.claude', 'omnify');
26
39
  const aiGuidesDir = path.join(__dirname, '..', 'ai-guides');
27
40
 
28
41
  try {
29
- // Create target directory
30
42
  if (!fs.existsSync(omnifyDir)) {
31
43
  fs.mkdirSync(omnifyDir, { recursive: true });
32
44
  }
33
45
 
34
- // Copy all files from ai-guides directory
35
46
  if (fs.existsSync(aiGuidesDir)) {
36
- const files = fs.readdirSync(aiGuidesDir);
37
- for (const file of files) {
38
- const srcPath = path.join(aiGuidesDir, file);
39
- const destPath = path.join(omnifyDir, file);
40
-
41
- if (fs.statSync(srcPath).isFile()) {
42
- fs.copyFileSync(srcPath, destPath);
43
- console.log(` Created .claude/omnify/${file}`);
47
+ for (const file of fs.readdirSync(aiGuidesDir)) {
48
+ const src = path.join(aiGuidesDir, file);
49
+ const dest = path.join(omnifyDir, file);
50
+ if (fs.statSync(src).isFile()) {
51
+ fs.copyFileSync(src, dest);
44
52
  }
45
53
  }
46
54
  }
47
-
48
- return true;
49
55
  } catch {
50
- return false;
51
- }
52
- }
53
-
54
- function main() {
55
- if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) {
56
- return;
57
- }
58
-
59
- // Skip if in omnify-ts monorepo (source code), but allow examples/
60
- const projectDir = process.env.INIT_CWD || process.cwd();
61
- if (projectDir.includes('omnify-ts') && !projectDir.includes('omnify-ts/examples')) {
62
- return;
63
- }
64
-
65
- const projectRoot = findProjectRoot();
66
- if (projectRoot) {
67
- copyAiGuidesToProject(projectRoot);
56
+ // Silent fail
68
57
  }
69
58
  }
70
59
 
@@ -0,0 +1,289 @@
1
+ /**
2
+ * JapaneseAddressField - Japanese address input component
3
+ * Handles postal code, prefecture, address fields with postal code lookup
4
+ */
5
+ import { useState, useCallback } from 'react';
6
+ import { Form, Input, Select, Button, message } from 'antd';
7
+ import { SearchOutlined, LoadingOutlined } from '@ant-design/icons';
8
+ import type { FormInstance } from 'antd';
9
+ import type { RuleObject } from 'antd/es/form';
10
+ import { zodRule } from '../lib/form-validation';
11
+ import { getZodLocale } from '../lib/zod-i18n';
12
+ import { Prefecture, PrefectureValues, getPrefectureLabel, getPrefectureExtra } from '../enum/plugin/Prefecture';
13
+
14
+ interface I18nConfig {
15
+ fields: Record<string, { label?: Record<string, string>; placeholder?: Record<string, string> }>;
16
+ }
17
+
18
+ interface SelectOption {
19
+ value: string | number;
20
+ label: string;
21
+ }
22
+
23
+ interface PostalLookupResult {
24
+ prefecture?: string;
25
+ prefectureCode?: string;
26
+ prefectureName?: string;
27
+ address1?: string;
28
+ address2?: string;
29
+ }
30
+
31
+ interface JapaneseAddressFieldProps {
32
+ form: FormInstance;
33
+ schemas: Record<string, unknown>;
34
+ i18n: I18nConfig;
35
+ prefix?: string;
36
+ usePrefectureId?: boolean;
37
+ prefectureOptions?: SelectOption[];
38
+ enablePostalLookup?: boolean;
39
+ showSearchButton?: boolean;
40
+ autoSearch?: boolean;
41
+ onPostalLookup?: (postalCode: string) => Promise<PostalLookupResult | null>;
42
+ }
43
+
44
+ function getLabel(i18n: I18nConfig, field: string, locale: string): string {
45
+ return i18n.fields[field]?.label?.[locale] ?? i18n.fields[field]?.label?.['en'] ?? field;
46
+ }
47
+
48
+ function getPlaceholder(i18n: I18nConfig, field: string, locale: string): string {
49
+ return i18n.fields[field]?.placeholder?.[locale] ?? i18n.fields[field]?.placeholder?.['en'] ?? '';
50
+ }
51
+
52
+ // Build prefecture code to string key mapping from generated enum
53
+ const PREFECTURE_CODE_TO_KEY: Record<string, string> = Object.fromEntries(
54
+ PrefectureValues.map((key) => {
55
+ const extra = getPrefectureExtra(key);
56
+ return [String(extra?.code ?? ''), key];
57
+ })
58
+ );
59
+
60
+ // Build default prefecture options from generated enum
61
+ function buildPrefectureOptions(locale: string): SelectOption[] {
62
+ return PrefectureValues.map((key) => ({
63
+ value: key,
64
+ label: getPrefectureLabel(key, locale),
65
+ }));
66
+ }
67
+
68
+ // Default postal lookup function using zipcloud API
69
+ async function lookupPostalCode(postalCode: string): Promise<PostalLookupResult | null> {
70
+ try {
71
+ const response = await fetch(`https://zipcloud.ibsnet.co.jp/api/search?zipcode=${postalCode}`);
72
+ const data = await response.json();
73
+
74
+ if (data.results && data.results.length > 0) {
75
+ const result = data.results[0];
76
+ const prefectureKey = PREFECTURE_CODE_TO_KEY[result.prefcode];
77
+ return {
78
+ prefecture: prefectureKey,
79
+ prefectureCode: result.prefcode,
80
+ prefectureName: result.address1,
81
+ address1: result.address2,
82
+ address2: result.address3,
83
+ };
84
+ }
85
+ return null;
86
+ } catch (error) {
87
+ console.error('Postal code lookup failed:', error);
88
+ return null;
89
+ }
90
+ }
91
+
92
+ // Localized messages
93
+ const MESSAGES = {
94
+ ja: {
95
+ searchAddress: '住所検索',
96
+ searching: '検索中...',
97
+ notFound: '郵便番号が見つかりませんでした',
98
+ error: '住所検索に失敗しました',
99
+ invalidFormat: '郵便番号の形式が正しくありません(例:123-4567)',
100
+ },
101
+ en: {
102
+ searchAddress: 'Search Address',
103
+ searching: 'Searching...',
104
+ notFound: 'Postal code not found',
105
+ error: 'Address lookup failed',
106
+ invalidFormat: 'Invalid postal code format (e.g., 123-4567)',
107
+ },
108
+ vi: {
109
+ searchAddress: 'Tìm địa chỉ',
110
+ searching: 'Đang tìm...',
111
+ notFound: 'Không tìm thấy mã bưu điện',
112
+ error: 'Tìm địa chỉ thất bại',
113
+ invalidFormat: 'Định dạng mã bưu điện không hợp lệ (VD: 123-4567)',
114
+ },
115
+ } as const;
116
+
117
+ function getMessage(key: keyof typeof MESSAGES.ja, locale: string): string {
118
+ return (MESSAGES as any)[locale]?.[key] ?? MESSAGES.ja[key];
119
+ }
120
+
121
+ export function JapaneseAddressField({
122
+ form,
123
+ schemas,
124
+ i18n,
125
+ prefix = 'address',
126
+ usePrefectureId = false,
127
+ prefectureOptions,
128
+ enablePostalLookup = true,
129
+ showSearchButton = true,
130
+ autoSearch = true,
131
+ onPostalLookup,
132
+ }: JapaneseAddressFieldProps) {
133
+ const locale = getZodLocale();
134
+ const [isSearching, setIsSearching] = useState(false);
135
+
136
+ // Field names
137
+ const postalCodeField = `${prefix}_postal_code`;
138
+ const prefectureField = usePrefectureId
139
+ ? `${prefix}_prefecture_id`
140
+ : `${prefix}_prefecture`;
141
+ const address1Field = `${prefix}_address1`;
142
+ const address2Field = `${prefix}_address2`;
143
+ const address3Field = `${prefix}_address3`;
144
+
145
+ // Use generated prefecture options
146
+ const options = prefectureOptions ?? buildPrefectureOptions(locale);
147
+
148
+ // Get rules from schemas
149
+ const getRule = (field: string): RuleObject[] => {
150
+ const schema = schemas[field];
151
+ if (!schema) return [];
152
+ return [zodRule(schema as any, getLabel(i18n, field, locale))];
153
+ };
154
+
155
+ // Check if a field is required by examining its Zod schema description
156
+ const isRequired = (field: string): boolean => {
157
+ const schema = schemas[field];
158
+ if (!schema) return false;
159
+ // Check if schema has .optional() or .nullable() - these make it not required
160
+ const schemaDesc = (schema as any)?._def?.typeName;
161
+ const inner = (schema as any)?._def?.innerType;
162
+ // ZodOptional or ZodNullable means not required
163
+ if (schemaDesc === 'ZodOptional' || schemaDesc === 'ZodNullable') return false;
164
+ if (inner?._def?.typeName === 'ZodOptional' || inner?._def?.typeName === 'ZodNullable') return false;
165
+ return true;
166
+ };
167
+
168
+ // Lookup address from postal code
169
+ const doLookup = useCallback(async (postalCode: string) => {
170
+ const digits = postalCode.replace(/[^0-9]/g, '');
171
+ if (digits.length !== 7) {
172
+ message.warning(getMessage('invalidFormat', locale));
173
+ return;
174
+ }
175
+
176
+ setIsSearching(true);
177
+ try {
178
+ const lookupFn = onPostalLookup ?? lookupPostalCode;
179
+ const result = await lookupFn(postalCode);
180
+
181
+ if (result) {
182
+ const prefectureValue = usePrefectureId ? result.prefectureCode : result.prefecture;
183
+ form.setFieldsValue({
184
+ [prefectureField]: prefectureValue,
185
+ [address1Field]: result.address1,
186
+ [address2Field]: result.address2,
187
+ });
188
+ } else {
189
+ message.info(getMessage('notFound', locale));
190
+ }
191
+ } catch (error) {
192
+ message.error(getMessage('error', locale));
193
+ console.error('Postal code lookup failed:', error);
194
+ } finally {
195
+ setIsSearching(false);
196
+ }
197
+ }, [form, locale, onPostalLookup, prefectureField, address1Field, address2Field, usePrefectureId]);
198
+
199
+ const handlePostalCodeChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
200
+ const postalCode = e.target.value.replace(/[^0-9]/g, '');
201
+ if (autoSearch && enablePostalLookup && postalCode.length === 7) {
202
+ await doLookup(postalCode);
203
+ }
204
+ };
205
+
206
+ const handleSearchClick = async () => {
207
+ const postalCode = form.getFieldValue(postalCodeField);
208
+ if (postalCode) {
209
+ await doLookup(postalCode);
210
+ }
211
+ };
212
+
213
+ return (
214
+ <>
215
+ {/* 郵便番号 */}
216
+ <Form.Item
217
+ name={postalCodeField}
218
+ label={getLabel(i18n, postalCodeField, locale)}
219
+ rules={getRule(postalCodeField)}
220
+ required={isRequired(postalCodeField)}
221
+ >
222
+ <Input
223
+ placeholder={getPlaceholder(i18n, postalCodeField, locale)}
224
+ style={{ width: enablePostalLookup && showSearchButton ? 'calc(100% - 110px)' : 150 }}
225
+ onChange={handlePostalCodeChange}
226
+ addonAfter={
227
+ enablePostalLookup && showSearchButton && (
228
+ <Button
229
+ type="text"
230
+ size="small"
231
+ icon={isSearching ? <LoadingOutlined /> : <SearchOutlined />}
232
+ onClick={handleSearchClick}
233
+ disabled={isSearching}
234
+ >
235
+ {getMessage('searchAddress', locale)}
236
+ </Button>
237
+ )
238
+ }
239
+ />
240
+ </Form.Item>
241
+
242
+ {/* 都道府県 */}
243
+ <Form.Item
244
+ name={prefectureField}
245
+ label={getLabel(i18n, prefectureField, locale)}
246
+ rules={getRule(prefectureField)}
247
+ required={isRequired(prefectureField)}
248
+ >
249
+ <Select
250
+ placeholder={getPlaceholder(i18n, prefectureField, locale)}
251
+ options={options}
252
+ style={{ width: 200 }}
253
+ showSearch
254
+ optionFilterProp="label"
255
+ />
256
+ </Form.Item>
257
+
258
+ {/* 市区町村 */}
259
+ <Form.Item
260
+ name={address1Field}
261
+ label={getLabel(i18n, address1Field, locale)}
262
+ rules={getRule(address1Field)}
263
+ required={isRequired(address1Field)}
264
+ >
265
+ <Input placeholder={getPlaceholder(i18n, address1Field, locale)} />
266
+ </Form.Item>
267
+
268
+ {/* 番地 */}
269
+ <Form.Item
270
+ name={address2Field}
271
+ label={getLabel(i18n, address2Field, locale)}
272
+ rules={getRule(address2Field)}
273
+ required={isRequired(address2Field)}
274
+ >
275
+ <Input placeholder={getPlaceholder(i18n, address2Field, locale)} />
276
+ </Form.Item>
277
+
278
+ {/* 建物名・部屋番号 */}
279
+ <Form.Item
280
+ name={address3Field}
281
+ label={getLabel(i18n, address3Field, locale)}
282
+ rules={getRule(address3Field)}
283
+ required={isRequired(address3Field)}
284
+ >
285
+ <Input placeholder={getPlaceholder(i18n, address3Field, locale)} />
286
+ </Form.Item>
287
+ </>
288
+ );
289
+ }