Coder Social home page Coder Social logo

gwtscript's Introduction

Hi there

This is GWTScript, tool for TS -> Java translation. It finds marked interfaces, type declarations and translates them into Java classes.

Development:

yarn start:dev

Table of content

Package usage guide

You should import compile() function and run it with config. Following code will find all files matching passed tsconfig, find all interfaces and types with "//@ToJava" comment and generate Java classes in "dist" folder.

import { compile } from '@dx-private/java-translate';
import { resolve } from 'path';

compile({
    tsconfigAbsolutePath: resolve(__dirname, './tsconfig.json'),
    destinationFolder: "dist",
    rootPackage: "com.devexperts.generated",
    generateFunctionType: (parameters, result) => {
        return {
            result: `JsFunc<${result}>`,
            imports: ["com.stuff.JsFunc"]
        }
    },
    generateArrayType: (type) => {
        return {
            imports: [],
            result: `Array<${type}>`
        }
    }
})

User Directives

The only way to interact with this lib from project code is directives. Directive is specific code comment with specific syntax. There are 3 directives:

  • Marking directive, "// @ToJava" by default. Tells translator to take interface or type declaration below and translate it.
  • Ignoring directive, "// @ToJavaIgnore" by default. Tells translator to ignore interface field below.
  • User Input directive, "// @InJava {JavaType} from {import1}, {import2}" by default. Tells translator to not parse interface field type and just take {JavaType} from directive
Marking directive

// @ToJava
interface A {
    a: number
    b: string
    c: boolean
}

// @ToJava 
type B = {
    c: {
        a: number
    }
}

// @ToJava
type C = Pick<A, "a" | "b">

Also you can override type name and package

// overriding name:
// @ToJava as B
interface A {
    a: number
    b: string
    c: boolean
}

// overriding package:
// @ToJava generated.js
// or
// @ToJava to generated.js
interface A {
    a: number
    b: string
    c: boolean
}

// both:
// @ToJava as B to generated.js
// or
// @ToJava to generated.js as B
// or
// @ToJava generated.js as B
// or
/* 
    @ToJava 
        as B
        to generated.js
*/
interface A {
    a: number
    b: string
    c: boolean
}

The provided package will be merged with specified root package and generated at the corresponding path.

For example:

// Source

// @ToJava as JsCarRed to generated.js.cars
interface RedCar {
    speed: number;
    color: string;
}

// @ToJava as JsCarGreen to generated.js.cars
interface GreenCar {
    speed: number;
    color: string;
}

// @ToJava as JsAirplane to generated.js.airplanes
interface Airplane {
    speed: number;
    color: string;
}
// Script

import { compile } from '@dx-private/java-translate';
import { resolve } from 'path';

compile({
    tsconfigAbsolutePath: resolve(__dirname, './tsconfig.json'),
    destinationFolder: "dist",
    rootPackage: "com.devexperts",
    generateFunctionType: (parameters, result) => {
        return {
            result: `JsFunc<${result}>`,
            imports: ["com.stuff.JsFunc"]
        }
    },
    generateArrayType: (type) => {
        return {
            imports: [],
            result: `Array<${type}>`
        }
    }
})

Result packages:

  • com.devexperts.generated.js.cars.jscarred
  • com.devexperts.generated.js.cars.jscargreen
  • com.devexperts.generated.js.airplanes.jsairplane

Result file structure:

  • dist
    • generated
      • js
        • cars
          • jscarred
            • JsCarRed.java
          • jscargreen
            • JsCarGreen.java
        • airplanes
          • jsairplane
            • JsAirplane.java
Ignoring directive

// @ToJava
interface A {
    a: number
    b: string
    c: boolean
    // @ToJavaIgnore
    d: SpecificType
}
User Input directive

