Skip to content
This repository has been archived by the owner on Oct 5, 2021. It is now read-only.

reduce core DI bundle size - ideas #7

Open
jeremy-coleman opened this issue Aug 18, 2019 · 5 comments
Open

reduce core DI bundle size - ideas #7

jeremy-coleman opened this issue Aug 18, 2019 · 5 comments
Assignees
Labels
enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed

Comments

@jeremy-coleman
Copy link

jeremy-coleman commented Aug 18, 2019

Hi, i think its possible to get the core (DI only) bundle size down by about 1000% with a little effort. This will certainly have to happen in the future, so it's better to address it now imo.

check out https://github.com/jmankopf/mani-injector and replace 'reflect-metadata' with @abraham/reflection. works great. (like 300 loc vs rxdi 40k lol).

jupyter lab has a light weight DI system with similar semantics as rxdi , except without decorators. I think it uses bottlejs under the hood.

Also,
I also did a quick test without getting it actually working, removing the ipfs and systemjs stuff from rxdi and got it down to around 7k loc, which that alone could be a good start.

https://github.com/jeffijoe/awilix also is pretty great because it doesn't require any reflection polyfill, and works in the browser by dropping 2 fs methods, which could be kept and swapped for an ipfs solution.

awilix + rxjs container/di
https://github.com/mojzu/container.ts/blob/master/src/container/container.ts

Thought/solution generation and comparison only atm

@Stradivario
Copy link
Member

Stradivario commented Aug 31, 2019

I am really not sure why github doesn't notify me for this important issue!
Looking forward and reading your comment!!!
Will reply with some ideas and hopefully pull request with initial implementation!

I was thinking for this situation from the first moment when i introduce these features but i wanted to have everything working like a MVP and concept and then at later time removing these functionalities from the main bundle.
They are really ugly and not so reusable written, so for sure these functionalities need refactoring inside the new module which will be created and removed from the main bundle!

I am really happy to see that there are some people interested in this Ecosystem :)!!

For now it treats me very well without big problems and it is time to improve core features of the platform and reduce bundle size!!

Thank you again for the issue!

If you use Telegram we can chat for further more details
https://t.me/dependency

Or searching @rxdi

@jeremy-coleman

P.S. One important thing i forgot to say:

I was thinking to remove all dependencies from the project and leave it stand alone! So in the future we need to get rid of everything and leave only dependency injection here.
Why is this important ?

We need to have ability to remove completely npm ecosystem in some period of time so we can have independent decentralized solution which can be helpful when we have nodes everywhere and create good ipfs module coverage.

@Stradivario
Copy link
Member

Stradivario commented May 12, 2020

@jeremy-coleman hi there ! Just for let you know that i have manage to create really small DI 1.31 kb .

https://github.com/r-html/rhtml/tree/master/packages/di

These are the core features for DI without async behaviour introduced inside @rxdi/core so we cannot put Promise or Observable inside custom providers { lazy: true, provide: 'no-name', useValue: Promise.resolve(1) } .
In couple of weeks i will adopt @rhtml/di and will transfer all my skills to extend @rxdi/core with it so we can have the same functionalities like before but without much of an unused code.

A simple example usage for @rhtml/di is:
Example without Reflections !!!

import { Injectable, Inject, set, get } from '@rhtml/di';

class User {
  id = 1;
}
class UserService {
  @Inject(User)
  user: User;
}

@Injectable()
class InjectableService {
  constructor(@Inject(User) private user: User) {}
}

@Module({
  providers: [UserService, InjectableService]
})
class UserModule {}

@Module({
  imports: [UserModule]
})
class AppModule {}
console.log(has(AppModule)); // False
set(AppModule);
const userService = get(UserService);
expect(userService.user.id).toBe(1);

Example with reflections (removes the need of Inject decorator inside constructor as a property since we get can get metadata parameters for the injected class) :

import '@abraham/reflection';

import { Injectable, Inject, set, get } from '@rhtml/di';

class User {
  id = 1;
}
class UserService {
  @Inject(User)
  user: User;
}

