Skip to content

Commit af7e7af

Browse files
feat(core): added factoryMethods - code review
1 parent 95119c5 commit af7e7af

16 files changed

Lines changed: 84 additions & 142 deletions

File tree

docs/content/docs/reference/configuration/output.mdx

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1678,31 +1678,23 @@ Generate factory methods for DTOs (Data Transfer Objects) initialized with safe
16781678
Functionality handles OpenAPI `readOnly` and `writeOnly` flags to generate appropriate payload structures:
16791679
- **Required properties:** Always included in the factory output, regardless of their visibility flags.
16801680
- **Optional `readOnly` properties:** Always omitted from the factory output, as they would be dropped by the server.
1681-
- **Optional `writeOnly` properties:** Always included in the factory output (even if `optionalPropertyStrategy` is set to `'omit'`).
1681+
- **Optional `writeOnly` properties:** Always included in the factory output (even if `includeOptionalProperty` is set to `false`).
16821682

16831683
```ts title="orval.config.ts"
16841684
export default defineConfig({
16851685
petstore: {
16861686
output: {
16871687
factoryMethods: {
1688-
generate: true,
16891688
functionNamePrefix: 'create',
1690-
mode: 'separate-file',
1691-
optionalPropertyStrategy: 'include',
1689+
mode: 'split',
1690+
includeOptionalProperty: true,
16921691
outputDirectory: `#output.workspace.schemas`,
16931692
},
16941693
},
16951694
},
16961695
});
16971696
```
16981697

1699-
### generate
1700-
1701-
**Type:** `Boolean`
1702-
**Default:** `false`
1703-
1704-
Enables or disables factory generation.
1705-
17061698
### functionNamePrefix
17071699

17081700
**Type:** `String`
@@ -1712,18 +1704,18 @@ Prefix for the generated factory function names.
17121704

17131705
### mode
17141706

1715-
**Type:** `'inline-with-schema' | 'separate-file' | 'combined-separate-file'`
1716-
**Default:** `'separate-file'`
1707+
**Type:** `'single' | 'split' | 'single-split'`
1708+
**Default:** `'split'`
17171709

17181710
Where to generate the factory methods:
1719-
- `inline-with-schema`: Appends the factory function to the schema file.
1720-
- `separate-file`: Creates a `{schema}.factory.ts` with factory method. By default it is placed next to schema file.
1721-
- `combined-separate-file`: Aggregates all factory methods into a single `factoryMethods.ts` file.
1711+
- `single`: Appends the factory function to the schema file.
1712+
- `split`: Creates a `{schema}.factory.ts` with factory method. By default it is placed next to schema file.
1713+
- `single-split`: Aggregates all factory methods into a single `factoryMethods.ts` file.
17221714

1723-
### optionalPropertyStrategy
1715+
### includeOptionalProperty
17241716

1725-
**Type:** `'include' | 'omit'`
1726-
**Default:** `'include'`
1717+
**Type:** `boolean`
1718+
**Default:** `true`
17271719

17281720
Determines whether optional schema properties are included in the default factory output.
17291721

@@ -1734,7 +1726,7 @@ Determines whether optional schema properties are included in the default factor
17341726

17351727
Defaults to the value configured in `#output.workspace.schemas`.
17361728
Determines where factory methods will be generated (can be used to generated methods away from schema directory).
1737-
Takes effect only when used `mode` is `separate-file` or `combined-separate-file`.
1729+
Takes effect only when used `mode` is `split` or `single-split`.
17381730
---
17391731

17401732
## Other Options

packages/angular/src/http-client.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,10 @@ const createOutput = (
126126
unionAddMissingProperties: false,
127127
propertySortOrder: 'Specification',
128128
factoryMethods: {
129-
generate: false,
130129
functionNamePrefix: 'create',
131-
mode: 'inline-with-schema',
130+
mode: 'single',
132131
outputDirectory: '',
133-
optionalPropertyStrategy: 'omit',
132+
includeOptionalProperty: false,
134133
},
135134
...overrides,
136135
} satisfies NormalizedOutputOptions;

packages/angular/src/http-resource.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,10 @@ const createOutput = (
136136
unionAddMissingProperties: false,
137137
propertySortOrder: 'Specification',
138138
factoryMethods: {
139-
generate: false,
140139
functionNamePrefix: 'create',
141-
mode: 'inline-with-schema',
140+
mode: 'single',
142141
outputDirectory: '',
143-
optionalPropertyStrategy: 'omit',
142+
includeOptionalProperty: false,
144143
},
145144
...overrides,
146145
} satisfies NormalizedOutputOptions;

packages/core/src/generators/factory.test.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@ import { NamingConvention } from '../types';
99
import { generateFactory } from './factory';
1010

1111
const baseFactoryMethods = {
12-
generate: true,
1312
functionNamePrefix: 'create',
14-
mode: 'inline-with-schema',
13+
mode: 'single',
1514
outputDirectory: '',
16-
optionalPropertyStrategy: 'omit',
15+
includeOptionalProperty: false,
1716
};
1817