// @ToJava
interface A {
    a: number
    // @InJava String
    b: string
    c: boolean
    // @InJava SimpleJavaType<int> from com.all.types.for.u.SimpleJavaType
    d: SpecificType<SpecificTypeA, SpecificTypeB>
    // @InJava MultiImportContainer<MultiImportContent> from com.types.lol.MultiImportContainer, com.dzeta.MultiImportContent
    e: UltraSpecificType
}

Config fields description

tsconfigAbsolutePath

Absolute path to project tsconfig file.

declare type tsconfigAbsolutePath = string;
interfacePredicate (Optional)(Default: /@ToJava/)

Filter (aka selector) (aka predicate) for interfaces and type declarations.

declare type interfacePredicate = 
    | RegExp
    | (node: ts.InterfaceDeclaration | ts.TypeAliasDeclaration) => boolean
filePredicate (Optional)

File filter.

declare type filePredicate = (fileName: string) => boolean
ignoreField (Optional)

Ignoring directive predicate function. true = ignore

declare type ignoreField = 
    | RegExp
    | (field: PropertySignature) => boolean
inJavaRegExpTest (Optional)

If provided replace "User Input directive" tag with passed RegExp

declare type inJavaRegExpTest = RegExp
destinationFolder

Generator destination folder

declare type destinationFolder = string
rootPackage

Root package for classes

declare type destinationFolder = string
generateFunctionType

Configures function field generation. Receives parameters and return type.

declare type generateFunctionType = (
        parameters: Array<{ name: string, type: string }>,
        returnType: string
    ) => {
        result: string;
        imports: string[];
    }

Example:

const generateFunctionType = (parameters: Array<{ name: string, type: string }>, returnType: string) => {
    if (
        returnType === "void"
    ) {
        return {
            result: `Action${
                parameters.length
            }<${parameters
                .map((param) => param.type)
                .join(", ")}>`,
            imports: [
                `com.github.timofeevda.gwt.rxjs.interop.functions.Action${parameters.length}`,
            ],
        };
    }
    return {
        result: `Func${
            parameters.length
        }<${func.parameters
            .map((param) => param.type)
            .join(", ")}, ${returnType}>`,
        imports: [
            `com.github.timofeevda.gwt.rxjs.interop.functions.Func${parameters.length}`,
        ],
    };
}
generateArrayType

Array type generator (same with generateFunctionType)

declare type generateArrayType = (
    type: string,
) => {
    result: string;
    imports: string[];
}

Example:

const generateArrayType = (type: string) => {
    return {
        imports: ["com.devexperts.client.reusable.core.list.JsList"],
        result: `JsList<${type}>`
    }
}
primitiveMapping (Optional)

Mapping for primitives telling generator how to translate NUMBER, STRING, BOOLEAN, ANY types. Example:

const primitiveMapping = {
    BOOLEAN: {
        result: "JsBool",
        imports: ["com.devexperts.client.reusable.core.JsBool"],
    },
    NUMBER: {
        result: "JsDouble",
        imports: ["com.devexperts.client.reusable.core.JsDouble"],
    },
    STRING: {
        result: "JsEnum",
        imports: ["com.devexperts.client.reusable.core.JsEnum"],
    },
    ANY: {
        result: "Object",
        imports: []
    }
}
nativeReferencesMap (Optional)

Mapping for types that should be converted in specific way. If you have type equivalents in both languages, rxjs Observable for example, you should enter this name in config. Example:

const nativeReferencesMap = {
    Bus: {
        import: "com.specific",
        text: "FEEventBus"
    },
    NewsSource: {
        import: "com.specific",
        text: "NewsSource"
    },
    Observable: {
        import: "com.rxjs.Observable",
        text: "Observable"
    }
}
getGroupName (Optional)

Sort generator output in folders by type info. Function should return folder name

interface TypeToGenerate {
    name: string;
    fields: readonly TypeField[];
    sourcePath: string;
}

declare type getGroupName = (type: TypeToGenerate) => string

Custom generator

