{"id":19337309,"url":"https://github.com/infobip/infobip-typescript-generator-extension","last_synced_at":"2025-04-23T01:30:58.561Z","repository":{"id":42439733,"uuid":"364538805","full_name":"infobip/infobip-typescript-generator-extension","owner":"infobip","description":"Library which provides new features on top of TypeScript Generator.","archived":false,"fork":false,"pushed_at":"2025-01-29T06:26:16.000Z","size":498,"stargazers_count":5,"open_issues_count":1,"forks_count":4,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-04-02T06:12:20.690Z","etag":null,"topics":["bean-validation","class-transformer","class-validator","java","typescript","typescript-generator"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/infobip.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-05-05T10:25:22.000Z","updated_at":"2025-01-29T06:26:20.000Z","dependencies_parsed_at":"2024-11-10T03:14:09.009Z","dependency_job_id":"cba25205-0b50-4fff-a41c-15da9eb8ea36","html_url":"https://github.com/infobip/infobip-typescript-generator-extension","commit_stats":null,"previous_names":[],"tags_count":65,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infobip%2Finfobip-typescript-generator-extension","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infobip%2Finfobip-typescript-generator-extension/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infobip%2Finfobip-typescript-generator-extension/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infobip%2Finfobip-typescript-generator-extension/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/infobip","download_url":"https://codeload.github.com/infobip/infobip-typescript-generator-extension/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250352206,"owners_count":21416454,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bean-validation","class-transformer","class-validator","java","typescript","typescript-generator"],"created_at":"2024-11-10T03:14:01.322Z","updated_at":"2025-04-23T01:30:58.177Z","avatar_url":"https://github.com/infobip.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Infobip TypeScript Generator Extension\n\n[![](https://github.com/infobip/infobip-typescript-generator-extension/workflows/maven/badge.svg)](https://github.com/infobip/infobip-typescript-generator-extension/actions?query=workflow%3Amaven)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.infobip/infobip-typescript-generator-extension/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.infobip/infobip-typescript-generator-extension)\n[![Coverage Status](https://coveralls.io/repos/github/infobip/infobip-typescript-generator-extension/badge.svg?branch=main)](https://coveralls.io/github/infobip/infobip-typescript-generator-extension?branch=main)\n[![Known Vulnerabilities](https://snyk.io/test/github/infobip/infobip-typescript-generator-extension/badge.svg)](https://snyk.io/test/github/infobip/infobip-typescript-generator-extension)\n\nLibrary which provides new features on top of [TypeScript Generator](https://github.com/vojtechhabarta/typescript-generator):\nannotation processor support (which eliminates the requirement for a maven plugin) and bean validation Java annotations to TypeScript decorators translation.\n\n## Contents\n\n1. [Bean Validation to class-validator translation](#BeanValidationToClassValidatorTranslation)\n    * [Simple Object](#SimpleObject)\n    * [Hierarchy](#Hierarchy)\n2. [Custom Validation to class-validator translation](#CustomValidationToClassValidatorTranslation)\n    * [@CustomTypeScriptDecorator](#CustomTypeScriptDecoratorAnnotation)\n    * [Limitations](#CustomValidationLimitations)\n    * [Example](#CustomValidationExample)\n3. [Localization.ts \u0026 CommonValidationMessages.ts](#Localization\u0026CommonValidationMessages)\n4. [Time type mappings](#TimeTypeMappings)\n5. [Annotation processor](#AnnotationProcessor)\n6. [Contributing](#Contributing)\n7. [License](#License)\n\n\u003ca id=\"BeanValidationToClassValidatorTranslation\"\u003e\u003c/a\u003e\n##  Bean Validation to class-validator translation\n\n\u003ca id=\"SimpleObject\"\u003e\u003c/a\u003e\n### Simple Object\n\nInput:\n\n```java\n\npublic record Foo(\n\n        @Size(min = 1, max = 2)\n        @NotEmpty\n        @NotNull\n        @Valid\n        String bar\n\n) {\n\n}\n```\n\nResult:\n\n```typescript\n/* tslint:disable */\n/* eslint-disable */\nimport { IsDefined, IsNotEmpty, MinLength, ValidateNested, MaxLength } from 'class-validator';\nimport { CommonValidationMessages } from './CommonValidationMessages';\n\nexport class Foo {\n    @MaxLength(2, { message: CommonValidationMessages.MaxLength(2) })\n    @MinLength(1, { message: CommonValidationMessages.MinLength(1) })\n    @IsNotEmpty({ message: CommonValidationMessages.IsNotEmpty })\n    @IsDefined({ message: CommonValidationMessages.IsDefined })\n    @ValidateNested()\n    bar: string;\n}\n```\n\n\u003ca id=\"Hierarchy\"\u003e\u003c/a\u003e\n###  Hierarchy\n\n[class-transformer](https://github.com/typestack/class-transformer) together\nwith [Infobip Jackson Extension](https://github.com/infobip/infobip-jackson-extension) is used to handle hierarchies.\n\nExample for a hierarchy is a multi level hierarchy for inbound and outbound messages.\n\nInput:\n\n```java\n\nenum Channel {\n    SMS(InboundSmsMessage.class, OutboundSmsMessage.class);\n\n    private final Class\u003c? extends InboundMessage\u003e inboundMessageType;\n    private final Class\u003c? extends OutboundMessage\u003e outboundMessageType;\n\n    Channel(Class\u003c? extends InboundMessage\u003e inboundMessageType,\n            Class\u003c? extends OutboundMessage\u003e outboundMessageType) {\n        this.inboundMessageType = inboundMessageType;\n        this.outboundMessageType = outboundMessageType;\n    }\n\n    public Class\u003c? extends InboundMessage\u003e getInboundMessageType() {\n        return inboundMessageType;\n    }\n\n    public Class\u003c? extends OutboundMessage\u003e getOutboundMessageType() {\n        return outboundMessageType;\n    }\n}\n\nenum Direction implements TypeProvider\u003cMessage\u003e {\n    INBOUND(InboundMessage.class),\n    OUTBOUND(OutboundMessage.class);\n\n    private final Class\u003c? extends Message\u003e type;\n\n    Direction(Class\u003c? extends Message\u003e type) {\n        this.type = type;\n    }\n\n    @Override\n    public Class\u003c? extends Message\u003e getType() {\n        return type;\n    }\n}\n\npublic enum CommonContentType implements TypeProvider\u003cCommonContent\u003e, ContentType {\n    TEXT(TextContent.class);\n\n    private final Class\u003c? extends CommonContent\u003e type;\n\n    CommonContentType(Class\u003c? extends CommonContent\u003e type) {\n        this.type = type;\n    }\n\n    @Override\n    public Class\u003c? extends CommonContent\u003e getType() {\n        return type;\n    }\n}\n\n@JsonTypeResolveWith(InboundMessageJsonTypeResolver.class)\ninterface InboundMessage extends Message {\n\n    @Override\n    default Direction getDirection() {\n        return Direction.INBOUND;\n    }\n\n}\n\n@JsonTypeResolveWith(MessageJsonTypeResolver.class)\ninterface Message {\n\n    Direction getDirection();\n\n    Channel getChannel();\n\n}\n\n@JsonTypeResolveWith(OutboundMessageJsonTypeResolver.class)\ninterface OutboundMessage extends Message {\n\n    @Override\n    default Direction getDirection() {\n        return Direction.OUTBOUND;\n    }\n\n}\n\npublic interface CommonContent extends SimpleJsonHierarchy\u003cCommonContentType\u003e, Content\u003cCommonContentType\u003e {\n\n}\n\npublic interface Content\u003cT extends ContentType\u003e {\n\n    T getType();\n\n}\n\npublic interface ContentType {\n\n}\n\nrecord TextContent(\n\n        @NotNull\n        @NotEmpty\n        String text\n\n) implements CommonContent {\n\n    @Override\n    public CommonContentType getType() {\n        return CommonContentType.TEXT;\n    }\n}\n\nrecord InboundSmsMessage(\n\n        CommonContent content\n\n) implements InboundMessage {\n\n    @Override\n    public Channel getChannel() {\n        return Channel.SMS;\n    }\n}\n\nrecord OutboundSmsMessage(\n\n        CommonContent content\n\n) implements OutboundMessage {\n\n    @Override\n    public Channel getChannel() {\n        return Channel.SMS;\n    }\n}\n```\n\nResult:\n\n```typescript\n/* tslint:disable */\n/* eslint-disable */\nimport { Type } from 'class-transformer';\nimport { IsDefined, IsNotEmpty } from 'class-validator';\nimport { CommonValidationMessages } from './CommonValidationMessages';\n\nexport enum Channel {\n    SMS = 'SMS',\n}\n\nexport enum Direction {\n    INBOUND = 'INBOUND',\n    OUTBOUND = 'OUTBOUND',\n}\n\nexport enum CommonContentType {\n    TEXT = 'TEXT',\n}\n\nexport interface InboundMessage extends Message {\n}\n\nexport interface Message {\n    channel: Channel;\n    direction: Direction;\n}\n\nexport interface OutboundMessage extends Message {\n}\n\nexport interface CommonContent extends Content\u003cCommonContentType\u003e {\n    type: CommonContentType;\n}\n\nexport interface Content\u003cT\u003e {\n    type: T;\n}\n\nexport interface ContentType {\n}\n\nexport class TextContent implements CommonContent {\n    readonly type: CommonContentType = CommonContentType.TEXT;\n    @IsDefined({ message: CommonValidationMessages.IsDefined })\n    @IsNotEmpty({ message: CommonValidationMessages.IsNotEmpty })\n    text: string;\n}\n\nexport class InboundSmsMessage implements InboundMessage {\n    readonly channel: Channel = Channel.SMS;\n    direction: Direction;\n    @Type(() =\u003e Object, {\n        discriminator: {\n            property: 'type', subTypes: [\n                { value: TextContent, name: CommonContentType.TEXT }\n            ]\n        }\n    })\n    content: CommonContent;\n}\n\nexport class OutboundSmsMessage implements OutboundMessage {\n    readonly channel: Channel = Channel.SMS;\n    direction: Direction;\n    @Type(() =\u003e Object, {\n        discriminator: {\n            property: 'type', subTypes: [\n                { value: TextContent, name: CommonContentType.TEXT }\n            ]\n        }\n    })\n    content: CommonContent;\n}\n```\n\n\u003ca id=\"CustomValidationToClassValidatorTranslation\"\u003e\u003c/a\u003e\n### Custom Validation to class-validator translation\n\n\u003ca id=\"CustomTypeScriptDecoratorAnnotation\"\u003e\u003c/a\u003e\n#### @CustomTypeScriptDecorator\n\nIn order to link custom java validation annotation with appropriate decorator, java validation annotation must be marked **@CustomTypeScriptDecorator**\nannotation.\n\n+ **@CustomTypeScriptDecorator** has:\n    * **typeScriptDecorator** optional parameter:\n        * if a provided annotation is going to be linked to a decorator with a specified name\n        * else it is going to be linked to a decorator with the same name as an annotation\n    * **decoratorParameterListExtractor** optional parameter:\n        * provides an implementation of a class which extracts annotation parameters\n        * provided class must implement **DecoratorParameterListExtractor** interface\n\nAlso in class which extends from **TypeScriptFileGenerator** two methods must be overridden:\n\n* **getCustomValidationAnnotationSettings**:\n    * Which will provide settings needed for locating custom java annotations. Setting will provide name of java package from where annotation scanning will\n      begin.\n* **getCustomDecoratorBasePath**:\n    * Which will provide base path to TypeScript decorators\n\nAfter providing the above information, **TypeScriptFileGenerator** will take a scan project for custom annotations and will perform logic to link annotations wit\nappropriate TypeScript decorators.\n\n\u003ca id=\"CustomValidationLimitations\"\u003e\u003c/a\u003e\n#### Limitations\n\n1. From class-validation only [Custom Validation Decorators](https://github.com/typestack/class-validator#custom-validation-decorators) are supported, reason\n   behind supporting only [Custom Validation Decorators](https://github.com/typestack/class-validator#custom-validation-decorators) and not\n   supporting [Custom Validation Classes]([Custom Validation Decorators](https://github.com/typestack/class-validator#custom-validation-decorators)) as well, is\n   that in first approach we are able to link custom java annotation with TypescriptDecorator by just looking at decorator's file name, while in second approach\n   we would need to parse a whole file in order to find where is a decorator defined.\n2. TypeScript decorator file name and decorator must be the same, reason behind this is similar to previous point, if we decide to not follow given convention we\n   would need to parse the whole Typescript file in order to find a given decorator. This would introduce additional complexity. This also means that one decorator is\n   located in one TypeScript file.\n3. All decorators will be copied in **dist** location under **validation** folder.\n4. **getCustomValidationAnnotationSettings** must be overridden in class which extends from **TypeScriptFileGenerator**. This is done to restrict scope of **\n   ClassGraph** annotation scanning. By default, **ClassGraph** will scan all classes in the class path and will try to extract annotations from them, if\n   restriction is not performed given operation could result in **OutOfMemoryError**.\n\n\u003ca id=\"CustomValidationExample\"\u003e\u003c/a\u003e\n#### Example\n\nAnnotation implementation:\n\n```java\n\n@CustomTypeScriptDecorator(\n    typeScriptDecorator = \"ComplexValidator\",\n    decoratorParameterListExtractor = DecoratorParameterListExtractorImpl.class,\n    type = ComplexCustomValidation.class)\n@Target(ElementType.FIELD)\n@Retention(RetentionPolicy.RUNTIME)\n@Constraint(validatedBy = ComplexCustomValidator.class)\npublic @interface ComplexCustomValidation {\n\n    String message() default \"must be valid element\";\n\n    int length();\n\n    Class\u003c?\u003e[] groups() default {};\n\n    Class\u003c? extends Payload\u003e[] payload() default {};\n\n}\n```\n\nInput:\n\n```java\n\npublic record Foo(\n\n        @ComplexCustomValidation(length = 100)\n        String bar\n\n) {\n\n}\n```\n\nResult:\n\n```typescript\n/* tslint:disable */\n/* eslint-disable */\nimport { CommonValidationMessages } from './CommonValidationMessages';\nimport { localize } from './Localization';\n\nexport class Foo {\n    @ComplexValidator(100, { message: localize('must be valid element') })\n    bar: string;\n}\n```\n\n\u003ca id=\"Localization\u0026CommonValidationMessages\"\u003e\u003c/a\u003e\n### Localization.ts \u0026 CommonValidationMessages.ts\n\nRunning **TypeScript Generator Extension** will result in two additional files:\n\n* **Localization.ts**\n    * Blueprint for Localization functionality\n* **CommonValidationMessages.ts**\n    * Blueprint for common validation messages\n\nAfter initial generation, this two files can be changed in order to adjust custom validation messages, or to implement a way how localization is supported.\nAfter manually making changes, you can tell **TypeScript Generator Extension** not to generate this files and more in the future. You can achieve this by\noverriding:\n\n* **writeCommonValidationMessagesTypeScriptFile** for **CommonValidationMessages.ts**:\n  * ```java\n    @Override\n    protected void writeCommonValidationMessagesTypeScriptFile(String code, Path filePath) {}\n    ```\n* **writeLocalization** **Localization.ts**:\n  * ```java\n    @Override\n    protected void writeLocalization(String code, Path filePath) {}\n    ```\n\n\u003ca id=\"TimeTypeMappings\"\u003e\u003c/a\u003e\n## Time type mappings\n\nTime type mappings are mapped to string by default.\n\n```java\nimport java.time.*;\n\npublic record Foo(\n\n        Instant instant,\n\n        LocalDateTime localDateTime,\n\n        ZonedDateTime zonedDateTime,\n\n        Duration duration\n\n) {\n\n\n}\n```\n\n```typescript\n/* tslint:disable */\n/* eslint-disable */\n\nexport class Foo {\n    instant: string;\n    localDateTime: string;\n    zonedDateTime: string;\n    duration: string;\n}\n```\n\n\u003ca id=\"AnnotationProcessor\"\u003e\u003c/a\u003e\n## Annotation processor\n\nDisclaimer: in order for annotation processor to work model classes and generator configuration have to be compiled before annotation processor is run. In\npractice this means that they have to be in separate modules.\n\nMain advantage of this approach: easier extension, reusability and no requirement to run Maven to generate TypeScript!\n\nMost, if not all, options available to TypeScript Generator Maven Plugin are also available to the annotation processor.\n\nSetup:\n\n1. In Maven module where Java model is defined add the following dependency:\n   ```xml\n   \u003cdependency\u003e\n      \u003cgroupId\u003ecom.infobip\u003c/groupId\u003e\n      \u003cartifactId\u003einfobip-typescript-generator-extension-api\u003c/artifactId\u003e\n      \u003cversion\u003e${infobip-typescript-generator-extension.version}\u003c/version\u003e\n   \u003c/dependency\u003e\n   ```\n2. Configure the generator by extending TypeScriptFileGenerator:\n\n   ```java\n   public class SimpleTypeScriptFileGenerator extends TypeScriptFileGenerator {\n   \n       public SimpleTypeScriptFileGenerator(Path basePath) {\n           super(basePath);\n       }\n   \n       @Override\n       public Input getInput() {\n           return Input.from(Foo.class);\n       }\n   \n       @Override\n       public Path outputFilePath(Path basePath) {\n           Path lib = basePath.getParent().getParent().resolve(\"dist\");\n   \n           try {\n               Files.createDirectories(lib);\n           } catch (IOException e) {\n               throw new UncheckedIOException(e);\n           }\n   \n           return lib.resolve(\"Simple.ts\");\n       }\n   \n       @Override\n        protected Path getDecoratorBasePath() {\n            return getBasePath().getParent().getParent().resolve(\"src/main/typescript/decorators\");\n        }\n   }\n   ```\n3. Custom java validation annotations must be marked with **@CustomTSDecorator** annotation. If a custom validation annotation name is not the same as a\n   decorator name, you can specify decorator by using **typeScriptDecorator** annotation property.\n\n4. Project only supports class-validator custom decorators [custom decorators](https://github.com/typestack/class-validator#custom-validation-decorators)\n\n5. By overriding **getDecoratorBasePath()** you are specifying path to typescript decorators which relates to custom java validations:\n   ```java\n        @Override\n        protected Path getDecoratorBasePath() {\n            return getBasePath().getParent().getParent().resolve(\"src/main/typescript/decorators\");\n        }\n   ```\n\n6. Define a separate module where annotation processing will occur (this module depends on model module)\n   with following dependency:\n   ```xml\n   \u003cdependency\u003e\n      \u003cgroupId\u003ecom.infobip\u003c/groupId\u003e\n      \u003cartifactId\u003einfobip-typescript-generator-extension-api\u003c/artifactId\u003e\n      \u003cversion\u003e${infobip-typescript-generator-extension.version}\u003c/version\u003e\n   \u003c/dependency\u003e\n   ```\n7. Add the annotation configuration class (this is only used to trigger the annotation processing with annotation):\n   ```java\n   @GenerateTypescript(generator = SimpleTypeScriptFileGenerator.class)\n   public class SimpleTypeScriptFileGeneratorConfiguration {\n   }\n   ```\n\nFor more complex examples look at\n[infobip-typescript-generator-extension-model-showcase](infobip-typescript-generator-extension-model-showcase) and at\n[infobip-typescript-generator-extension-annotation-processor-showcase](infobip-typescript-generator-extension-annotation-processor-showcase).\n\nGenerated typescript can be seen in [dist folder](infobip-typescript-generator-extension-annotation-processor-showcase/dist). In production you'd probably add\ndist to .gitignore, here it's not mainly to be used a an showcase of how the end result looks like.\n\nSince there's no maven plugin it's possible to run TypeScript Generator with multiple different configurations in same project!\nAforementioned showcase folders use this to test and showcase different parts of functionality.\n\n\u003ca id=\"Contributing\"\u003e\u003c/a\u003e\n## Contributing\n\nIf you have an idea for a new feature or want to report a bug please use the issue tracker.\n\nPull requests are welcome!\n\n\u003ca id=\"License\"\u003e\u003c/a\u003e\n## License\n\nThis library is licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfobip%2Finfobip-typescript-generator-extension","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finfobip%2Finfobip-typescript-generator-extension","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfobip%2Finfobip-typescript-generator-extension/lists"}