Hookyns / tst-reflect

Advanced TypeScript runtime reflection system
MIT License
337 stars 11 forks source link

Participation in the new version of the system #78

Open Hookyns opened 1 year ago

Hookyns commented 1 year ago

Sign here who wants to participate in the alpha of the new major version. I will invite you to the private project when it is ready. πŸŽ‰

seo-rii commented 4 months ago

Can I also join in? Thank you!

moodmosaic commented 4 months ago

@Hookyns, count me in pleaseπŸ‘

tyler-mitchell commented 3 months ago

Looking forward to checking out the new version πŸ”₯

Papipo commented 3 weeks ago

Can I use this to generate Teal types from TS ones? (Writing my own generator) I so, please add me to the repo. Otherwise, if you know of any tool I can use for this task please tell me. Thanks!

Hookyns commented 3 weeks ago

Can I use this to generate Teal types from TS ones? (Writing my own generator) I so, please add me to the repo. Otherwise, if you know of any tool I can use for this task please tell me. Thanks!

Hello @Papipo, Yes, you can. It generates some kind of database of types. You can select them and do whatever you want with them. You'll have access to modules, types contained in those modules, classes, functions, interfaces, type aliases, methods of classes/objects, properties etc.

I created a demo of such generator for you. In that demo, there are two folders (two projects), some-ts-project and tl-generator; ignore other stuff in the root directory.

In the tl-generator there is the index.ts file which is super simple cmd tool. It requires path to typelib (metadata generated from some TS project; generated by the rttist - new tool from Alpha version) and path to output directory for generated LUA files (generating files to FS is not implemented in the demo). I use ts-node to run that script. It's in the root package.json file.

Papipo commented 2 weeks ago

Amazing! Can I get access to the repo? Thanks!

BTW, how does this lib compare to ts-morph? I have been using it but I think the DX is not as pleasant as it could be.

Hookyns commented 2 weeks ago

Amazing! Can I get access to the repo? Thanks!

BTW, how does this lib compare to ts-morph? I have been using it but I think the DX is not as pleasant as it could be.

ts-morph is wrapper for TS compiler API. It helps you manipulate the TS source code. With RTTIST you do nothing with the source code, I do that for you; I traverse the TS code, handle different TS versions, find all types, all their properties, methods, decorators etc. and I create one file with metadata about all the types. Then I provide you runtime API you can use to read that metadata file. So you will use RTTIST cli that will generate metadata from a TS project and you can work with it at runtime. It's typical reflection as you may know from eg. C#, Java, Go etc.

With this, you can access metadata about all the types in the project:

const allTypesInWholeProject: Type[] = Metadata.getTypes();

So you can write a node.js script that will read your TS project, you'll receive all the TS metadata and you can just generate LUA files using the node:fs API.

Bit more info in the docs. Still WIP.

