Hookyns / tst-reflect

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

[Question] Anyway to add it as angular transformer #74

Closed yd021976 closed 1 year ago

yd021976 commented 1 year ago

Hi and thank you for this great tool ! I wonder if there is any chance to configure test-reflect to work with angular ngc (i.e. with command like "ng build" or "ng serve")

I know the trick to add a custom transformer in the web pack config in angular as described in the Medium article custom-typescript-transformers

But what I don't get is that in test-reflect transform function (in index.ts file), the function expect a ts.Program parameter, but the angular fileEmitter function call the custom transformer with a ts.TransformationContext parameter and I don't know how to get the ts.Program => I get why test-relfect expects ts.Program as compiler like ttypescript call transformers with ts.Program as parameter...

So, I really need reflection feature in my angular project to do "clean code" and avoid using boilerplate that would complexify the code and maintainability.

Do you have any suggestions for this case ?

Thank you for your help.

Hookyns commented 1 year ago

Hi @yd021976, I created this for Angular support https://github.com/Hookyns/ng-custom-transformers

Not tested with the latest version and there is an issue with hot-reloads, but it will be solved in upcoming v1.0.

Other way is ts-patch.

Hookyns commented 1 year ago

Here is a StackBlitz demo.

yd021976 commented 1 year ago

Thank you very much ! I'll try this today :-) I think you should add this to the "installation" guide as an exemple or sort of how to. I'm sure I'm not the only one looking for Angular integration of your great tool.

yd021976 commented 1 year ago

Hi @yd021976, I created this for Angular support https://github.com/Hookyns/ng-custom-transformers

Not tested with the latest version and there is an issue with hot-reloads, but it will be solved in upcoming v1.0.

Other way is ts-patch.

Hello again, I configured with success test-reflection-transformer thanks to your help. Anyway, I'm afraid it is not "compatible" with Angular, I fall into some strange errors :

For some classes (not all, only some...) test-reflect-transformer emit an error saying Module not found: Error: Can't resolve './classe.file.js' in '<myClassThatUseGetType>'

I tried to disable AOT, but same error.

I think for any reasons, tst-reflect-transformer transpile some code to import my class file as a ".js" file that doesn't exist.

Any idea or is a known problem ?

Thank you

Hookyns commented 1 year ago

Angular is complex closed system, there can be many issues with many different things.

Some issues can be caused by transpileOnly or noLib options; or by incremental builds (you can check this by doing a clean build - remove dist files and remove xx.tsbuildinfo file(s)).

For some classes (not all, only some...) test-reflect-transformer emit an error saying Module not found: Error: Can't resolve './classe.file.js' in ''

There is no such error in tst-reflect. It must be from Angular or from TypeScript iself. If you create a repro, I can try to look at it.

yd021976 commented 1 year ago

Hello, here is a simple GitHub repo that generates the error angular tst-reflect error The code causing error is in the app.component.ts file, it's the code using getType on the CTest class const t2 = getType<CTest>()

I simply use the command ng build --plugin ng-custom-transformers to build the project and I correctly configure tst-reflect plugin. Note that if I build without --plugin ng-custom-transformers I have no error.

I think "ng-custom-transformers" generates an "import test.class.js" statement in sourcecode but the file never exists as the angular project is transpiled in main.js, polyfill.js and runtime.js. But I don't know if it is "normal". In addition, I see that the tst-reflect-transformer doesn't create the metadata.lib.ts file as it is describe in the readme.md of your project. Don't know if it should or not, and if so, don't get how to configure it.

Thank you if you can investigate this problem.

yd021976 commented 1 year ago

After looking a while in web pack generated files, I see that tst-reflect-transformer generates this code for my CTest class

_ßr.Type.store.set(19286, { k: 1, isg: false, n: "CTest", fn: "tst-reflect/src/base/test.class.ts:CTest#19286", props: [{ n: "foo", t: _ßr.Type.store.get(19285), am: 2, acs: 0, ro: false, o: false }], indxs: [], args: [], ctors: [{ params: [] }], ctor: function () {
        return import("./../base/test.class.js").then(m => m.CTest);
    } });