This feature allows you to generate your own output instead of default Java GWT classes. Basically custom generator is just a function receiving parser output, so you can use it not only for code generation but just to take a look or experiments.

Usage

When you use a custom generator it is no longer necessary to provide basic generator config fields so the config type changes while you use a custom generator.

compile(
    {
        interfacePredicate: /@ToTranslate/,
        tsconfigAbsolutePath: resolve(__dirname, './tsconfig.json'),
    },
    (parsed: ParserOutput) => {
        // Our code
    }
)

Generator function should be passed as a second argument to compile() function and so you can use parser output. The types of nodes provided by Parser Output are described below.

interface ParserOutput {
    typesToGenerate: readonly TypeToGenerate[];
}

// Marked type
interface TypeToGenerate {
    name: string;
    fields: readonly TypeField[];
    sourcePath: string;
}

interface TypeField {
    name: string;
    type: ParsedType;
}

type ParsedType =
    | PrimitiveType
    | FunctionType
    | ObjectType
    | ArrayType
    | UnionType
    | ReferenceType
    | UserType
    | NumberLiteral
    | StringLiteral;

// Basic class for types
abstract class Type<ID, T> {
    public abstract identifier: ID;
    constructor(public type: T) {}
}

/*
    Literals like "string literal" or just 4
    type A = {
        stringLiteral: "str"
        numberLiteral: 4
    }
*/
class Literal<T extends number | string> extends Type<"literal", T> {
    identifier: "literal" = "literal";
}

class NumberLiteral extends Literal<number> {}

class StringLiteral extends Literal<string> {}

// parser only supports this primitives
type PrimitiveTypes = {
    NUMBER: true;
    STRING: true;
    BOOLEAN: true;
    VOID: true;
    ANY: true;
};

class PrimitiveType extends Type<"primitive", keyof PrimitiveTypes> {
    identifier: "primitive" = "primitive";
}

// "type" argument here is the return type
class FunctionType extends Type<"function", ParsedType> {
    identifier: "function" = "function";

    constructor(
        type: ParsedType,
        public parameters: ReadonlyArray<{ name: string; type: ParsedType }>
    ) {
        super(type);
    }
}

class ObjectType extends Type<
    "object",
    ReadonlyArray<{ name: string; type: ParsedType }>
> {
    identifier: "object" = "object";
    constructor(
        value: ReadonlyArray<{ name: string; type: ParsedType }>
    ) {
        super(value);
    }
}

class ArrayType extends Type<"array", ParsedType> {
    identifier: "array" = "array";
}


class UnionType<T extends ParsedType = ParsedType> extends Type<
    "union",
    readonly T[]
> {
    identifier: "union" = "union";
}


class ReferenceType extends Type<
    "reference",
    { typeName: string; genericArgs: readonly ParsedType[] }
> {
    identifier: "reference" = "reference";
}

// The type you get when use User Input directive
class UserType extends Type<
    "hard-coded-type",
    { text: string; imports: string[] }
> {
    identifier: "hard-coded-type" = "hard-coded-type";
}

gwtscript's People

Contributors

astroner avatar dramoturg avatar nikolaenkov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gwtscript's Issues

Java class cannot be generated by extended TS type

For example, we want to generate a java class based on an extended type FakeType, but in the end the class is not generated.

`
type SomeType {
name: string;
}

// @tojava
type FakeType = SomeType & {
surname: string;
}
`

But we can generate a java class based on an extended interface.

`
type SomeType {
name: string;
}

// @tojava
interface FakeType extends SomeType {
surname: string;
}
`

No filepath or class in case of InJava format is incorrect

// @ToJava
export interface SomeInterface {

  // @InJava Observable<String> com.github.timofeevda.gwt.rxjs.interop.observable.Observable from xxx
  field: Observable<string>;
}

In case format is of // @injava annotation is incorrect - you'll receive error about incorrect format but you'll not get the place where the issue takes place.

  Failed to parse InJava comment. Example ...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.