Definition of Type and Module and eg. base Object type (Taken from .d.ts inside rttist package. Not documented yet.) ```typescript class ObjectLikeTypeBase extends Type { /** * Returns array of properties. */ getProperties(): ReadonlyArray; /** * Returns property matched by name. */ getProperty(name: string | number | symbol): PropertyInfo | undefined; /** * Returns array of indexes. */ getIndexes(): ReadonlyArray; /** * Returns array of methods. */ getMethods(): ReadonlyArray; /** * Returns method matched by name. */ getMethod(name: string | number | symbol): MethodInfo | undefined; } class MethodInfo { /** * Name of the method. */ get name(): MemberName; /** * Method is optional. */ get optional(): boolean; /** * Access modifier. */ get accessModifier(): AccessModifier; /** * Returns array of decorators. */ getDecorators(): ReadonlyArray; /** * Returns array of method signatures. */ getSignatures(): ReadonlyArray; toString(): string; } class SignatureInfo { /** * Return type of the method. */ get returnType(): Type; /** * Returns parameters of the signature. */ getParameters(): ReadonlyArray; /** * Returns array of type parameters. */ getTypeParameters(): ReadonlyArray; toString(): string; } class ParameterInfo { /** * Name of the parameter. */ readonly name: string; /** * Parameter is optional. */ readonly optional: boolean; /** * Parameter is the rest rest parameter. */ readonly rest: boolean; /** * Type of the parameter. */ get type(): Type; /** * Returns array of decorators. */ getDecorators(): ReadonlyArray; toString(): string; } class Type { static readonly Invalid: Type; static readonly NonPrimitiveObject: Type; static readonly Any: Type; static readonly Unknown: Type; static readonly Void: Type; static readonly Never: Type; static readonly Null: Type; static readonly Undefined: Type; static readonly String: Type; static readonly Number: Type; static readonly BigInt: Type; static readonly Boolean: Type; static readonly True: Type; static readonly False: Type; static readonly Date: Type; static readonly Error: Type; static readonly Symbol: Type; static readonly UniqueSymbol: Type; static readonly RegExp: Type; static readonly Int8Array: Type; static readonly Uint8Array: Type; static readonly Uint8ClampedArray: Type; static readonly Int16Array: Type; static readonly Uint16Array: Type; static readonly Int32Array: Type; static readonly Uint32Array: Type; static readonly Float32Array: Type; static readonly Float64Array: Type; static readonly BigInt64Array: Type; static readonly BigUint64Array: Type; static readonly ArrayDefinition: Type; static readonly TupleDefinition: Type; static readonly ReadonlyArrayDefinition: Type; static readonly MapDefinition: Type; static readonly WeakMapDefinition: Type; static readonly SetDefinition: Type; static readonly WeakSetDefinition: Type; static readonly PromiseDefinition: Type; static readonly GeneratorDefinition: Type; static readonly AsyncGeneratorDefinition: Type; static readonly IteratorDefinition: Type; static readonly IterableDefinition: Type; static readonly IterableIteratorDefinition: Type; static readonly AsyncIteratorDefinition: Type; static readonly AsyncIterableDefinition: Type; static readonly AsyncIterableIteratorDefinition: Type; static readonly ArrayBuffer: Type; static readonly SharedArrayBuffer: Type; static readonly Atomics: Type; static readonly DataView: Type; static readonly Type: Type; static readonly Module: Type; /** * Type identifier. */ get id(): TypeIdentifier; get displayName(): string; /** * Kind of the type. */ get kind(): TypeKind; /** * Module which declare type represented by the this Type instance. */ get module(): Module; /** * Name of the type. */ get name(): string; /** * Type is exported from its Module. */ get exported(): boolean; /** * Type has iterator, is iterable. */ get iterable(): boolean; /** * Type is nullable so null and undefined are valid values for the type. */ get nullable(): boolean; /** * Definition of the generic type. * @internal Hidden in Type; Should be visible only by GenericTypeDefinition<>. */ get genericTypeDefinition(): GenericType | undefined; /** * Returns true if type is equal to type passed as type argument. */ is(): boolean; /** * Returns true if types are equal. * @param target */ is(target: TType): target is TType; /** * Returns array of generic type arguments. * @internal Exposed by {@link GenericType}. */ getTypeArguments(): ReadonlyArray; /** * Check whether the type is generic. */ isGenericType(): this is GenericType; /** * Check whether the type is definition of the generic type. */ isGenericTypeDefinition(): this is GenericType; /** * Check whether the type is generic type parameter. */ isTypeParameter(): this is TypeParameterType; /** * Returns a value indicating whether the Type is container for unified Types or not. */ isUnion(): this is UnionType; /** * Returns a value indicating whether the Type is container for intersecting Types or not. */ isIntersection(): this is IntersectionType; /** * Returns a value indicating whether the Type is a class or not. */ isClass(): this is ClassType; /** * Returns a value indicating whether the Type is a interface or not. */ isInterface(): this is InterfaceType; /** * Returns a value indicating whether the Type is a interface or not. */ isTypeAlias(): this is TypeAliasType; /** * Returns a value indicating whether the Type is an literal or not. */ isLiteral(): this is LiteralType; /** * Returns true if type is union or intersection of types */ isUnionOrIntersection(): this is UnionType | IntersectionType; /** * Check if this type is an Array. */ isArray(): this is ArrayType; /** * Check if this type is a Tuple. */ isTuple(): this is TupleType; /** * Determines whether the object represented by the current Type is an Enum. * @return {boolean} */ isEnum(): this is EnumType; /** * Determines whether the object represented by the current Type is an Conditional type. * @return {boolean} */ isConditional(): this is ConditionalType; /** * Determines whether the object represented by the current Type is an object-like type. */ isObjectLike(): this is ObjectLikeTypeBase; /** * Determines whether the object represented by the current Type is an Object type. */ isObject(): this is ObjectType; /** * Determines whether the object represented by the current Type is an Template type. */ isTemplate(): this is TemplateType; /** * Determines whether the object represented by the current Type is a Function type. */ isFunction(): this is FunctionType; /** * Determines whether the object represented by the current Type is one of the predefined ES symbols. */ isESSymbol(): this is ESSymbolType; /** * Determines whether the object represented by the current Type is an unique symbol. */ isUniqueSymbol(): this is UniqueSymbolType; /** * Returns true whether current Type is instantiable. */ isInstantiable(): boolean; /** * Check if this is a primitive type ("string", "number", "boolean" etc.). */ isPrimitive(): boolean; /** * Check if this type is a string. */ isString(): boolean; /** * Check if this type is a number. */ isNumber(): boolean; /** * Check if this type is a bigint. */ isBigInt(): boolean; /** * Check if this type is a boolean. */ isBoolean(): boolean; /** * Check if this type is an "any". */ isAny(): boolean; /** * Check if this type is an "never". */ isNever(): boolean; /** * Check if this type is an "void". */ isVoid(): boolean; /** * Check if this type is an "undefined". */ isUndefined(): boolean; /** * Check if this type is an "null". */ isNull(): boolean; /** * Returns string representation of the type. * @returns {string} Returns string in format "Kind{fullName}" */ toString(): string; } ``` ```typescript class Module { /** * Module for all the native types. */ static readonly Native: Module; /** * Module for dynamic types without specific module. */ static readonly Dynamic: Module; /** * Unknown module. */ static readonly Invalid: Module; /** * The name of the module. * @description It is filename of the module in the most of the cases. */ readonly name: string; /** * The path of the module. */ readonly path: string; /** * Module identifier. */ get id(): ModuleIdentifier; /** * Returns array of modules required by this Module. * @description These are all the imported modules. */ getChildren(): ReadonlyArray; /** * Returns array of types from the module. */ getTypes(): ReadonlyArray; /** * Imports module and returns exported object. */ import(): Promise; } ```
Papipo commented 2 weeks ago

