From c00c2168dcf296fe14e6d5e3e04cd0b2a617aef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Magyar?= Date: Wed, 9 Feb 2022 13:21:53 +0100 Subject: [PATCH 1/9] add option to generate named exports from Typescript generator --- .../typescript/TypeScriptGenerator.ts | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/generators/typescript/TypeScriptGenerator.ts b/src/generators/typescript/TypeScriptGenerator.ts index e9ab9261fd..73fbb0d5cb 100644 --- a/src/generators/typescript/TypeScriptGenerator.ts +++ b/src/generators/typescript/TypeScriptGenerator.ts @@ -19,6 +19,7 @@ export interface TypeScriptOptions extends CommonGeneratorOptions { return dependencyModelName !== outputModel.renderedName; }); + + const {moduleSystem = "ESM", exportType = "default"} = options; + //Create the correct dependency imports - modelDependencies = modelDependencies.map((formattedDependencyModelName) => { - if (options.moduleSystem === 'CJS') { - return `const ${formattedDependencyModelName} = require('./${formattedDependencyModelName}');`; + modelDependencies = modelDependencies.map( + (dependencyName) => { + const dependencyObject = + exportType === "named" ? `{${dependencyName}}` : dependencyName; + + return moduleSystem === "CJS" + ? `const ${dependencyObject} = require('./${dependencyName}');` + : `import ${dependencyObject} from './${dependencyName}';`; } - return `import ${formattedDependencyModelName} from './${formattedDependencyModelName}';`; - }); + ); //Ensure we expose the model correctly, based on the module system - let modelCode = `${outputModel.result} -export default ${outputModel.renderedName}; -`; - if (options.moduleSystem === 'CJS') { - modelCode = `${outputModel.result} -module.exports = ${outputModel.renderedName};`; - } + const cjsExport = + exportType === 'default' + ? `module.exports = ${outputModel.renderedName};` + : `exports.${outputModel.renderedName} = ${outputModel.renderedName};`; + const esmExport = + exportType === 'default' + ? `export default ${outputModel.renderedName};` + : ''; + const modelCode = `${ + moduleSystem === 'ESM' && exportType === 'named' ? 'export ' : ' ' + }${outputModel.result}\n${moduleSystem === 'CJS' ? cjsExport : esmExport}`; const outputContent = `${[...modelDependencies, ...outputModel.dependencies].join('\n')} From 7b36a9055b2ea79c72bdf37b317805dcaf2be2e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Magyar?= Date: Fri, 25 Feb 2022 14:46:16 +0100 Subject: [PATCH 2/9] Fix existing tests to match snapshots exactly --- src/generators/typescript/TypeScriptGenerator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generators/typescript/TypeScriptGenerator.ts b/src/generators/typescript/TypeScriptGenerator.ts index 73fbb0d5cb..4dfd789fdc 100644 --- a/src/generators/typescript/TypeScriptGenerator.ts +++ b/src/generators/typescript/TypeScriptGenerator.ts @@ -81,10 +81,10 @@ export class TypeScriptGenerator extends AbstractGenerator Date: Fri, 25 Feb 2022 14:48:55 +0100 Subject: [PATCH 3/9] Add tests for named export generation --- .../typescript/TypeScriptGenerator.spec.ts | 77 ++++---- .../TypeScriptGenerator.spec.ts.snap | 180 ++++++++++++++++++ 2 files changed, 217 insertions(+), 40 deletions(-) diff --git a/test/generators/typescript/TypeScriptGenerator.spec.ts b/test/generators/typescript/TypeScriptGenerator.spec.ts index 0be7f7ae97..393c78d301 100644 --- a/test/generators/typescript/TypeScriptGenerator.spec.ts +++ b/test/generators/typescript/TypeScriptGenerator.spec.ts @@ -490,56 +490,53 @@ ${content}`; expect(arrayModel.result).toMatchSnapshot(); expect(arrayModel.dependencies).toEqual([]); }); + + const doc = { + $id: 'Address', + type: 'object', + properties: { + street_name: { type: 'string' }, + city: { type: 'string', description: 'City description' }, + state: { type: 'string' }, + house_number: { type: 'number' }, + marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, + members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, + array_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, + other_model: { type: 'object', $id: 'OtherModel', properties: { street_name: { type: 'string' } } }, + }, + patternProperties: { + '^S(.?*)test&': { + type: 'string' + } + }, + required: ['street_name', 'city', 'state', 'house_number', 'array_type'], + }; + test('should render models and their dependencies for CJS module system', async () => { - const doc = { - $id: 'Address', - type: 'object', - properties: { - street_name: { type: 'string' }, - city: { type: 'string', description: 'City description' }, - state: { type: 'string' }, - house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - array_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, - other_model: { type: 'object', $id: 'OtherModel', properties: { street_name: { type: 'string' } } }, - }, - patternProperties: { - '^S(.?*)test&': { - type: 'string' - } - }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], - }; const models = await generator.generateCompleteModels(doc, {moduleSystem: 'CJS'}); expect(models).toHaveLength(2); expect(models[0].result).toMatchSnapshot(); expect(models[1].result).toMatchSnapshot(); }); + + test('should render models and their dependencies for CJS module system with named exports', async () => { + const models = await generator.generateCompleteModels(doc, {moduleSystem: 'CJS', exportType: 'named'}); + expect(models).toHaveLength(2); + expect(models[0].result).toMatchSnapshot(); + expect(models[1].result).toMatchSnapshot(); + }); + test('should render models and their dependencies for ESM module system', async () => { - const doc = { - $id: 'Address', - type: 'object', - properties: { - street_name: { type: 'string' }, - city: { type: 'string', description: 'City description' }, - state: { type: 'string' }, - house_number: { type: 'number' }, - marriage: { type: 'boolean', description: 'Status if marriage live in given house' }, - members: { oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }, - array_type: { type: 'array', items: [{ type: 'string' }, { type: 'number' }] }, - other_model: { type: 'object', $id: 'OtherModel', properties: { street_name: { type: 'string' } } }, - }, - patternProperties: { - '^S(.?*)test&': { - type: 'string' - } - }, - required: ['street_name', 'city', 'state', 'house_number', 'array_type'], - }; const models = await generator.generateCompleteModels(doc, {moduleSystem: 'ESM'}); expect(models).toHaveLength(2); expect(models[0].result).toMatchSnapshot(); expect(models[1].result).toMatchSnapshot(); }); + + test('should render models and their dependencies for ESM module system with named exports', async () => { + const models = await generator.generateCompleteModels(doc, {moduleSystem: 'ESM', exportType: 'named'}); + expect(models).toHaveLength(2); + expect(models[0].result).toMatchSnapshot(); + expect(models[1].result).toMatchSnapshot(); + }); }); diff --git a/test/generators/typescript/__snapshots__/TypeScriptGenerator.spec.ts.snap b/test/generators/typescript/__snapshots__/TypeScriptGenerator.spec.ts.snap index 7d5cebb9dd..79eb6ee6bd 100644 --- a/test/generators/typescript/__snapshots__/TypeScriptGenerator.spec.ts.snap +++ b/test/generators/typescript/__snapshots__/TypeScriptGenerator.spec.ts.snap @@ -92,6 +92,96 @@ class OtherModel { module.exports = OtherModel;" `; +exports[`TypeScriptGenerator should render models and their dependencies for CJS module system with named exports 1`] = ` +"const {OtherModel} = require('./OtherModel'); + +class Address { + private _streetName: string; + private _city: string; + private _state: string; + private _houseNumber: number; + private _marriage?: boolean; + private _members?: string | number | boolean; + private _arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]]; + private _otherModel?: OtherModel; + private _additionalProperties?: Map | boolean | null>; + private _sTestPatternProperties?: Map; + + constructor(input: { + streetName: string, + city: string, + state: string, + houseNumber: number, + marriage?: boolean, + members?: string | number | boolean, + arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]], + otherModel?: OtherModel, + }) { + this._streetName = input.streetName; + this._city = input.city; + this._state = input.state; + this._houseNumber = input.houseNumber; + this._marriage = input.marriage; + this._members = input.members; + this._arrayType = input.arrayType; + this._otherModel = input.otherModel; + } + + get streetName(): string { return this._streetName; } + set streetName(streetName: string) { this._streetName = streetName; } + + get city(): string { return this._city; } + set city(city: string) { this._city = city; } + + get state(): string { return this._state; } + set state(state: string) { this._state = state; } + + get houseNumber(): number { return this._houseNumber; } + set houseNumber(houseNumber: number) { this._houseNumber = houseNumber; } + + get marriage(): boolean | undefined { return this._marriage; } + set marriage(marriage: boolean | undefined) { this._marriage = marriage; } + + get members(): string | number | boolean | undefined { return this._members; } + set members(members: string | number | boolean | undefined) { this._members = members; } + + get arrayType(): [string, number, ...(object | string | number | Array | boolean | null)[]] { return this._arrayType; } + set arrayType(arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]]) { this._arrayType = arrayType; } + + get otherModel(): OtherModel | undefined { return this._otherModel; } + set otherModel(otherModel: OtherModel | undefined) { this._otherModel = otherModel; } + + get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } + + get sTestPatternProperties(): Map | undefined { return this._sTestPatternProperties; } + set sTestPatternProperties(sTestPatternProperties: Map | undefined) { this._sTestPatternProperties = sTestPatternProperties; } +} +exports.Address = Address;" +`; + +exports[`TypeScriptGenerator should render models and their dependencies for CJS module system with named exports 2`] = ` +" + +class OtherModel { + private _streetName?: string; + private _additionalProperties?: Map | boolean | null>; + + constructor(input: { + streetName?: string, + }) { + this._streetName = input.streetName; + } + + get streetName(): string | undefined { return this._streetName; } + set streetName(streetName: string | undefined) { this._streetName = streetName; } + + get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } +} +exports.OtherModel = OtherModel;" +`; + exports[`TypeScriptGenerator should render models and their dependencies for ESM module system 1`] = ` "import OtherModel from './OtherModel'; @@ -183,3 +273,93 @@ class OtherModel { export default OtherModel; " `; + +exports[`TypeScriptGenerator should render models and their dependencies for ESM module system with named exports 1`] = ` +"import {OtherModel} from './OtherModel'; + +export class Address { + private _streetName: string; + private _city: string; + private _state: string; + private _houseNumber: number; + private _marriage?: boolean; + private _members?: string | number | boolean; + private _arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]]; + private _otherModel?: OtherModel; + private _additionalProperties?: Map | boolean | null>; + private _sTestPatternProperties?: Map; + + constructor(input: { + streetName: string, + city: string, + state: string, + houseNumber: number, + marriage?: boolean, + members?: string | number | boolean, + arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]], + otherModel?: OtherModel, + }) { + this._streetName = input.streetName; + this._city = input.city; + this._state = input.state; + this._houseNumber = input.houseNumber; + this._marriage = input.marriage; + this._members = input.members; + this._arrayType = input.arrayType; + this._otherModel = input.otherModel; + } + + get streetName(): string { return this._streetName; } + set streetName(streetName: string) { this._streetName = streetName; } + + get city(): string { return this._city; } + set city(city: string) { this._city = city; } + + get state(): string { return this._state; } + set state(state: string) { this._state = state; } + + get houseNumber(): number { return this._houseNumber; } + set houseNumber(houseNumber: number) { this._houseNumber = houseNumber; } + + get marriage(): boolean | undefined { return this._marriage; } + set marriage(marriage: boolean | undefined) { this._marriage = marriage; } + + get members(): string | number | boolean | undefined { return this._members; } + set members(members: string | number | boolean | undefined) { this._members = members; } + + get arrayType(): [string, number, ...(object | string | number | Array | boolean | null)[]] { return this._arrayType; } + set arrayType(arrayType: [string, number, ...(object | string | number | Array | boolean | null)[]]) { this._arrayType = arrayType; } + + get otherModel(): OtherModel | undefined { return this._otherModel; } + set otherModel(otherModel: OtherModel | undefined) { this._otherModel = otherModel; } + + get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } + + get sTestPatternProperties(): Map | undefined { return this._sTestPatternProperties; } + set sTestPatternProperties(sTestPatternProperties: Map | undefined) { this._sTestPatternProperties = sTestPatternProperties; } +} +" +`; + +exports[`TypeScriptGenerator should render models and their dependencies for ESM module system with named exports 2`] = ` +" + +export class OtherModel { + private _streetName?: string; + private _additionalProperties?: Map | boolean | null>; + + constructor(input: { + streetName?: string, + }) { + this._streetName = input.streetName; + } + + get streetName(): string | undefined { return this._streetName; } + set streetName(streetName: string | undefined) { this._streetName = streetName; } + + get additionalProperties(): Map | boolean | null> | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | boolean | null> | undefined) { this._additionalProperties = additionalProperties; } +} +" +`; From d0a8d549c347e4d8cf5c852da05988012ff1132f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Magyar?= Date: Fri, 25 Feb 2022 15:21:19 +0100 Subject: [PATCH 4/9] Add documentation for new and old `RenderCompleteModelOptions` options --- docs/generators.md | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/docs/generators.md b/docs/generators.md index aeb562d272..3bb9a66b7c 100644 --- a/docs/generators.md +++ b/docs/generators.md @@ -26,7 +26,7 @@ Options are passed as the first argument to the generator's constructor. Check t const generator = new TypeScriptGenerator({ ...options }); ``` -Default options contain: +Default generator options (common to all generators) are as follows: | Option | Type | Description | Default value | |---|---|---|---| @@ -36,10 +36,15 @@ Default options contain: | `defaultPreset` | Object | Default preset for generator. For more information, read [customization](./customization.md) document. | _Implemented by generator_ | | `presets` | Array | Array contains **presets**. For more information, read [customization](./customization.md) document. | `[]` | +In addition, generators take additional options when calling their `renderCompleteModel(input, options)` functions. +This allows the caller to specify additional options when generating a multi-file model from the input with cross dependencies. + Below is a list of additional options available for a given generator. ### [TypeScript](./languages/TypeScript.md) +#### Generator options + | Option | Type | Description | Default value | |---|---|---|---| | `renderTypes` | Boolean | Render signature for types. | `true` | @@ -49,8 +54,17 @@ Below is a list of additional options available for a given generator. | `namingConvention.type` | Function | A function that returns the format of the type. | _Returns pascal cased name, and ensures that reserved keywords are never rendered__ | | `namingConvention.property` | Function | A function that returns the format of the property. | _Returns camel cased name, and ensures that names of properties does not clash against reserved keywords for TS, as well as JS to ensure painless transpilation_ | +#### Render complete model options + +| Option | Type | Description | Default value | +|----------------|--------------------------|----------------------------------------------------------------------------|---------------| +| `moduleSystem` | 'ESM' | 'CJS' | Which module system the generated files should use (`import` or `require`) | 'CJS' | +| `exportType` | 'default' | 'named' | Whether the exports should be default or named exports | 'default' | + ### [Java](./languages/Java.md) +#### Generator options + | Option | Type | Description | Default value | |---|---|---|---| | `collectionType` | String | It indicates with which signature should be rendered the `array` type. Its value can be either `List` (`List<{type}>`) or `Array` (`{type}[]`). | `List` | @@ -58,30 +72,60 @@ Below is a list of additional options available for a given generator. | `namingConvention.type` | Function | A function that returns the format of the type. | _Returns pascal cased name, and ensures that reserved keywords are never rendered__ | | `namingConvention.property` | Function | A function that returns the format of the property. | _Returns camel cased name, and ensures that names of properties does not clash against reserved keywords_ | +#### Render complete model options + +| Option | Type | Description | Default value | +|---------------|--------|-----------------------------------------------|---------------| +| `packageName` | string | The package name to generate the models under | [required] | + ### [JavaScript](./languages/JavaScript.md) +#### Generator options + | Option | Type | Description | Default value | |---|---|---|---| | `namingConvention` | Object | Options for naming conventions. | - | | `namingConvention.type` | Function | A function that returns the format of the type. | _Returns pascal cased name, and ensures that reserved keywords are never rendered_ | | `namingConvention.property` | Function | A function that returns the format of the property. | _Returns camel cased name, and ensures that names of properties does not clash against reserved keywords_ | +#### Render complete model options + +| Option | Type | Description | Default value | +|----------------|--------------------------|----------------------------------------------------------------------------|---------------| +| `moduleSystem` | 'ESM' | 'CJS' | Which module system the generated files should use (`import` or `require`) | 'CJS' | + ### [Go](./languages/Go.md) +#### Generator options + | Option | Type | Description | Default value | |---|---|---|---| | `namingConvention` | Object | Options for naming conventions. | - | | `namingConvention.type` | Function | A function that returns the format of the type. | _Returns pascal cased name_ | | `namingConvention.field` | Function | A function that returns the format of the field. | _Returns pascal cased name_ | +#### Render complete model options + +| Option | Type | Description | Default value | +|---------------|--------|-----------------------------------------------|---------------| +| `packageName` | string | The package name to generate the models under | [required] | + ### [C#](./languages/Csharp.md) +#### Generator options + | Option | Type | Description | Default value | |---|---|---|---| | `namingConvention` | Object | Options for naming conventions. | - | | `namingConvention.type` | Function | A function that returns the format of the type. | _Returns pascal cased name, and ensures that reserved keywords are never rendered__ | | `namingConvention.property` | Function | A function that returns the format of the property. | _Returns camel cased name, and ensures that names of properties does not clash against reserved keywords_ | +#### Render complete model options + +| Option | Type | Description | Default value | +|-------------|--------|--------------------------------------------|---------------| +| `namespace` | string | The namespace to generate the models under | [required] | + ## Custom generator The minimum set of required actions to create a new generator are: From ef8b4fdaff39c818d052254de9eb4f8cd8d77627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Magyar?= Date: Thu, 3 Mar 2022 09:54:37 +0100 Subject: [PATCH 5/9] Add note to docs --- docs/languages/TypeScript.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/languages/TypeScript.md b/docs/languages/TypeScript.md index a1c0f747a2..fc7f1c1824 100644 --- a/docs/languages/TypeScript.md +++ b/docs/languages/TypeScript.md @@ -56,6 +56,8 @@ Check out this [example out for a live demonstration](../../examples/typescript- ## Rendering complete models to a specific module system In some cases you might need to render the complete models to a specific module system such as ESM and CJS. +You can choose between default exports and named exports when using either, with the `exportType` option. + Check out this [example for a live demonstration how to generate the complete TypeScript models to use ESM module system](../../examples/typescript-use-esm). Check out this [example for a live demonstration how to generate the complete TypeScript models to use CJS module system](../../examples/typescript-use-cjs). \ No newline at end of file From a81bb7fc164d137c240c5497112c35108d6d316d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Magyar?= Date: Thu, 3 Mar 2022 11:49:47 +0100 Subject: [PATCH 6/9] Fix export keyword location using a preset forced to be processed first --- .../typescript/TypeScriptGenerator.ts | 45 ++++++++++++------- .../typescript/presets/ExportKeywordPreset.ts | 40 +++++++++++++++++ src/generators/typescript/presets/index.ts | 1 + 3 files changed, 71 insertions(+), 15 deletions(-) create mode 100644 src/generators/typescript/presets/ExportKeywordPreset.ts diff --git a/src/generators/typescript/TypeScriptGenerator.ts b/src/generators/typescript/TypeScriptGenerator.ts index 4dfd789fdc..7b2d3a1db5 100644 --- a/src/generators/typescript/TypeScriptGenerator.ts +++ b/src/generators/typescript/TypeScriptGenerator.ts @@ -1,10 +1,11 @@ -import { - AbstractGenerator, +import { + AbstractGenerator, CommonGeneratorOptions, defaultGeneratorOptions } from '../AbstractGenerator'; -import { CommonModel, CommonInputModel, RenderOutput } from '../../models'; +import { CommonModel, CommonInputModel, RenderOutput, PresetWithOptions } from '../../models'; import { TypeHelpers, ModelKind, CommonNamingConvention, CommonNamingConventionImplementation } from '../../helpers'; +import { TS_EXPORT_KEYWORD_PRESET } from './presets/ExportKeywordPreset' import { TypeScriptPreset, TS_DEFAULT_PRESET } from './TypeScriptPreset'; import { ClassRenderer } from './renderers/ClassRenderer'; import { InterfaceRenderer } from './renderers/InterfaceRenderer'; @@ -40,7 +41,7 @@ export class TypeScriptGenerator extends AbstractGenerator { + async renderCompleteModel(model: CommonModel, inputModel: CommonInputModel, {moduleSystem = "ESM", exportType = "default"}: TypeScriptRenderCompleteModelOptions): Promise { + // Shallow copy presets so that we can restore it once we are done + const originalPresets = [...(this.options.presets ? this.options.presets : [])] + + // Add preset that adds the `export` keyword if needed + if ( + moduleSystem === "ESM" && + exportType === "named" && + (originalPresets[0] != TS_EXPORT_KEYWORD_PRESET || + (originalPresets[0]?.hasOwnProperty("preset") && + (originalPresets[0] as PresetWithOptions).preset != TS_EXPORT_KEYWORD_PRESET)) + ) { + this.options.presets = [TS_EXPORT_KEYWORD_PRESET, ...originalPresets]; + } + const outputModel = await this.render(model, inputModel); let modelDependencies = model.getNearestDependencies(); //Ensure model dependencies have their rendered name @@ -60,8 +75,6 @@ export class TypeScriptGenerator extends AbstractGenerator { @@ -74,7 +87,7 @@ export class TypeScriptGenerator extends AbstractGenerator { - const presets = this.getPresets('class'); + const presets = this.getPresets('class'); const renderer = new ClassRenderer(this.options, this, presets, model, inputModel); const result = await renderer.runSelfPreset(); const renderedName = renderer.nameType(model.$id, model); @@ -118,7 +133,7 @@ ${modelCode}`; } async renderInterface(model: CommonModel, inputModel: CommonInputModel): Promise { - const presets = this.getPresets('interface'); + const presets = this.getPresets('interface'); const renderer = new InterfaceRenderer(this.options, this, presets, model, inputModel); const result = await renderer.runSelfPreset(); const renderedName = renderer.nameType(model.$id, model); @@ -126,7 +141,7 @@ ${modelCode}`; } async renderEnum(model: CommonModel, inputModel: CommonInputModel): Promise { - const presets = this.getPresets('enum'); + const presets = this.getPresets('enum'); const renderer = new EnumRenderer(this.options, this, presets, model, inputModel); const result = await renderer.runSelfPreset(); const renderedName = renderer.nameType(model.$id, model); @@ -134,7 +149,7 @@ ${modelCode}`; } async renderType(model: CommonModel, inputModel: CommonInputModel): Promise { - const presets = this.getPresets('type'); + const presets = this.getPresets('type'); const renderer = new TypeRenderer(this.options, this, presets, model, inputModel); const result = await renderer.runSelfPreset(); const renderedName = renderer.nameType(model.$id, model); diff --git a/src/generators/typescript/presets/ExportKeywordPreset.ts b/src/generators/typescript/presets/ExportKeywordPreset.ts new file mode 100644 index 0000000000..815b4a485d --- /dev/null +++ b/src/generators/typescript/presets/ExportKeywordPreset.ts @@ -0,0 +1,40 @@ +import { CommonModel } from "../../../models"; +import { TypeScriptPreset } from "../TypeScriptPreset"; +import { TypeScriptRenderer } from "../TypeScriptRenderer"; + +const renderWithExportKeyword = ({ + content, +}: { + renderer: TypeScriptRenderer; + content: string; + item: CommonModel; +}): string => `export ${content}`; + + +/** + * Preset which adds export keyword wherever applicable (named exports) + * + * @implements {TypeScriptPreset} + */ +export const TS_EXPORT_KEYWORD_PRESET: TypeScriptPreset = { + class: { + self({ renderer, model, content }) { + return renderWithExportKeyword({ renderer, content, item: model }); + }, + }, + interface: { + self({ renderer, model, content }) { + return renderWithExportKeyword({ renderer, content, item: model }); + }, + }, + type: { + self({ renderer, model, content }) { + return renderWithExportKeyword({ renderer, content, item: model }); + }, + }, + enum: { + self({ renderer, model, content }) { + return renderWithExportKeyword({ renderer, content, item: model }); + }, + }, +}; diff --git a/src/generators/typescript/presets/index.ts b/src/generators/typescript/presets/index.ts index 58c93bfea2..19aefb35b4 100644 --- a/src/generators/typescript/presets/index.ts +++ b/src/generators/typescript/presets/index.ts @@ -1 +1,2 @@ export * from './CommonPreset'; +export * from './ExportKeywordPreset'; From e239afaa18fa50f8e11275b05ebd8fbf9ea5f108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Magyar?= Date: Thu, 3 Mar 2022 21:56:26 +0100 Subject: [PATCH 7/9] fix lint errors --- .../typescript/TypeScriptGenerator.ts | 22 +++++++++---------- .../typescript/presets/ExportKeywordPreset.ts | 7 +++--- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/generators/typescript/TypeScriptGenerator.ts b/src/generators/typescript/TypeScriptGenerator.ts index 7b2d3a1db5..9feacfdc6f 100644 --- a/src/generators/typescript/TypeScriptGenerator.ts +++ b/src/generators/typescript/TypeScriptGenerator.ts @@ -5,7 +5,7 @@ import { } from '../AbstractGenerator'; import { CommonModel, CommonInputModel, RenderOutput, PresetWithOptions } from '../../models'; import { TypeHelpers, ModelKind, CommonNamingConvention, CommonNamingConventionImplementation } from '../../helpers'; -import { TS_EXPORT_KEYWORD_PRESET } from './presets/ExportKeywordPreset' +import { TS_EXPORT_KEYWORD_PRESET } from './presets'; import { TypeScriptPreset, TS_DEFAULT_PRESET } from './TypeScriptPreset'; import { ClassRenderer } from './renderers/ClassRenderer'; import { InterfaceRenderer } from './renderers/InterfaceRenderer'; @@ -49,17 +49,17 @@ export class TypeScriptGenerator extends AbstractGenerator { + async renderCompleteModel(model: CommonModel, inputModel: CommonInputModel, {moduleSystem = 'ESM', exportType = 'default'}: TypeScriptRenderCompleteModelOptions): Promise { // Shallow copy presets so that we can restore it once we are done - const originalPresets = [...(this.options.presets ? this.options.presets : [])] + const originalPresets = [...(this.options.presets ? this.options.presets : [])]; // Add preset that adds the `export` keyword if needed if ( - moduleSystem === "ESM" && - exportType === "named" && - (originalPresets[0] != TS_EXPORT_KEYWORD_PRESET || - (originalPresets[0]?.hasOwnProperty("preset") && - (originalPresets[0] as PresetWithOptions).preset != TS_EXPORT_KEYWORD_PRESET)) + moduleSystem === 'ESM' && + exportType === 'named' && + (originalPresets[0] !== TS_EXPORT_KEYWORD_PRESET || + (Object.prototype.hasOwnProperty.call(originalPresets[0], 'preset') && + (originalPresets[0] as PresetWithOptions).preset !== TS_EXPORT_KEYWORD_PRESET)) ) { this.options.presets = [TS_EXPORT_KEYWORD_PRESET, ...originalPresets]; } @@ -79,9 +79,9 @@ export class TypeScriptGenerator extends AbstractGenerator { const dependencyObject = - exportType === "named" ? `{${dependencyName}}` : dependencyName; + exportType === 'named' ? `{${dependencyName}}` : dependencyName; - return moduleSystem === "CJS" + return moduleSystem === 'CJS' ? `const ${dependencyObject} = require('./${dependencyName}');` : `import ${dependencyObject} from './${dependencyName}';`; } @@ -103,7 +103,7 @@ export class TypeScriptGenerator extends AbstractGenerator `export ${content}`; - /** * Preset which adds export keyword wherever applicable (named exports) * From 3c9c336668ff09b7b63d0900b5a69086e088c9e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Magyar?= Date: Thu, 17 Mar 2022 15:13:04 +0100 Subject: [PATCH 8/9] Extract hasPreset as a helper function --- .../typescript/TypeScriptGenerator.ts | 9 ++++----- src/helpers/PresetHelpers.ts | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 src/helpers/PresetHelpers.ts diff --git a/src/generators/typescript/TypeScriptGenerator.ts b/src/generators/typescript/TypeScriptGenerator.ts index 9feacfdc6f..fdc423529b 100644 --- a/src/generators/typescript/TypeScriptGenerator.ts +++ b/src/generators/typescript/TypeScriptGenerator.ts @@ -1,9 +1,10 @@ +import { hasPreset } from "../../helpers/PresetHelpers"; import { AbstractGenerator, CommonGeneratorOptions, defaultGeneratorOptions } from '../AbstractGenerator'; -import { CommonModel, CommonInputModel, RenderOutput, PresetWithOptions } from '../../models'; +import { CommonModel, CommonInputModel, RenderOutput } from '../../models'; import { TypeHelpers, ModelKind, CommonNamingConvention, CommonNamingConventionImplementation } from '../../helpers'; import { TS_EXPORT_KEYWORD_PRESET } from './presets'; import { TypeScriptPreset, TS_DEFAULT_PRESET } from './TypeScriptPreset'; @@ -53,13 +54,11 @@ export class TypeScriptGenerator extends AbstractGenerator( + presets: Presets

