Skip to content

Commit c9dff41

Browse files
Shyam-Raghuwanshiwhocoder11
authored andcommitted
refactor: use separate ConstValueRenderer for const exports
1 parent 368cc0e commit c9dff41

File tree

8 files changed

+374
-37
lines changed

8 files changed

+374
-37
lines changed

src/generators/typescript/TypeScriptGenerator.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { ClassRenderer } from './renderers/ClassRenderer';
3636
import { InterfaceRenderer } from './renderers/InterfaceRenderer';
3737
import { EnumRenderer } from './renderers/EnumRenderer';
3838
import { TypeRenderer } from './renderers/TypeRenderer';
39+
import { ConstValueRenderer } from './renderers/ConstValueRenderer';
3940
import {
4041
TypeScriptDefaultConstraints,
4142
TypeScriptDefaultTypeMapping
@@ -301,6 +302,15 @@ ${modelCode}`;
301302
...options
302303
});
303304
const dependencyManagerToUse = this.getDependencyManager(optionsToUse);
305+
306+
// Render const values first (if any exist)
307+
const constValuesOutput = await this.renderConstValue(
308+
model,
309+
inputModel,
310+
optionsToUse
311+
);
312+
313+
// Render the class
304314
const presets = this.getPresets('class');
305315
const renderer = new ClassRenderer(
306316
optionsToUse,
@@ -310,14 +320,53 @@ ${modelCode}`;
310320
inputModel,
311321
dependencyManagerToUse
312322
);
313-
const result = await renderer.runSelfPreset();
323+
const classResult = await renderer.runSelfPreset();
324+
325+
// Combine const values and class
326+
const result = constValuesOutput
327+
? `${constValuesOutput.result}\n\n${classResult}`
328+
: classResult;
329+
314330
return RenderOutput.toRenderOutput({
315331
result,
316332
renderedName: model.name,
317333
dependencies: dependencyManagerToUse.dependencies
318334
});
319335
}
320336