I see there is in progress support for vite. In fact my use case is with Vue SFCs: I am writing the UI of a game using them and I want to grab its Props interface to aid in the development of the backend game rules, which will be written in Teal. So right now I am using @vue/compiler-sfc to extract the scriptSetup.

Hookyns commented 2 weeks ago

I see there is in progress support for vite. In fact my use case is with Vue SFCs: I am writing the UI of a game using them and I want to grab its Props interface to aid in the development of the backend game rules, which will be written in Teal. So right now I am using @vue/compiler-sfc to extract the scriptSetup.

@Papipo Does it have to be "real-time"? I mean, does it have to be generated with the HMR/fast-reload while you develop your Vue app? Or can it be CLI tool that you'll run here and there?

Can you prepare some demo project on StackBlitz? How does your components look like, how the props are defined and what the output should be?

Papipo commented 2 weeks ago

For now I am going with a CLI but honestly I thought about using a watcher at some point. Not sure if I will be coding both the FE at the same time, but it might happen.

For now the approach is to code the FE in Vue, as I can use storybook or in general fake payloads, and only then, transform the entry point Props interface into Teal types to start coding the backend, but we'll see (the project is a year old but only now I've decided to go this route). After all, it's the FE who dictates what props it needs.

I'll try to give you a sample repo but that'll have to wait at least a week because I am on vacation for the rest of the week.

BTW, if you are curious, I plan to use Elixir for the host app, Luerl (an implementation of lua to be run in the BEAM) to run the game engine/rules (lua via teal compiler) and LiveVue to update the Vue props via a websocket (hence the requirement to transform types, for pure sanity). We'll see if it works as intended πŸ˜