@omnifyjp/ts 1.0.0 → 1.0.1

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.
@@ -48,7 +48,7 @@ function generateBaseModel(name, schema, reader, config) {
48
48
  const hidden = buildHidden(properties, expandedProperties, propertyOrder);
49
49
  const appends = buildAppends(expandedProperties);
50
50
  const casts = buildCasts(properties, expandedProperties, propertyOrder);
51
- const relations = buildRelations(properties, propertyOrder, modelNamespace, reader);
51
+ const relations = buildRelations(name, properties, propertyOrder, modelNamespace, reader);
52
52
  const accessors = buildAccessors(expandedProperties);
53
53
  const primaryKey = options['primaryKey'] ?? 'id';
54
54
  const rawId = options.id;
@@ -354,8 +354,9 @@ function buildCasts(properties, expandedProperties, propertyOrder) {
354
354
  return '';
355
355
  return casts.map(c => ` ${c}`).join('\n') + '\n';
356
356
  }
357
- function buildRelations(properties, propertyOrder, modelNamespace, reader) {
357
+ function buildRelations(schemaName, properties, propertyOrder, modelNamespace, reader) {
358
358
  const methods = [];
359
+ const sourceTableName = reader.getTableName(schemaName);
359
360
  for (const propName of propertyOrder) {
360
361
  const prop = properties[propName];
361
362
  if (!prop || prop['type'] !== 'Association')
@@ -363,7 +364,8 @@ function buildRelations(properties, propertyOrder, modelNamespace, reader) {
363
364
  // Resolve namespace per-target (package schemas use their own namespace)
364
365
  const target = prop['target'] ?? '';
365
366
  const ns = target ? reader.resolveModelNamespace(target, modelNamespace) : modelNamespace;
366
- const result = buildRelation(propName, prop, ns);
367
+ const targetTableName = target ? reader.getTableName(target) : '';
368
+ const result = buildRelation(propName, prop, ns, { sourceTableName, targetTableName });
367
369
  if (result) {
368
370
  methods.push('\n' + result.method);
369
371
  }
@@ -12,6 +12,10 @@ export declare function toCamelCase(value: string): string;
12
12
  * Handles common cases: -y → -ies, -s/-sh/-ch/-x/-z → -es, default → -s
13
13
  */
14
14
  export declare function pluralize(value: string): string;
15
+ /**
16
+ * Singularize a word (simple English rules — reverse of pluralize).
17
+ */
18
+ export declare function singularize(value: string): string;
15
19
  /** Convert a schema name to a table name (snake_case + pluralized). */
16
20
  export declare function toTableName(schemaName: string): string;
17
21
  /**
@@ -38,6 +38,30 @@ export function pluralize(value) {
38
38
  }
39
39
  return value + 's';
40
40
  }
41
+ /**
42
+ * Singularize a word (simple English rules — reverse of pluralize).
43
+ */
44
+ export function singularize(value) {
45
+ // Already singular or empty
46
+ if (!value || value.length <= 2)
47
+ return value;
48
+ if (value.endsWith('ies')) {
49
+ return value.slice(0, -3) + 'y';
50
+ }
51
+ if (value.endsWith('ves')) {
52
+ // knives → knife, leaves → leaf
53
+ const stem = value.slice(0, -3);
54
+ // Try -fe first (e.g., "knives" → "knife"), fallback to -f (e.g., "leaves" → "leaf")
55
+ return stem + 'f';
56
+ }
57
+ if (value.endsWith('ses') || value.endsWith('shes') || value.endsWith('ches') || value.endsWith('xes') || value.endsWith('zes')) {
58
+ return value.slice(0, -2);
59
+ }
60
+ if (value.endsWith('s') && !value.endsWith('ss')) {
61
+ return value.slice(0, -1);
62
+ }
63
+ return value;
64
+ }
41
65
  /** Convert a schema name to a table name (snake_case + pluralized). */
42
66
  export function toTableName(schemaName) {
43
67
  return pluralize(toSnakeCase(schemaName));
@@ -5,8 +5,14 @@ interface RelationResult {
5
5
  method: string;
6
6
  imports: string[];
7
7
  }
8
+ interface RelationContext {
9
+ sourceTableName: string;
10
+ targetTableName: string;
11
+ }
8
12
  /** Build Eloquent relation method PHP code. */
9
- export declare function buildRelation(propName: string, property: Record<string, unknown>, modelNamespace: string): RelationResult | null;
13
+ export declare function buildRelation(propName: string, property: Record<string, unknown>, modelNamespace: string, context?: RelationContext): RelationResult | null;
14
+ /** Generate pivot table name matching Go's GeneratePivotTableName logic. */
15
+ export declare function generatePivotTableName(sourceTable: string, targetTable: string): string;
10
16
  /** Get the return type hint for a relation. */
11
17
  export declare function getReturnType(relation: string): string;
12
18
  export {};
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Port of RelationBuilder.php — generates Eloquent relation methods.
3
3
  */
4
- import { toSnakeCase, toCamelCase } from './naming-helper.js';
4
+ import { toSnakeCase, toCamelCase, singularize } from './naming-helper.js';
5
5
  /** Build Eloquent relation method PHP code. */
6
- export function buildRelation(propName, property, modelNamespace) {
6
+ export function buildRelation(propName, property, modelNamespace, context) {
7
7
  const relation = property['relation'] ?? '';
8
8
  const target = property['target'] ?? '';
9
9
  const imports = [];
@@ -19,7 +19,7 @@ export function buildRelation(propName, property, modelNamespace) {
19
19
  method = hasMany(propName, property, target, modelNamespace);
20
20
  break;
21
21
  case 'ManyToMany':
22
- method = belongsToMany(propName, property, target, modelNamespace);
22
+ method = belongsToMany(propName, property, target, modelNamespace, context);
23
23
  break;
24
24
  case 'MorphTo':
25
25
  method = morphTo(propName);
@@ -35,6 +35,11 @@ export function buildRelation(propName, property, modelNamespace) {
35
35
  return null;
36
36
  return { method, imports };
37
37
  }
38
+ /** Generate pivot table name matching Go's GeneratePivotTableName logic. */
39
+ export function generatePivotTableName(sourceTable, targetTable) {
40
+ const tables = [singularize(sourceTable), singularize(targetTable)].sort();
41
+ return tables.join('_');
42
+ }
38
43
  /** Get the return type hint for a relation. */
39
44
  export function getReturnType(relation) {
40
45
  switch (relation) {
@@ -90,8 +95,11 @@ function hasMany(propName, property, target, ns) {
90
95
  return $this->hasMany(${fqcn}::class, '${foreignKey}');
91
96
  }`;
92
97
  }
93
- function belongsToMany(propName, property, target, ns) {
94
- const joinTable = property['joinTable'] ?? '';
98
+ function belongsToMany(propName, property, target, ns, context) {
99
+ let joinTable = property['joinTable'] ?? '';
100
+ if (!joinTable && context?.sourceTableName && context?.targetTableName) {
101
+ joinTable = generatePivotTableName(context.sourceTableName, context.targetTableName);
102
+ }
95
103
  const methodName = toCamelCase(propName);
96
104
  const pivotFields = property['pivotFields'] ?? {};
97
105
  const fqcn = `\\${ns}\\${target}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnifyjp/ts",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "TypeScript model type generator from Omnify schemas.json",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",