337+
async renderConstValue(
338+
model: ConstrainedObjectModel,
339+
inputModel: InputMetaModel,
340+
options?: DeepPartial<TypeScriptOptions>
341+
): Promise<RenderOutput | null> {
342+
const optionsToUse = TypeScriptGenerator.getOptions({
343+
...this.options,
344+
...options
345+
});
346+
const dependencyManagerToUse = this.getDependencyManager(optionsToUse);
347+
const presets = this.getPresets('constValue');
348+
const renderer = new ConstValueRenderer(
349+
optionsToUse,
350+
this,
351+
presets,
352+
model,
353+
inputModel,
354+
dependencyManagerToUse
355+
);
356+
357+
// Check if there are any const properties to render
358+
if (renderer.getConstProperties().length === 0) {
359+
return null;
360+
}
361+
362+
const result = await renderer.runSelfPreset();
363+
return RenderOutput.toRenderOutput({
364+
result,
365+
renderedName: `${model.name}Constants`,
366+
dependencies: dependencyManagerToUse.dependencies
367+
});
368+
}
369+
321370
async renderInterface(
322371
model: ConstrainedObjectModel,
323372
inputModel: InputMetaModel,

src/generators/typescript/TypeScriptPreset.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
InterfacePreset,
55
EnumPreset,
66
CommonPreset,
7-
ConstrainedMetaModel
7+
ConstrainedMetaModel,
8+
ConstValuePreset
89
} from '../../models';
910
import {
1011
ClassRenderer,
@@ -16,6 +17,10 @@ import {
1617
} from './renderers/InterfaceRenderer';
1718
import { EnumRenderer, TS_DEFAULT_ENUM_PRESET } from './renderers/EnumRenderer';
1819
import { TypeRenderer, TS_DEFAULT_TYPE_PRESET } from './renderers/TypeRenderer';
20+
import {
21+
ConstValueRenderer,
22+
TS_DEFAULT_CONST_VALUE_PRESET
23+
} from './renderers/ConstValueRenderer';
1924
import { TypeScriptOptions } from './TypeScriptGenerator';
2025

2126
export type ClassPresetType<O> = ClassPreset<ClassRenderer, O>;
@@ -26,17 +31,20 @@ export type TypePresetType<O> = CommonPreset<
2631
O,
2732
ConstrainedMetaModel
2833
>;
34+
export type ConstValuePresetType<O> = ConstValuePreset<ConstValueRenderer, O>;
2935

3036
export type TypeScriptPreset<O = any> = Preset<{
3137
class: ClassPresetType<O>;
3238
interface: InterfacePresetType<O>;
3339
enum: EnumPresetType<O>;
3440
type: TypePresetType<O>;
41+
constValue: ConstValuePresetType<O>;
3542
}>;
3643

3744
export const TS_DEFAULT_PRESET: TypeScriptPreset<TypeScriptOptions> = {
3845
class: TS_DEFAULT_CLASS_PRESET,
3946
interface: TS_DEFAULT_INTERFACE_PRESET,
4047
enum: TS_DEFAULT_ENUM_PRESET,
41-
type: TS_DEFAULT_TYPE_PRESET
48+
type: TS_DEFAULT_TYPE_PRESET,
49+
constValue: TS_DEFAULT_CONST_VALUE_PRESET
4250
};

src/generators/typescript/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ export * from './TypeScriptFileGenerator';
33
export { TS_DEFAULT_PRESET } from './TypeScriptPreset';
44
export type { TypeScriptPreset } from './TypeScriptPreset';
55
export * from './presets';
6+
export {
7+
ConstValueRenderer,
8+
TS_DEFAULT_CONST_VALUE_PRESET
9+
} from './renderers/ConstValueRenderer';
610

711
export {
812
defaultEnumKeyConstraints as typeScriptDefaultEnumKeyConstraints,

src/generators/typescript/renderers/ClassRenderer.ts

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,43 +17,11 @@ export class ClassRenderer extends TypeScriptObjectRenderer {
1717
await this.runAdditionalContentPreset()
1818
];
1919

20-
const constExportsBlock = this.renderConstExports();
21-
22-
return `${constExportsBlock}class ${this.model.name} {
20+
return `class ${this.model.name} {
2321
${this.indent(this.renderBlock(content, 2))}
2422
}`;
2523
}
2624

27-
/**
28-
* Generates exported constants for properties with const values.
29-
* Converts camelCase property names to UPPER_SNAKE_CASE.
30-
* e.g., eventType with const "EXAMPLE_EVENT" -> export const EVENT_TYPE = 'EXAMPLE_EVENT';
31-
*/
32-
renderConstExports(): string {
33-
const constExports = Object.values(this.model.properties)
34-
.map((prop) => {
35-
const constValue = prop.property.options.const?.value;
36-
if (constValue === undefined) {
37-
return null;
38-
}
39-
const constName = prop.propertyName
40-
.replaceAll(/([a-z])([A-Z])/g, '$1_$2')
41-
.toUpperCase();
42-
// Use JSON.stringify for non-string values to avoid [object Object] issues
43-
const safeValue = typeof constValue === 'string'
44-
? constValue
45-
: JSON.stringify(constValue);
46-
return `export const ${constName} = ${safeValue};`;
47-
})
48-
.filter((val): val is string => val !== null);
49-
50-
if (constExports.length === 0) {
51-
return '';
52-
}
53-
54-
return constExports.join('\n') + '\n\n';
55-
}
56-
5725
runCtorPreset(): Promise<string> {
5826
return this.runPreset('ctor');
5927
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { TypeScriptRenderer } from '../TypeScriptRenderer';
2+
import {
3+
ConstrainedObjectModel,
4+
ConstrainedObjectPropertyModel
5+
} from '../../../models';
6+
import { ConstValuePresetType } from '../TypeScriptPreset';
7+
import { TypeScriptOptions } from '../TypeScriptGenerator';
8+
9+
/**
10+
* Renderer for TypeScript's exported const values
11+
*
12+
* This renderer generates exported const declarations for properties
13+
* that have const values defined in the schema.
14+
*
15+
* @extends TypeScriptRenderer
16+
*/
17+
export class ConstValueRenderer extends TypeScriptRenderer<ConstrainedObjectModel> {
18+
async defaultSelf(): Promise<string> {
19+
const content = [
20+
await this.renderItems(),
21+
await this.runAdditionalContentPreset()
22+
];
23+
24+
return this.renderBlock(content);
25+
}
26+
27+
/**
28+
* Get all properties that have const values defined
29+
*/
30+
getConstProperties(): ConstrainedObjectPropertyModel[] {
31+
return Object.values(this.model.properties).filter(
32+
(prop) => prop.property.options.const?.value !== undefined
33+
);
34+
}
35+
36+
/**
37+
* Converts a property name from camelCase to UPPER_SNAKE_CASE
38+
*/
39+
toConstName(propertyName: string): string {
40+
return propertyName.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase();
41+
}
42+
43+
async renderItems(): Promise<string> {
44+
const constProperties = this.getConstProperties();
45+
const items: string[] = [];
46+
47+
for (const property of constProperties) {
48+
const renderedItem = await this.runItemPreset(property);
49+
if (renderedItem) {
50+
items.push(renderedItem);
51+
}
52+
}
53+
54+
return this.renderBlock(items);
55+
}
56+
57+
runItemPreset(property: ConstrainedObjectPropertyModel): Promise<string> {
58+
return this.runPreset('item', { property });
59+
}
60+
}
61+
62+
export const TS_DEFAULT_CONST_VALUE_PRESET: ConstValuePresetType<TypeScriptOptions> =
63+
{
64+
self({ renderer }) {
65+
return renderer.defaultSelf();
66+
},
67+
item({ property, renderer }): string {
68+
const constValue = property.property.options.const?.value;
69+
if (constValue === undefined) {
70+
return '';
71+
}
72+
73+
const constName = renderer.toConstName(property.propertyName);
74+
// Use proper formatting based on value type
75+
const formattedValue =
76+
typeof constValue === 'string' ? constValue : JSON.stringify(constValue);
77+
78+
return `export const ${constName} = ${formattedValue};`;
79+
}
80+
};

src/models/Preset.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ export interface EnumPreset<R extends AbstractRenderer, O>
7171
item?: (args: PresetArgs<R, O, ConstrainedEnumModel> & EnumArgs) => string;
7272
}
7373

74+
export interface ConstValuePreset<R extends AbstractRenderer, O>
75+
extends CommonPreset<R, O, ConstrainedObjectModel> {
76+
item?: (
77+
args: PresetArgs<R, O, ConstrainedObjectModel> & PropertyArgs
78+
) => string;
79+
}
80+
7481
export type Preset<
7582
C extends Record<string, CommonPreset<any, any, any>> = any
7683
> = Partial<C>;

0 commit comments

Comments
 (0)