1918
const baseOverride = {
@@ -141,7 +140,7 @@ describe('generateFactory', () => {
141140
createMockContext({
142141
factoryMethods: {
143142
...baseFactoryMethods,
144-
optionalPropertyStrategy: 'include',
143+
includeOptionalProperty: true,
145144
},
146145
}),
147146
);
@@ -205,7 +204,7 @@ describe('generateFactory', () => {
205204
expect(result?.imports).toContainEqual({ name: 'RefTarget' });
206205
});
207206

208-
it('handles mode: separate-file import paths', () => {
207+
it('handles mode: split import paths', () => {
209208
const schema: OpenApiSchemaObject = {
210209
type: 'object',
211210
required: ['target'],
@@ -218,7 +217,7 @@ describe('generateFactory', () => {
218217
schema,
219218
'WithRef',
220219
createMockContext({
221-
factoryMethods: { ...baseFactoryMethods, mode: 'separate-file' },
220+
factoryMethods: { ...baseFactoryMethods, mode: 'split' },
222221
}),
223222
);
224223
expect(result?.imports).toContainEqual({
@@ -228,7 +227,7 @@ describe('generateFactory', () => {
228227
});
229228
});
230229

231-
it('handles mode: combined-separate-file import paths', () => {
230+
it('handles mode: single-split import paths', () => {
232231
const schema: OpenApiSchemaObject = {
233232
type: 'object',
234233
required: ['target'],
@@ -243,7 +242,7 @@ describe('generateFactory', () => {
243242
createMockContext({
244243
factoryMethods: {
245244
...baseFactoryMethods,
246-
mode: 'combined-separate-file',
245+
mode: 'single-split',
247246
},
248247
}),
249248
);

packages/core/src/generators/factory.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ function getSchemaImportPath(
3131
refName: string,
3232
context: ContextSpec,
3333
): string | undefined {
34-
if (context.output.factoryMethods.mode === 'inline-with-schema') {
34+
if (context.output.factoryMethods?.mode === 'single') {
3535
return undefined;
3636
}
37-
let outputDir = context.output.factoryMethods.outputDirectory;
37+
let outputDir = context.output.factoryMethods?.outputDirectory;
3838
let schemasPath = getSchemasPath(context);
3939

4040
if (context.output.workspace) {
@@ -128,14 +128,15 @@ export function generateFactory(
128128
name: string,
129129
context: ContextSpec,
130130
): { model: string; imports: GeneratorImport[] } | undefined {
131-
if (!canGenerateSchema(schema)) return undefined;
131+
if (!canGenerateSchema(schema) || !context.output.factoryMethods)
132+
return undefined;
132133

133134
const { functionNamePrefix, mode } = context.output.factoryMethods;
134135
const factoryName = `${functionNamePrefix}${pascal(name)}`;
135136
const imports: GeneratorImport[] = [];
136137
const payload = buildPayload(schema, context, [name], imports);
137138

138-
if (mode !== 'inline-with-schema') {
139+
if (mode !== 'single') {
139140
const schemaImportPath = getSchemaImportPath(name, context);
140141
imports.push({ name, importPath: schemaImportPath });
141142
}
@@ -287,10 +288,10 @@ function buildRefPayload(
287288
return `{} as ${refName}`;
288289
}
289290

290-
const { functionNamePrefix, mode } = context.output.factoryMethods;
291+
const { functionNamePrefix, mode } = context.output.factoryMethods!;
291292
const refFactoryName = `${functionNamePrefix}${pascal(refName)}`;
292293

293-
if (mode !== 'combined-separate-file') {
294+
if (mode !== 'single-split') {
294295
const importPath = resolveImportPath(mode, refName, context);
295296
imports.push({ name: refFactoryName, importPath, isConstant: true });
296297
}
@@ -310,13 +311,13 @@ function resolveImportPath(
310311
): string | undefined {
311312
const baseName = conventionName(refName, context.output.namingConvention);
312313
switch (mode) {
313-
case 'separate-file': {
314+
case 'split': {
314315
return `./${baseName}.factory`;
315316
}
316-
case 'combined-separate-file': {
317+
case 'single-split': {
317318
return `./${conventionName('factoryMethods', context.output.namingConvention)}`;
318319
}
319-
case 'inline-with-schema': {
320+
case 'single': {
320321
return `./${baseName}`;
321322
}
322323
}
@@ -350,7 +351,7 @@ function buildObjectPayload(
350351
parents: string[],
351352
imports: GeneratorImport[],
352353
): string {
353-
const { optionalPropertyStrategy } = context.output.factoryMethods;
354+
const { includeOptionalProperty } = context.output.factoryMethods!;
354355
const props = getProperties(schema);
355356
const requiredProps: string[] =
356357
(schema.required as string[] | undefined) ?? [];
@@ -360,7 +361,7 @@ function buildObjectPayload(
360361
entries.sort(([a], [b]) => a.localeCompare(b));
361362
}
362363

363-
const includeOptional = optionalPropertyStrategy === 'include';
364+
const includeOptional = includeOptionalProperty;
364365
const lines: string[] = [];
365366

366367
for (const [key, prop] of entries) {

packages/core/src/generators/schema-definition.test.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,7 @@ describe('generateSchemasDefinition', () => {
1111
const context = {
1212
output: {
1313
override: { namingConvention: {} },
14-
factoryMethods: {
15-
generate: false,
16-
functionNamePrefix: 'create',
17-
mode: 'separate-file',
18-
optionalPropertyStrategy: 'include',
19-
},
14+
factoryMethods: undefined,
2015
},
2116
target: 'typescript',
2217
workspace: '',
@@ -145,12 +140,7 @@ describe('generateSchemasDefinition', () => {
145140
enum: 'PascalCase',
146141
},
147142
},
148-
factoryMethods: {
149-
generate: false,
150-
functionNamePrefix: 'create',
151-
mode: 'separate-file',
152-
optionalPropertyStrategy: 'include',
153-
},
143+
factoryMethods: undefined,
154144
},
155145
target: 'typescript',
156146
workspace: '',

packages/core/src/generators/schema-definition.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,14 @@ export function generateSchemasDefinition(
7171
if (!seenNames.has(normalizedName)) {
7272
seenNames.add(normalizedName);
7373

74-
if (context.output.factoryMethods.generate && schema.schema) {
74+
if (context.output.factoryMethods && schema.schema) {
7575
const factoryData = generateFactory(
7676
schema.schema,
7777
schema.name,
7878
context,
7979
);
8080
if (factoryData) {
81-
if (context.output.factoryMethods.mode === 'inline-with-schema') {
81+
if (context.output.factoryMethods.mode === 'single') {
8282
schema.model += `\n${factoryData.model}`;
8383
for (const imp of factoryData.imports) {
8484
if (

packages/core/src/test-utils/context.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,7 @@ export function createTestContextSpec({
4141
unionAddMissingProperties: false,
4242
optionsParamRequired: false,
4343
propertySortOrder: PropertySortOrder.SPECIFICATION,
44-
factoryMethods: {
45-
generate: false,
46-
functionNamePrefix: 'create',
47-
mode: 'separate-file',
48-
outputDirectory: '',
49-
optionalPropertyStrategy: 'include',
50-
},
44+
factoryMethods: undefined,
5145
override: {
5246
title: undefined,
5347
transformer: undefined,

packages/core/src/types.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export interface NormalizedOutputOptions {
5858
unionAddMissingProperties: boolean;
5959
optionsParamRequired: boolean;
6060
propertySortOrder: PropertySortOrder;
61-
factoryMethods: NormalizedFactoryMethodsOptions;
61+
factoryMethods?: NormalizedFactoryMethodsOptions;
6262
}
6363

6464
export interface NormalizedParamsSerializerOptions {
@@ -276,26 +276,20 @@ export type EnumGeneration =
276276

277277
export type SchemaGenerationType = 'typescript' | 'zod';
278278

279-
export type FactoryMethodsOptionalPropertyStrategy = 'include' | 'omit';
280-
export type FactoryMethodsMode =
281-
| 'inline-with-schema'
282-
| 'separate-file'
283-
| 'combined-separate-file';
279+
export type FactoryMethodsMode = 'single' | 'split' | 'single-split';
284280

285281
export interface FactoryMethodsOptions {
286-
generate?: boolean;
287282
functionNamePrefix?: string;
288283
mode?: FactoryMethodsMode;
289284
outputDirectory?: string;
290-
optionalPropertyStrategy?: FactoryMethodsOptionalPropertyStrategy;
285+
includeOptionalProperty?: boolean;
291286
}
292287

293288
export interface NormalizedFactoryMethodsOptions {
294-
generate: boolean;
295289
functionNamePrefix: string;
296290
mode: FactoryMethodsMode;
297291
outputDirectory: string;
298-
optionalPropertyStrategy: FactoryMethodsOptionalPropertyStrategy;
292+
includeOptionalProperty: boolean;
299293
}
300294

301295
export interface SchemaOptions {

packages/core/src/writers/schemas.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ async function emitFactoryForSchema(
426426
) {
427427
if (schema.factory && schema.factoryMode) {
428428
const mode = schema.factoryMode;
429-
if (mode === 'separate-file') {
429+
if (mode === 'split') {
430430
const baseName = conventionName(schema.name, namingConvention);
431431
const factoryName = `${baseName}.factory`;
432432
helpers.separateFactoryNames.push(factoryName);
@@ -439,7 +439,7 @@ async function emitFactoryForSchema(
439439
getPath(factoryDir, factoryName, fileExtension),
440440
factoryFile,
441441
);
442-
} else if (mode === 'combined-separate-file') {
442+
} else if (mode === 'single-split') {
443443
helpers.isCombined.value = true;
444444
helpers.combinedFactoryContent.value += `${schema.factory}\n`;
445445
helpers.combinedFactoryImports.push(...(schema.factoryImports ?? []));

0 commit comments

Comments
 (0)