@Injectable()
class InjectableService {
  constructor(private user: User) {}
}

@Module({
  providers: [UserService, InjectableService]
})
class UserModule {}

@Module({
  imports: [UserModule]
})

@jeremy-coleman
Copy link
Author

jeremy-coleman commented May 12, 2020

@Stradivario great code, that's impressively concise, even more simple DI than hooks

I was just looking over this the other day, and thought it could be a compliment to rxdi, its a small framework using lit and rx. https://github.com/raycar5/valv

also, lit has a mechanism for resolving async data, check it out https://www.youtube.com/watch?v=Io6JjgckHbg , around 24 minutes 15 seconds

@Stradivario
Copy link
Member

Stradivario commented May 13, 2020

@jeremy-coleman thank you very much i really appreciate it!

I take a look at valv but it looks like it couples the code more than i want it to be coupled... will think about it more.

About lit html:

It has ability to resolve async data but not Observable like data which is streamable. Why ? Because it doesn't unsubscribe the observable from the view and you got a lot of memory leaks. What you could do is just to use Promises but this is not Reactive website it is a bit of statically by my means of reactive website :D.

This is why i decided to Fork lit-html and create @rxdi/lit-html with some Decorator modifications and better bundling.

https://github.com/rxdi/lit-html/blob/master/src/decorators/component.decorator.ts#L133

Here you can check how i deal with subscriptions of the component.

If there are any registered Observables inside this of the component and they subscribe i put subscription to Component subscriptions and when the component is destroyed from the DOM tree all subscriptions are unsubscribed automatically. This way i deal with async data.

After all that knowledge i decided to create Monadic approach for writing WebComponents and HTML in general which is kind of something that i don't see at the moment anywhere.

Let me show you what is going on:

<r-component>
  <r-selector>r-counter</r-selector>
  <r-props>
    <r-prop key="value" type="Number"></r-prop>
  </r-props>
  <r-render .state=${({ value, loading }, setState) => html`
    <button @click=${() => setState({ value: value + value, loading: false })}>
      Increment
    </button>
    <r-if .exp=${loading}>
      Loading...
    </r-if>
    <p>${value}</p>
  `}>
  </r-render>
</r-component>

Then using it

<r-counter value="5"></r-counter>

Basically you can create Webcomponent declarative way which is quite awesome by my opinie.

Maybe you want to check this example also! :))
https://github.com/r-html/example-hydration/blob/master/src/app.component.ts

Another great thing that i use is @rxdi/ui-kit

Demo website
https://rxdi.github.io/ui-kit

These are all created with @rxdi/lit-html
https://github.com/rxdi/ui-kit/tree/master/src

Here is the first experiment with Monadic approach of building html blocks
this is even with animation and Graphql :D

https://rxdi.github.io/ui-kit/graph

Regards!

@Stradivario
Copy link
Member

Stradivario commented Jul 14, 2020

@jeremy-coleman Managed to make it work with 80% of the functionalities of @rxdi/core you can try it out. Soon this old lady here will be replaced by @rhtml/di !! :))

import '@abraham/reflection';

import { Inject, Injectable, InjectionToken } from '@rhtml/di';
import { Bootstrap, Component, Module } from '@rhtml/di/module';

type UserId = number;
const UserId = new InjectionToken<UserId>();

const now = Date.now();

@Injectable()
export class UserService {
  constructor(@Inject(UserId) public userId: number) {
    console.log('[UserService]', userId);
  }
}

@Component()
class AppComponent {
  constructor(public userService: UserService) {
    console.log('[AppComponent] ', userService.userId);
  }

  OnInit() {
    console.log('[AppComponent] Init');
  }

  OnDestroy() {
    console.log('[AppComponent] Destroy');
  }
}

@Module({
  providers: [
    UserService,
    {
      provide: UserId,
      useFactory: () =>
        new Promise<number>(resolve => setTimeout(() => resolve(1234), 1000))
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

Bootstrap(AppModule).then(() =>
  console.log('Started', `after ${Date.now() - now}`)
);

Only 2.5 KB :D

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants