@omnifyjp/ts 1.2.6 → 1.3.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.
package/dist/cli.js CHANGED
@@ -53,6 +53,7 @@ function resolveFromConfig(configPath) {
53
53
  factory: laravelConfig?.factory,
54
54
  provider: laravelConfig?.provider,
55
55
  policy: laravelConfig?.policy,
56
+ nestedset: laravelConfig?.nestedset,
56
57
  } : undefined;
57
58
  return {
58
59
  input: resolve(configDir, schemasPath),
@@ -23,9 +23,12 @@ export function generateModels(reader, config) {
23
23
  return files;
24
24
  }
25
25
  function generateForSchema(name, schema, reader, config) {
26
+ const properties = (schema.properties ?? {});
27
+ const options = schema.options ?? {};
28
+ const hasNestedSet = detectNestedSet(options, properties);
26
29
  return [
27
30
  generateBaseModel(name, schema, reader, config),
28
- generateUserModel(name, config),
31
+ generateUserModel(name, config, hasNestedSet),
29
32
  ];
30
33
  }
31
34
  function generateBaseModel(name, schema, reader, config) {
@@ -40,6 +43,9 @@ function generateBaseModel(name, schema, reader, config) {
40
43
  const isAuthenticatable = options['authenticatable'] ?? false;
41
44
  const hasSoftDelete = options.softDelete ?? false;
42
45
  const hasTimestamps = options.timestamps ?? false;
46
+ // NodeTrait is added to the user-editable Model, not BaseModel
47
+ const hasNestedSet = false;
48
+ const nestedSetParentColumn = null;
43
49
  const translatableFields = reader.getTranslatableFields(name);
44
50
  const hasTranslatable = translatableFields.length > 0;
45
51
  const primaryKey = options['primaryKey'] ?? 'id';
@@ -48,17 +54,18 @@ function generateBaseModel(name, schema, reader, config) {
48
54
  const isCompositeKey = primaryKey.includes(',');
49
55
  const needsUuidTrait = !isCompositeKey && idType === 'Uuid';
50
56
  const needsUlidTrait = !isCompositeKey && idType === 'Ulid';
51
- const imports = buildImports(baseNamespace, modelName, hasSoftDelete, isAuthenticatable, hasTranslatable, properties, needsUuidTrait, needsUlidTrait);
57
+ const imports = buildImports(baseNamespace, modelName, hasSoftDelete, isAuthenticatable, hasTranslatable, properties, needsUuidTrait, needsUlidTrait, hasNestedSet, config.nestedset.namespace);
52
58
  const docProperties = buildDocProperties(properties, expandedProperties, propertyOrder);
53
59
  const baseClass = isAuthenticatable ? 'Authenticatable' : 'BaseModel';
54
60
  const implementsClause = hasTranslatable ? ' implements TranslatableContract' : '';
55
- const traits = buildTraits(hasSoftDelete, isAuthenticatable, hasTranslatable, needsUuidTrait, needsUlidTrait);
61
+ const traits = buildTraits(hasSoftDelete, isAuthenticatable, hasTranslatable, needsUuidTrait, needsUlidTrait, hasNestedSet);
56
62
  const fillable = buildFillable(properties, expandedProperties, propertyOrder);
57
63
  const hidden = buildHidden(properties, expandedProperties, propertyOrder);
58
64
  const appends = buildAppends(expandedProperties);
59
65
  const casts = buildCasts(properties, expandedProperties, propertyOrder);
60
66
  const relations = buildRelations(name, properties, propertyOrder, modelNamespace, reader);
61
67
  const accessors = buildAccessors(expandedProperties);
68
+ const nestedSetMethod = buildNestedSetParentIdMethod(nestedSetParentColumn);
62
69
  let keyTypeSection = '';
63
70
  if (idType === 'Uuid' || idType === 'Ulid' || idType === 'String') {
64
71
  keyTypeSection = `
@@ -155,16 +162,29 @@ ${appends} ];
155
162
  return [
156
163
  ${casts} ];
157
164
  }
158
- ${relations}${accessors}
165
+ ${relations}${accessors}${nestedSetMethod}
159
166
  }
160
167
  `;
161
168
  return baseFile(`${config.models.basePath}/${modelName}BaseModel.php`, content);
162
169
  }
163
- function generateUserModel(name, config) {
170
+ function generateUserModel(name, config, hasNestedSet = false) {
164
171
  const modelName = toPascalCase(name);
165
172
  const modelNamespace = config.models.namespace;
166
173
  const baseNamespace = config.models.baseNamespace;
167
174
  const factoryNamespace = config.factories.namespace;
175
+ const nestedSetNamespace = config.nestedset.namespace;
176
+ const imports = [
177
+ `use ${baseNamespace}\\${modelName}BaseModel;`,
178
+ `use ${factoryNamespace}\\${modelName}Factory;`,
179
+ `use Illuminate\\Database\\Eloquent\\Factories\\HasFactory;`,
180
+ ];
181
+ if (hasNestedSet) {
182
+ imports.push(`use ${nestedSetNamespace}\\NodeTrait;`);
183
+ }
184
+ const traits = [' use HasFactory;'];
185
+ if (hasNestedSet) {
186
+ traits.push(' use NodeTrait;');
187
+ }
168
188
  const content = `<?php
169
189
 
170
190
  /**
@@ -175,16 +195,14 @@ function generateUserModel(name, config) {
175
195
 
176
196
  namespace ${modelNamespace};
177
197
 
178
- use ${baseNamespace}\\${modelName}BaseModel;
179
- use ${factoryNamespace}\\${modelName}Factory;
180
- use Illuminate\\Database\\Eloquent\\Factories\\HasFactory;
198
+ ${imports.join('\n')}
181
199
 
182
200
  /**
183
201
  * ${modelName} — add project-specific model logic here.
184
202
  */
185
203
  class ${modelName} extends ${modelName}BaseModel
186
204
  {
187
- use HasFactory;
205
+ ${traits.join('\n')}
188
206
 
189
207
  /**
190
208
  * Create a new factory instance for the model.
@@ -199,7 +217,7 @@ class ${modelName} extends ${modelName}BaseModel
199
217
  `;
200
218
  return userFile(`${config.models.path}/${modelName}.php`, content);
201
219
  }
202
- function buildImports(baseNamespace, modelName, hasSoftDelete, isAuthenticatable, hasTranslatable, properties, needsUuidTrait = false, needsUlidTrait = false) {
220
+ function buildImports(baseNamespace, modelName, hasSoftDelete, isAuthenticatable, hasTranslatable, properties, needsUuidTrait = false, needsUlidTrait = false, hasNestedSet = false, nestedSetNamespace = 'Aimeos\\Nestedset') {
203
221
  const lines = [];
204
222
  if (isAuthenticatable) {
205
223
  lines.push('use Illuminate\\Foundation\\Auth\\User as Authenticatable;');
@@ -222,6 +240,9 @@ function buildImports(baseNamespace, modelName, hasSoftDelete, isAuthenticatable
222
240
  if (hasSoftDelete) {
223
241
  lines.push('use Illuminate\\Database\\Eloquent\\SoftDeletes;');
224
242
  }
243
+ if (hasNestedSet) {
244
+ lines.push(`use ${nestedSetNamespace}\\NodeTrait;`);
245
+ }
225
246
  if (hasTranslatable) {
226
247
  lines.push('use Astrotomic\\Translatable\\Contracts\\Translatable as TranslatableContract;');
227
248
  lines.push('use Astrotomic\\Translatable\\Translatable;');
@@ -262,7 +283,7 @@ function buildDocProperties(properties, expandedProperties, propertyOrder) {
262
283
  }
263
284
  return lines.length === 0 ? '' : lines.join('\n') + '\n';
264
285
  }
265
- function buildTraits(hasSoftDelete, isAuthenticatable, hasTranslatable, needsUuidTrait = false, needsUlidTrait = false) {
286
+ function buildTraits(hasSoftDelete, isAuthenticatable, hasTranslatable, needsUuidTrait = false, needsUlidTrait = false, hasNestedSet = false) {
266
287
  const lines = [];
267
288
  lines.push(' use HasLocalizedDisplayName;');
268
289
  if (needsUuidTrait)
@@ -271,6 +292,8 @@ function buildTraits(hasSoftDelete, isAuthenticatable, hasTranslatable, needsUui
271
292
  lines.push(' use HasUlids;');
272
293
  if (isAuthenticatable)
273
294
  lines.push(' use Notifiable;');
295
+ if (hasNestedSet)
296
+ lines.push(' use NodeTrait;');
274
297
  if (hasSoftDelete)
275
298
  lines.push(' use SoftDeletes;');
276
299
  if (hasTranslatable)
@@ -455,6 +478,50 @@ class ${modelName} extends Package${modelName}
455
478
  `;
456
479
  return userFile(`${config.models.path}/${modelName}.php`, content);
457
480
  }
481
+ /**
482
+ * Detect whether a schema uses nested set (tree structure).
483
+ * Checks both the explicit `nestedSet: true` option and the presence of
484
+ * nested set columns (_lft, _rgt, parent_id / _parent_id) in properties.
485
+ */
486
+ function detectNestedSet(options, properties) {
487
+ if (options['nestedSet'] === true)
488
+ return true;
489
+ const propNames = Object.keys(properties);
490
+ const hasLft = propNames.some(p => p === '_lft' || p === 'lft');
491
+ const hasRgt = propNames.some(p => p === '_rgt' || p === 'rgt');
492
+ const hasParent = propNames.some(p => p === 'parent_id' || p === '_parent_id' || p === 'parentId' || p === 'parent');
493
+ return hasLft && hasRgt && hasParent;
494
+ }
495
+ /**
496
+ * Detect the parent ID column name for nested set models.
497
+ * Returns the column name if it differs from the default 'parent_id', or null.
498
+ */
499
+ function detectNestedSetParentColumn(properties) {
500
+ const propNames = Object.keys(properties);
501
+ // Check for non-standard parent_id column names
502
+ if (propNames.includes('_parent_id'))
503
+ return '_parent_id';
504
+ if (propNames.includes('parent_id') || propNames.includes('parentId') || propNames.includes('parent'))
505
+ return null;
506
+ return null;
507
+ }
508
+ /**
509
+ * Build the getParentIdName() method override for nested set models
510
+ * that use a non-standard parent_id column name.
511
+ */
512
+ function buildNestedSetParentIdMethod(parentColumn) {
513
+ if (!parentColumn)
514
+ return '';
515
+ return `
516
+ /**
517
+ * Get the column name for the parent ID.
518
+ */
519
+ public function getParentIdName(): string
520
+ {
521
+ return '${parentColumn}';
522
+ }
523
+ `;
524
+ }
458
525
  function fullNameAccessor(prefix, suffix, fields, separator) {
459
526
  const methodName = 'get' + toPascalCase(prefix) + suffix + 'Attribute';
460
527
  const fieldAccess = fields.map(f => `$this->${f}`).join(', ');
@@ -17,6 +17,10 @@ export interface LaravelPathOverride {
17
17
  path?: string;
18
18
  namespace?: string;
19
19
  }
20
+ /** Nested set package configuration. */
21
+ export interface NestedSetOverride {
22
+ namespace?: string;
23
+ }
20
24
  /** Overrides from codegen.laravel YAML config (all optional). */
21
25
  export interface LaravelCodegenOverrides {
22
26
  model?: LaravelPathOverride;
@@ -25,6 +29,7 @@ export interface LaravelCodegenOverrides {
25
29
  factory?: LaravelPathOverride;
26
30
  provider?: LaravelPathOverride;
27
31
  policy?: LaravelPathOverride;
32
+ nestedset?: NestedSetOverride;
28
33
  }
29
34
  /** PHP codegen configuration (resolved with defaults). */
30
35
  export interface PhpConfig {
@@ -60,6 +65,9 @@ export interface PhpConfig {
60
65
  path: string;
61
66
  basePath: string;
62
67
  };
68
+ nestedset: {
69
+ namespace: string;
70
+ };
63
71
  }
64
72
  /**
65
73
  * Derive full PHP config from optional overrides.
package/dist/php/types.js CHANGED
@@ -40,6 +40,7 @@ export function derivePhpConfig(overrides) {
40
40
  const providerNs = overrides?.provider?.namespace ?? pathToNamespace(providerPath);
41
41
  const policyPath = overrides?.policy?.path ?? DEFAULT_POLICY_PATH;
42
42
  const policyNs = overrides?.policy?.namespace ?? pathToNamespace(policyPath);
43
+ const nestedsetNs = overrides?.nestedset?.namespace ?? 'Aimeos\\Nestedset';
43
44
  return {
44
45
  models: {
45
46
  namespace: modelNs,
@@ -73,5 +74,8 @@ export function derivePhpConfig(overrides) {
73
74
  path: policyPath,
74
75
  basePath: `${policyPath}/Base`,
75
76
  },
77
+ nestedset: {
78
+ namespace: nestedsetNs,
79
+ },
76
80
  };
77
81
  }
package/dist/types.d.ts CHANGED
@@ -87,6 +87,7 @@ export interface SchemaOptions {
87
87
  readonly timestamps?: boolean;
88
88
  readonly softDelete?: boolean;
89
89
  readonly hidden?: boolean;
90
+ readonly nestedSet?: boolean;
90
91
  readonly tableName?: string;
91
92
  readonly indexes?: readonly unknown[];
92
93
  readonly unique?: readonly unknown[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnifyjp/ts",
3
- "version": "1.2.6",
3
+ "version": "1.3.1",
4
4
  "description": "TypeScript model type generator from Omnify schemas.json",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",