, + preset: P +) => + presets.some( + (presetListItem) => + // Check regular preset equality + preset === presetListItem || + // Check PresetWithOptions equality + (Object.prototype.hasOwnProperty.call(preset, "preset") && + preset.preset === presetListItem) + ); From 628ed7d5fcc3dbce0e5d6c5931862190665e46f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Magyar?= Date: Thu, 17 Mar 2022 15:19:30 +0100 Subject: [PATCH 9/9] fix lint errors --- src/generators/typescript/TypeScriptGenerator.ts | 2 +- src/helpers/PresetHelpers.ts | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/generators/typescript/TypeScriptGenerator.ts b/src/generators/typescript/TypeScriptGenerator.ts index fdc423529b..6a74b1e819 100644 --- a/src/generators/typescript/TypeScriptGenerator.ts +++ b/src/generators/typescript/TypeScriptGenerator.ts @@ -1,4 +1,4 @@ -import { hasPreset } from "../../helpers/PresetHelpers"; +import { hasPreset } from '../../helpers/PresetHelpers'; import { AbstractGenerator, CommonGeneratorOptions, diff --git a/src/helpers/PresetHelpers.ts b/src/helpers/PresetHelpers.ts index 824b696352..6114a40eda 100644 --- a/src/helpers/PresetHelpers.ts +++ b/src/helpers/PresetHelpers.ts @@ -1,4 +1,4 @@ -import { Preset, Presets } from "../models"; +import { Preset, Presets } from '../models'; /** * Returns true if and only if a given preset is already included in a list of presets @@ -9,12 +9,12 @@ import { Preset, Presets } from "../models"; export const hasPreset =

( presets: Presets

, preset: P -) => - presets.some( - (presetListItem) => +): boolean => + presets.some( + (presetListItem) => // Check regular preset equality - preset === presetListItem || + preset === presetListItem || // Check PresetWithOptions equality - (Object.prototype.hasOwnProperty.call(preset, "preset") && + (Object.prototype.hasOwnProperty.call(preset, 'preset') && preset.preset === presetListItem) - ); + );