@omnifyjp/ts 0.4.2 → 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.
- package/dist/php/base-model-generator.js +2 -2
- package/dist/php/factory-generator.d.ts +1 -1
- package/dist/php/factory-generator.js +6 -3
- package/dist/php/locales-generator.d.ts +1 -1
- package/dist/php/locales-generator.js +2 -2
- package/dist/php/model-generator.d.ts +2 -1
- package/dist/php/model-generator.js +43 -5
- package/dist/php/naming-helper.d.ts +4 -0
- package/dist/php/naming-helper.js +24 -0
- package/dist/php/relation-builder.d.ts +7 -1
- package/dist/php/relation-builder.js +13 -5
- package/dist/php/request-generator.d.ts +1 -1
- package/dist/php/request-generator.js +2 -2
- package/dist/php/resource-generator.d.ts +1 -1
- package/dist/php/resource-generator.js +18 -7
- package/dist/php/schema-reader.d.ts +23 -1
- package/dist/php/schema-reader.js +82 -0
- package/dist/php/service-provider-generator.js +2 -2
- package/dist/types.d.ts +26 -0
- package/package.json +1 -1
|
@@ -7,9 +7,9 @@ import { baseFile } from './types.js';
|
|
|
7
7
|
export function generateBaseModel(reader, config) {
|
|
8
8
|
const baseNamespace = config.models.baseNamespace;
|
|
9
9
|
const modelNamespace = config.models.namespace;
|
|
10
|
-
// Build morph map entries from
|
|
10
|
+
// Build morph map entries from project-owned visible object schemas
|
|
11
11
|
const morphMap = {};
|
|
12
|
-
for (const name of Object.keys(reader.
|
|
12
|
+
for (const name of Object.keys(reader.getProjectVisibleObjectSchemas())) {
|
|
13
13
|
const modelName = toPascalCase(name);
|
|
14
14
|
morphMap[modelName] = `\\${modelNamespace}\\${modelName}::class`;
|
|
15
15
|
}
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { SchemaReader } from './schema-reader.js';
|
|
5
5
|
import type { GeneratedFile, PhpConfig } from './types.js';
|
|
6
|
-
/** Generate Factory classes for all visible object schemas. */
|
|
6
|
+
/** Generate Factory classes for all project-owned visible object schemas. */
|
|
7
7
|
export declare function generateFactories(reader: SchemaReader, config: PhpConfig): GeneratedFile[];
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
import { toPascalCase } from './naming-helper.js';
|
|
5
5
|
import { toFaker, compoundFaker, associationFaker } from './faker-mapper.js';
|
|
6
6
|
import { userFile } from './types.js';
|
|
7
|
-
/** Generate Factory classes for all visible object schemas. */
|
|
7
|
+
/** Generate Factory classes for all project-owned visible object schemas. */
|
|
8
8
|
export function generateFactories(reader, config) {
|
|
9
9
|
const files = [];
|
|
10
10
|
const schemas = reader.getSchemas();
|
|
11
|
-
for (const [name, schema] of Object.entries(reader.
|
|
11
|
+
for (const [name, schema] of Object.entries(reader.getProjectVisibleObjectSchemas())) {
|
|
12
12
|
const file = generateFactory(name, schema, reader, schemas, config);
|
|
13
13
|
if (file)
|
|
14
14
|
files.push(file);
|
|
@@ -31,7 +31,10 @@ function generateFactory(name, schema, reader, schemas, config) {
|
|
|
31
31
|
const type = prop['type'] ?? 'String';
|
|
32
32
|
// Handle associations (foreign keys)
|
|
33
33
|
if (type === 'Association') {
|
|
34
|
-
|
|
34
|
+
// Resolve namespace per-target (package schemas use their own model namespace)
|
|
35
|
+
const target = prop['target'] ?? '';
|
|
36
|
+
const targetNs = target ? reader.resolveModelNamespace(target, modelNamespace) : modelNamespace;
|
|
37
|
+
const result = associationFaker(propName, prop, targetNs, name);
|
|
35
38
|
if (result) {
|
|
36
39
|
attributes.push(result.fake);
|
|
37
40
|
if (result.import)
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { SchemaReader } from './schema-reader.js';
|
|
5
5
|
import type { GeneratedFile, PhpConfig } from './types.js';
|
|
6
|
-
/** Generate Locales classes for all object schemas. */
|
|
6
|
+
/** Generate Locales classes for all project-owned object schemas. */
|
|
7
7
|
export declare function generateLocales(reader: SchemaReader, config: PhpConfig): GeneratedFile[];
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { toPascalCase, toSnakeCase } from './naming-helper.js';
|
|
5
5
|
import { baseFile } from './types.js';
|
|
6
|
-
/** Generate Locales classes for all object schemas. */
|
|
6
|
+
/** Generate Locales classes for all project-owned object schemas. */
|
|
7
7
|
export function generateLocales(reader, config) {
|
|
8
8
|
const files = [];
|
|
9
|
-
for (const [name, schema] of Object.entries(reader.
|
|
9
|
+
for (const [name, schema] of Object.entries(reader.getProjectObjectSchemas())) {
|
|
10
10
|
const file = generateLocalesClass(name, schema, reader, config);
|
|
11
11
|
if (file)
|
|
12
12
|
files.push(file);
|
|
@@ -3,5 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { SchemaReader } from './schema-reader.js';
|
|
5
5
|
import type { GeneratedFile, PhpConfig } from './types.js';
|
|
6
|
-
/** Generate base model and user model for all object schemas
|
|
6
|
+
/** Generate base model and user model for all project-owned object schemas,
|
|
7
|
+
* plus user models for package schemas (extending the package model). */
|
|
7
8
|
export declare function generateModels(reader: SchemaReader, config: PhpConfig): GeneratedFile[];
|
|
@@ -5,12 +5,21 @@ import { toPascalCase, toSnakeCase } from './naming-helper.js';
|
|
|
5
5
|
import { toCast, toPhpDocType, isHiddenByDefault } from './type-mapper.js';
|
|
6
6
|
import { buildRelation } from './relation-builder.js';
|
|
7
7
|
import { baseFile, userFile } from './types.js';
|
|
8
|
-
/** Generate base model and user model for all object schemas
|
|
8
|
+
/** Generate base model and user model for all project-owned object schemas,
|
|
9
|
+
* plus user models for package schemas (extending the package model). */
|
|
9
10
|
export function generateModels(reader, config) {
|
|
10
11
|
const files = [];
|
|
11
|
-
|
|
12
|
+
// Project-owned schemas: base model + user model
|
|
13
|
+
for (const [name, schema] of Object.entries(reader.getProjectObjectSchemas())) {
|
|
12
14
|
files.push(...generateForSchema(name, schema, reader, config));
|
|
13
15
|
}
|
|
16
|
+
// Package schemas: user model only (extends package model)
|
|
17
|
+
for (const [name, schema] of Object.entries(reader.getPackageObjectSchemas())) {
|
|
18
|
+
const pkgNs = reader.resolveModelNamespace(name, config.models.namespace);
|
|
19
|
+
if (pkgNs !== config.models.namespace) {
|
|
20
|
+
files.push(generatePackageUserModel(name, pkgNs, config));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
14
23
|
return files;
|
|
15
24
|
}
|
|
16
25
|
function generateForSchema(name, schema, reader, config) {
|
|
@@ -39,7 +48,7 @@ function generateBaseModel(name, schema, reader, config) {
|
|
|
39
48
|
const hidden = buildHidden(properties, expandedProperties, propertyOrder);
|
|
40
49
|
const appends = buildAppends(expandedProperties);
|
|
41
50
|
const casts = buildCasts(properties, expandedProperties, propertyOrder);
|
|
42
|
-
const relations = buildRelations(properties, propertyOrder, modelNamespace);
|
|
51
|
+
const relations = buildRelations(name, properties, propertyOrder, modelNamespace, reader);
|
|
43
52
|
const accessors = buildAccessors(expandedProperties);
|
|
44
53
|
const primaryKey = options['primaryKey'] ?? 'id';
|
|
45
54
|
const rawId = options.id;
|
|
@@ -345,13 +354,18 @@ function buildCasts(properties, expandedProperties, propertyOrder) {
|
|
|
345
354
|
return '';
|
|
346
355
|
return casts.map(c => ` ${c}`).join('\n') + '\n';
|
|
347
356
|
}
|
|
348
|
-
function buildRelations(properties, propertyOrder, modelNamespace) {
|
|
357
|
+
function buildRelations(schemaName, properties, propertyOrder, modelNamespace, reader) {
|
|
349
358
|
const methods = [];
|
|
359
|
+
const sourceTableName = reader.getTableName(schemaName);
|
|
350
360
|
for (const propName of propertyOrder) {
|
|
351
361
|
const prop = properties[propName];
|
|
352
362
|
if (!prop || prop['type'] !== 'Association')
|
|
353
363
|
continue;
|
|
354
|
-
|
|
364
|
+
// Resolve namespace per-target (package schemas use their own namespace)
|
|
365
|
+
const target = prop['target'] ?? '';
|
|
366
|
+
const ns = target ? reader.resolveModelNamespace(target, modelNamespace) : modelNamespace;
|
|
367
|
+
const targetTableName = target ? reader.getTableName(target) : '';
|
|
368
|
+
const result = buildRelation(propName, prop, ns, { sourceTableName, targetTableName });
|
|
355
369
|
if (result) {
|
|
356
370
|
methods.push('\n' + result.method);
|
|
357
371
|
}
|
|
@@ -381,6 +395,30 @@ function buildAccessors(expandedProperties) {
|
|
|
381
395
|
}
|
|
382
396
|
return methods.join('\n');
|
|
383
397
|
}
|
|
398
|
+
/** Generate a user model for a package schema that extends the package's model class. */
|
|
399
|
+
function generatePackageUserModel(name, packageNamespace, config) {
|
|
400
|
+
const modelName = toPascalCase(name);
|
|
401
|
+
const modelNamespace = config.models.namespace;
|
|
402
|
+
const packageModelClass = `${packageNamespace}\\${modelName}`;
|
|
403
|
+
const content = `<?php
|
|
404
|
+
|
|
405
|
+
namespace ${modelNamespace};
|
|
406
|
+
|
|
407
|
+
use ${packageModelClass} as Package${modelName};
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* ${modelName} Model (extends package model)
|
|
411
|
+
*
|
|
412
|
+
* This file is generated once and can be customized.
|
|
413
|
+
* Add your project-specific methods and relations here.
|
|
414
|
+
*/
|
|
415
|
+
class ${modelName} extends Package${modelName}
|
|
416
|
+
{
|
|
417
|
+
// Add your custom methods here
|
|
418
|
+
}
|
|
419
|
+
`;
|
|
420
|
+
return userFile(`${config.models.path}/${modelName}.php`, content);
|
|
421
|
+
}
|
|
384
422
|
function fullNameAccessor(prefix, suffix, fields, separator) {
|
|
385
423
|
const methodName = 'get' + toPascalCase(prefix) + suffix + 'Attribute';
|
|
386
424
|
const fieldAccess = fields.map(f => `$this->${f}`).join(', ');
|
|
@@ -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
|
-
|
|
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}`;
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { SchemaReader } from './schema-reader.js';
|
|
5
5
|
import type { GeneratedFile, PhpConfig } from './types.js';
|
|
6
|
-
/** Generate Store/Update request classes for all visible object schemas. */
|
|
6
|
+
/** Generate Store/Update request classes for all project-owned visible object schemas. */
|
|
7
7
|
export declare function generateRequests(reader: SchemaReader, config: PhpConfig): GeneratedFile[];
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
import { toPascalCase, toSnakeCase, toCamelCase } from './naming-helper.js';
|
|
5
5
|
import { toStoreRules, toUpdateRules, formatRules, hasRuleObject } from './type-mapper.js';
|
|
6
6
|
import { baseFile, userFile } from './types.js';
|
|
7
|
-
/** Generate Store/Update request classes for all visible object schemas. */
|
|
7
|
+
/** Generate Store/Update request classes for all project-owned visible object schemas. */
|
|
8
8
|
export function generateRequests(reader, config) {
|
|
9
9
|
const files = [];
|
|
10
|
-
for (const [name, schema] of Object.entries(reader.
|
|
10
|
+
for (const [name, schema] of Object.entries(reader.getProjectVisibleObjectSchemas())) {
|
|
11
11
|
files.push(...generateForSchema(name, schema, reader, config));
|
|
12
12
|
}
|
|
13
13
|
return files;
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { SchemaReader } from './schema-reader.js';
|
|
5
5
|
import type { GeneratedFile, PhpConfig } from './types.js';
|
|
6
|
-
/** Generate Resource classes for all visible object schemas. */
|
|
6
|
+
/** Generate Resource classes for all project-owned visible object schemas. */
|
|
7
7
|
export declare function generateResources(reader: SchemaReader, config: PhpConfig): GeneratedFile[];
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
import { toPascalCase, toSnakeCase, toCamelCase } from './naming-helper.js';
|
|
5
5
|
import { isHiddenByDefault, toResourceExpression } from './type-mapper.js';
|
|
6
6
|
import { baseFile, userFile } from './types.js';
|
|
7
|
-
/** Generate Resource classes for all visible object schemas. */
|
|
7
|
+
/** Generate Resource classes for all project-owned visible object schemas. */
|
|
8
8
|
export function generateResources(reader, config) {
|
|
9
9
|
const files = [];
|
|
10
|
-
for (const [name, schema] of Object.entries(reader.
|
|
10
|
+
for (const [name, schema] of Object.entries(reader.getProjectVisibleObjectSchemas())) {
|
|
11
11
|
files.push(generateBaseResource(name, schema, reader, config));
|
|
12
12
|
files.push(generateUserResource(name, config));
|
|
13
13
|
}
|
|
@@ -38,7 +38,7 @@ function generateBaseResource(name, schema, reader, config) {
|
|
|
38
38
|
if (isHiddenByDefault(type) || hidden)
|
|
39
39
|
continue;
|
|
40
40
|
if (type === 'Association') {
|
|
41
|
-
addAssociationFields(propName, prop, fields, resourceNamespace, modelNamespace);
|
|
41
|
+
addAssociationFields(propName, prop, fields, resourceNamespace, modelNamespace, reader);
|
|
42
42
|
continue;
|
|
43
43
|
}
|
|
44
44
|
if (expandedProperties[propName]) {
|
|
@@ -150,26 +150,37 @@ class ${modelName}Resource extends ${modelName}ResourceBase
|
|
|
150
150
|
`;
|
|
151
151
|
return userFile(`${config.resources.path}/${modelName}Resource.php`, content);
|
|
152
152
|
}
|
|
153
|
-
function addAssociationFields(propName, prop, fields, resourceNamespace, modelNamespace) {
|
|
153
|
+
function addAssociationFields(propName, prop, fields, resourceNamespace, modelNamespace, reader) {
|
|
154
154
|
const relation = prop['relation'] ?? '';
|
|
155
155
|
const target = prop['target'] ?? '';
|
|
156
156
|
const methodName = toCamelCase(propName);
|
|
157
|
+
// Resolve resource namespace for the target (null if package without resource ns)
|
|
158
|
+
const targetResNs = target ? reader.resolveResourceNamespace(target, resourceNamespace) : resourceNamespace;
|
|
159
|
+
// If target is a package schema without a resource namespace, use simple whenLoaded
|
|
160
|
+
if (!targetResNs && relation !== 'MorphTo') {
|
|
161
|
+
if (relation === 'ManyToOne') {
|
|
162
|
+
const snakeName = toSnakeCase(propName) + '_id';
|
|
163
|
+
fields.push(` '${snakeName}' => $this->${snakeName},`);
|
|
164
|
+
}
|
|
165
|
+
fields.push(` '${methodName}' => $this->whenLoaded('${methodName}'),`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
157
168
|
const targetResource = toPascalCase(target) + 'Resource';
|
|
158
169
|
switch (relation) {
|
|
159
170
|
case 'ManyToOne': {
|
|
160
171
|
const snakeName = toSnakeCase(propName) + '_id';
|
|
161
172
|
fields.push(` '${snakeName}' => $this->${snakeName},`);
|
|
162
|
-
fields.push(` '${methodName}' => $this->whenLoaded('${methodName}', fn() => new \\${
|
|
173
|
+
fields.push(` '${methodName}' => $this->whenLoaded('${methodName}', fn() => new \\${targetResNs}\\${targetResource}($this->${methodName})),`);
|
|
163
174
|
break;
|
|
164
175
|
}
|
|
165
176
|
case 'OneToMany':
|
|
166
177
|
case 'ManyToMany':
|
|
167
178
|
case 'MorphMany':
|
|
168
|
-
fields.push(` '${methodName}' => $this->whenLoaded('${methodName}', fn() => \\${
|
|
179
|
+
fields.push(` '${methodName}' => $this->whenLoaded('${methodName}', fn() => \\${targetResNs}\\${targetResource}::collection($this->${methodName})),`);
|
|
169
180
|
break;
|
|
170
181
|
case 'OneToOne':
|
|
171
182
|
case 'MorphOne':
|
|
172
|
-
fields.push(` '${methodName}' => $this->whenLoaded('${methodName}', fn() => new \\${
|
|
183
|
+
fields.push(` '${methodName}' => $this->whenLoaded('${methodName}', fn() => new \\${targetResNs}\\${targetResource}($this->${methodName})),`);
|
|
173
184
|
break;
|
|
174
185
|
case 'MorphTo':
|
|
175
186
|
fields.push(` '${methodName}' => $this->whenLoaded('${methodName}'),`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Port of SchemaReader.php — reads schemas.json data.
|
|
3
3
|
*/
|
|
4
|
-
import type { SchemasJson, SchemaDefinition, ExpandedProperty } from '../types.js';
|
|
4
|
+
import type { SchemasJson, SchemaDefinition, ExpandedProperty, PackageExportInfo } from '../types.js';
|
|
5
5
|
export declare class SchemaReader {
|
|
6
6
|
private data;
|
|
7
7
|
constructor(data: SchemasJson);
|
|
@@ -11,6 +11,28 @@ export declare class SchemaReader {
|
|
|
11
11
|
getObjectSchemas(): Record<string, SchemaDefinition>;
|
|
12
12
|
getVisibleObjectSchemas(): Record<string, SchemaDefinition>;
|
|
13
13
|
getEnumSchemas(): Record<string, SchemaDefinition>;
|
|
14
|
+
/** Check if a schema belongs to a package. */
|
|
15
|
+
isPackageSchema(name: string): boolean;
|
|
16
|
+
/** Object schemas owned by the project (not from packages). */
|
|
17
|
+
getProjectObjectSchemas(): Record<string, SchemaDefinition>;
|
|
18
|
+
/** Object schemas from packages (not project-owned). */
|
|
19
|
+
getPackageObjectSchemas(): Record<string, SchemaDefinition>;
|
|
20
|
+
/** Visible object schemas owned by the project. */
|
|
21
|
+
getProjectVisibleObjectSchemas(): Record<string, SchemaDefinition>;
|
|
22
|
+
/** Get the top-level packages map. */
|
|
23
|
+
getPackages(): Record<string, PackageExportInfo>;
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the model namespace for a target schema.
|
|
26
|
+
* - Project schema → uses the project's model namespace
|
|
27
|
+
* - Package schema → uses the package's codegen.laravel.model.namespace (if set)
|
|
28
|
+
*/
|
|
29
|
+
resolveModelNamespace(targetName: string, projectModelNamespace: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Resolve the resource namespace for a target schema.
|
|
32
|
+
* Returns null if the target is a package schema without a resource namespace
|
|
33
|
+
* (meaning no resource class exists for it in the project).
|
|
34
|
+
*/
|
|
35
|
+
resolveResourceNamespace(targetName: string, projectResourceNamespace: string): string | null;
|
|
14
36
|
getCompoundTypes(): Record<string, unknown>;
|
|
15
37
|
getSimpleTypes(): Record<string, unknown>;
|
|
16
38
|
getCustomEnums(): Record<string, string[]>;
|
|
@@ -9,6 +9,9 @@ export class SchemaReader {
|
|
|
9
9
|
static fromData(data) {
|
|
10
10
|
return new SchemaReader(data);
|
|
11
11
|
}
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// All schemas (project + package)
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
12
15
|
getSchemas() {
|
|
13
16
|
return this.data.schemas ?? {};
|
|
14
17
|
}
|
|
@@ -43,6 +46,85 @@ export class SchemaReader {
|
|
|
43
46
|
}
|
|
44
47
|
return result;
|
|
45
48
|
}
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Project-only schemas (package === null or undefined)
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
/** Check if a schema belongs to a package. */
|
|
53
|
+
isPackageSchema(name) {
|
|
54
|
+
const schema = this.getSchema(name);
|
|
55
|
+
return schema?.package != null;
|
|
56
|
+
}
|
|
57
|
+
/** Object schemas owned by the project (not from packages). */
|
|
58
|
+
getProjectObjectSchemas() {
|
|
59
|
+
const result = {};
|
|
60
|
+
for (const [name, schema] of Object.entries(this.getObjectSchemas())) {
|
|
61
|
+
if (schema.package == null) {
|
|
62
|
+
result[name] = schema;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
/** Object schemas from packages (not project-owned). */
|
|
68
|
+
getPackageObjectSchemas() {
|
|
69
|
+
const result = {};
|
|
70
|
+
for (const [name, schema] of Object.entries(this.getObjectSchemas())) {
|
|
71
|
+
if (schema.package != null) {
|
|
72
|
+
result[name] = schema;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
/** Visible object schemas owned by the project. */
|
|
78
|
+
getProjectVisibleObjectSchemas() {
|
|
79
|
+
const result = {};
|
|
80
|
+
for (const [name, schema] of Object.entries(this.getProjectObjectSchemas())) {
|
|
81
|
+
if (!schema.options?.hidden) {
|
|
82
|
+
result[name] = schema;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Packages
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
/** Get the top-level packages map. */
|
|
91
|
+
getPackages() {
|
|
92
|
+
return this.data.packages ?? {};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Resolve the model namespace for a target schema.
|
|
96
|
+
* - Project schema → uses the project's model namespace
|
|
97
|
+
* - Package schema → uses the package's codegen.laravel.model.namespace (if set)
|
|
98
|
+
*/
|
|
99
|
+
resolveModelNamespace(targetName, projectModelNamespace) {
|
|
100
|
+
const target = this.getSchema(targetName);
|
|
101
|
+
if (target?.package) {
|
|
102
|
+
const pkg = this.getPackages()[target.package];
|
|
103
|
+
const pkgNs = pkg?.codegen?.laravel?.model?.namespace;
|
|
104
|
+
if (pkgNs)
|
|
105
|
+
return pkgNs;
|
|
106
|
+
}
|
|
107
|
+
return projectModelNamespace;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Resolve the resource namespace for a target schema.
|
|
111
|
+
* Returns null if the target is a package schema without a resource namespace
|
|
112
|
+
* (meaning no resource class exists for it in the project).
|
|
113
|
+
*/
|
|
114
|
+
resolveResourceNamespace(targetName, projectResourceNamespace) {
|
|
115
|
+
const target = this.getSchema(targetName);
|
|
116
|
+
if (target?.package) {
|
|
117
|
+
const pkg = this.getPackages()[target.package];
|
|
118
|
+
const pkgNs = pkg?.codegen?.laravel?.resource?.namespace;
|
|
119
|
+
if (pkgNs)
|
|
120
|
+
return pkgNs;
|
|
121
|
+
return null; // no resource namespace → no resource class
|
|
122
|
+
}
|
|
123
|
+
return projectResourceNamespace;
|
|
124
|
+
}
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// Property helpers
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
46
128
|
getCompoundTypes() {
|
|
47
129
|
return this.data.customTypes?.compound ?? {};
|
|
48
130
|
}
|
|
@@ -6,9 +6,9 @@ import { baseFile } from './types.js';
|
|
|
6
6
|
/** Generate the OmnifyServiceProvider for the application. */
|
|
7
7
|
export function generateServiceProvider(reader, config) {
|
|
8
8
|
const modelNamespace = config.models.namespace;
|
|
9
|
-
// Build morph map entries
|
|
9
|
+
// Build morph map entries (project-owned schemas only)
|
|
10
10
|
const morphEntries = [];
|
|
11
|
-
for (const name of Object.keys(reader.
|
|
11
|
+
for (const name of Object.keys(reader.getProjectVisibleObjectSchemas())) {
|
|
12
12
|
const modelName = toPascalCase(name);
|
|
13
13
|
morphEntries.push(` '${modelName}' => \\${modelNamespace}\\${modelName}::class,`);
|
|
14
14
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -22,8 +22,33 @@ export interface SchemasJson {
|
|
|
22
22
|
readonly simple: Record<string, SimpleTypeDefinition>;
|
|
23
23
|
readonly enums: Record<string, string[]>;
|
|
24
24
|
};
|
|
25
|
+
readonly packages?: Record<string, PackageExportInfo>;
|
|
25
26
|
readonly schemas: Record<string, SchemaDefinition>;
|
|
26
27
|
}
|
|
28
|
+
/** Package metadata in schemas.json (codegen namespace references). */
|
|
29
|
+
export interface PackageExportInfo {
|
|
30
|
+
readonly codegen?: PackageCodegenExport;
|
|
31
|
+
}
|
|
32
|
+
/** Codegen namespace info from a package. */
|
|
33
|
+
export interface PackageCodegenExport {
|
|
34
|
+
readonly laravel?: {
|
|
35
|
+
readonly model?: {
|
|
36
|
+
readonly namespace?: string;
|
|
37
|
+
};
|
|
38
|
+
readonly request?: {
|
|
39
|
+
readonly namespace?: string;
|
|
40
|
+
};
|
|
41
|
+
readonly resource?: {
|
|
42
|
+
readonly namespace?: string;
|
|
43
|
+
};
|
|
44
|
+
readonly factory?: {
|
|
45
|
+
readonly namespace?: string;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
readonly typescript?: {
|
|
49
|
+
readonly modelsPath?: string;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
27
52
|
/** Compound type definition (e.g., JapaneseName). */
|
|
28
53
|
export interface CompoundTypeDefinition {
|
|
29
54
|
readonly fields: readonly CompoundField[];
|
|
@@ -68,6 +93,7 @@ export interface SchemaOptions {
|
|
|
68
93
|
/** Schema definition from schemas.json. */
|
|
69
94
|
export interface SchemaDefinition {
|
|
70
95
|
readonly name: string;
|
|
96
|
+
readonly package?: string | null;
|
|
71
97
|
readonly tableName?: string;
|
|
72
98
|
readonly connection?: string;
|
|
73
99
|
readonly displayName?: LocalizedString;
|