Skip to content

Commit

Permalink
fix: ensure reserved keywords can never be rendered for TS output (#311)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonaslagoni authored Jul 28, 2021
1 parent 14267b4 commit e9ca783
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 6 deletions.
6 changes: 3 additions & 3 deletions docs/generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,16 @@ Below is a list of additional options available for a given generator.
| `renderTypes` | Boolean | Render signature for types. | `true` |
| `modelType` | String | It indicates which model type should be rendered for the `object` type. Its value can be either `interface` or `class`. | `class` |
| `namingConvention` | Object | Options for naming conventions. | - |
| `namingConvention.type` | Function | A function that returns the format of the type. | _Returns pascal cased name_ |
| `namingConvention.property` | Function | A function that returns the format of the property. | _Returns camel cased name_ |
| `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_ |

### [Java](../src/generators/java/JavaGenerator.ts)

| 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` |
| `namingConvention` | Object | Options for naming conventions. | - |
| `namingConvention.type` | Function | A function that returns the format of the type. | _Returns pascal cased name_ |
| `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_ |

### [JavaScript](../src/generators/javascript/JavaScriptGenerator.ts)
Expand Down
9 changes: 6 additions & 3 deletions src/generators/typescript/TypeScriptRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ export const ReservedTypeScriptKeywords = [
'static',
'yield'
];

/**
* Common renderer for TypeScript types
*
Expand All @@ -85,6 +84,10 @@ export abstract class TypeScriptRenderer extends AbstractRenderer<TypeScriptOpti
super(options, generator, presets, model, inputModel);
}

static isReservedTypeScriptKeyword(word: string): boolean {
return ReservedTypeScriptKeywords.includes(word);
}

/**
* Renders the name of a type based on provided generator option naming convention type function.
*
Expand All @@ -95,7 +98,7 @@ export abstract class TypeScriptRenderer extends AbstractRenderer<TypeScriptOpti
*/
nameType(name: string | undefined, model?: CommonModel): string {
return this.options?.namingConvention?.type
? this.options.namingConvention.type(name, { model: model || this.model, inputModel: this.inputModel })
? this.options.namingConvention.type(name, { model: model || this.model, inputModel: this.inputModel, isReservedKeyword: TypeScriptRenderer.isReservedTypeScriptKeyword(`${name}`)})
: name || '';
}

Expand All @@ -107,7 +110,7 @@ export abstract class TypeScriptRenderer extends AbstractRenderer<TypeScriptOpti
*/
nameProperty(propertyName: string | undefined, property?: CommonModel): string {
return this.options?.namingConvention?.property
? this.options.namingConvention.property(propertyName, { model: this.model, inputModel: this.inputModel, property })
? this.options.namingConvention.property(propertyName, { model: this.model, inputModel: this.inputModel, property, isReservedKeyword: TypeScriptRenderer.isReservedTypeScriptKeyword(`${propertyName}`) })
: propertyName || '';
}

Expand Down
8 changes: 8 additions & 0 deletions test/generators/java/JavaRenderer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,21 @@ describe('JavaRenderer', () => {
const name = renderer.nameType('type__someType');
expect(name).toEqual('TypeSomeType');
});
test('should render reserved type keyword correctly', () => {
const name = renderer.nameType('enum');
expect(name).toEqual('ReservedEnum');
});
});

describe('nameProperty()', () => {
test('should name the property', () => {
const name = renderer.nameProperty('property__someProperty');
expect(name).toEqual('propertySomeProperty');
});
test('should render reserved property keyword correctly', () => {
const name = renderer.nameProperty('enum');
expect(name).toEqual('reservedEnum');
});
});
describe('renderType()', () => {
test('Should render refs with pascal case', () => {
Expand Down
40 changes: 40 additions & 0 deletions test/generators/typescript/TypeScriptGenerator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,46 @@ describe('TypeScriptGenerator', () => {
beforeEach(() => {
generator = new TypeScriptGenerator();
});

test('should not render `class` with reserved keyword', async () => {
const doc = {
$id: 'Address',
type: 'object',
properties: {
enum: { type: 'string' },
reservedEnum: { type: 'string' }
},
additionalProperties: false
};
const expected = `export class Address {
private _reservedReservedEnum?: string;
private _reservedEnum?: string;
constructor(input: {
reservedReservedEnum?: string,
reservedEnum?: string,
}) {
this._reservedReservedEnum = input.reservedReservedEnum;
this._reservedEnum = input.reservedEnum;
}
get reservedReservedEnum(): string | undefined { return this._reservedReservedEnum; }
set reservedReservedEnum(reservedReservedEnum: string | undefined) { this._reservedReservedEnum = reservedReservedEnum; }
get reservedEnum(): string | undefined { return this._reservedEnum; }
set reservedEnum(reservedEnum: string | undefined) { this._reservedEnum = reservedEnum; }
}`;

const inputModel = await generator.process(doc);
const model = inputModel.models['Address'];

let classModel = await generator.renderClass(model, inputModel);
expect(classModel.result).toEqual(expected);

classModel = await generator.render(model, inputModel);
expect(classModel.result).toEqual(expected);
});

test('should render `class` type', async () => {
const doc = {
$id: '_address',
Expand Down
8 changes: 8 additions & 0 deletions test/generators/typescript/TypeScriptRenderer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,21 @@ describe('TypeScriptRenderer', () => {
const name = renderer.nameType('type__someType');
expect(name).toEqual('TypeSomeType');
});
test('should render reserved type keyword correctly', () => {
const name = renderer.nameType('enum');
expect(name).toEqual('ReservedEnum');
});
});

describe('nameProperty()', () => {
test('should name the property', () => {
const name = renderer.nameProperty('property__someProperty');
expect(name).toEqual('propertySomeProperty');
});
test('should render reserved property keyword correctly', () => {
const name = renderer.nameProperty('enum');
expect(name).toEqual('reservedEnum');
});
});

describe('renderComments()', () => {
Expand Down

0 comments on commit e9ca783

Please sign in to comment.