Unfortunably, the import file "./../base/test.class.js" is never generated by the compiler (angular or typescript, I don't know)

I think it comes from tst-reflect-transformer NodeGenerator.ts source file that calls ts.factory.createImportDeclaration.

Hookyns commented 1 year ago

Hi, thanks for the repro! I did not run it yet (I'm on phone) but now I understand what is happening.

It must be an issue in some bundling phase of ng build caused by tst-reflect-transformer because it generate imports with extension (because of nodenext/esm modul type). Angular probably contains strict checks and validate the import paths and throw because there is no .js file, just .ts file so it don't know which file it should follow and bundle.

Solution is removing the extension or setting the extension to ts. But I have to test the nodenext again because this will break it imho. Maybe I will introduce new option or base the generation of the extension on module type.

The thing here is, that we generate the import, it is regular import clause at typescript level so bundlers can understand, follow it and transform it to something else, depending on the bundle rules. Because it is .js, it didn't transform that to correct path so there are imports with wrong path in the final JS.

PS: There are two modes: inline and typelib. One generate inline/infile metadata and the second generate the standalone metadata library file. Check https://github.com/Hookyns/tst-reflect/wiki/Configuration

Hookyns commented 1 year ago

Okay, I've published tst-reflect-transformer@0.12.1 which fix the issue. But there is still an issue with hot-reload -> reflection disappear when recompiled after changes.

yd021976 commented 1 year ago

Very great job, thanks a lot ! Angular project build without errors :-) But... There is another bug : The type of runtime vars failed. I think this is because the "reflect-id" doesn't exist in test-reflect store (I don't know why, but I see that it store only "one" reference).

I've updated my repro with comments out in appComponent.ts what is working and what is not.

PS : Yes, there is the "hot reload" issue. I will try to disable the hot reload if possible. If you have a trick/fix let me know !

Hookyns commented 1 year ago

What's the syntax you used? There is no code in your demo repo. getType<test>()? It must be getType<typeof test>() or getType(test). getType<test> is not valid TS syntax. image


I pushed an angular example to this repo yesterday, and it works. See line 21. https://github.com/Hookyns/tst-reflect/blob/58f1e67612a28ee4c5f1ff2ae789c7bb5ec70c09/examples/angular/src/app/app.component.ts#L14-L26

Hookyns commented 1 year ago

Hot reloads can be hot-fixed by adding this to polyfills:

import { Type, getType } from "tst-reflect";
(window as any)._ßr = { Type, getType };

But then there is an issue with generated type IDs, which is related to global issue with incremental build.

IDs of types are the IDs which assign TypeScript itself. But when you do any partial (incremental) build, IDs are different. But not changed parts have references to old IDs and new parts have the new IDs. This no more an issue in new upcoming major version, but it is an issue in current public version and it cannot be fixed.

yd021976 commented 1 year ago

What's the syntax you used? There is no code in your demo repo. getType<test>()? It must be getType<typeof test>() or getType(test). getType<test> is not valid TS syntax. image

I pushed an angular example to this repo yesterday, and it works. See line 21.

https://github.com/Hookyns/tst-reflect/blob/58f1e67612a28ee4c5f1ff2ae789c7bb5ec70c09/examples/angular/src/app/app.component.ts#L14-L26

oupsss.. there was a line of code missing in my repo, below the syntax I'm using in my repo there is code in the src/app and src/base folders, the link is still tst-reflect-test

I'll do some new tests today, but good to ear your tests work well PS : Thanks for the quick and dirty fix for the hot-reload

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'tst-reflect';
  foo: CTest = new CTest()

  ngOnInit() {
    const t = getType<ITest>() // work : Type found
    const t2 = getType<CTest>() // work : type found

    /** This will not work : The type of <test> runtime var is not found.
     * It seems because the "reflect_id" of class CTest is not found in the tst-reflect "store" property 
     */
    const test = new CTest()
    let t3 = getType(test)
  }
}
Hookyns commented 1 year ago
let t3 = getType(test);

Works for me. It only doesn't work after hot reload, as I said, cuz of different id assigned after change. If it is doesn't work for you even after full build, try to clear ng cache, do complete rebuild.

yd021976 commented 1 year ago

Hello, no problem, it works like a charm. I don't know what I've done this morning, but it was not working. Now it's ok. I'm very impatient for the v1.0 release :-)

Very great library ! And thank you for your help

yd021976 commented 1 year ago

Ho, I forgot one more thing. Do you think it would be a good idea to update the doc to explain how to configure tst-reflect for Angular ? If so, I can write a proposal update of the README.md. Just tell me.

Hookyns commented 1 year ago

Ho, I forgot one more thing. Do you think it would be a good idea to update the doc to explain how to configure tst-reflect for Angular ? If so, I can write a proposal update of the README.md. Just tell me.

I already wrote something. But I didn't want to publish that, cuz support of Angular is just terrible now and new major version is coming soon and it's a game changer, with custom website and complete documentation.

Hookyns commented 1 year ago

But if you have some points, you can submit a PR. 🙂