Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plugin in separate repository #45

Open
restjohn opened this issue Apr 7, 2021 · 4 comments
Open

plugin in separate repository #45

restjohn opened this issue Apr 7, 2021 · 4 comments

Comments

@restjohn
Copy link

restjohn commented Apr 7, 2021

Hello and thanks for your article and repo.

Is there a way to extend this architecture such that plugin code can exist in a different Angular library/repository, and build against the core app as an NPM dependency? Would this require that the core app be broken into separate projects, something like a library of injectable services and shared libraries that plugins can inject and use, as well as the main app entry point? The main app would then use the core library, just as the plugins would. A plugin's package.json file might then look something like

{
  ...,
  "dependencies": {
    "@myapp/core_services": "~1.0.0"
    ...
  }

Then NPM would enforce version compatibility between the plugins and the core app.

@GEP-JOHNGOUDA-zz
Copy link

Hi @restjohn yes you can, in fact that is the point behind a plugin architecture. Plugin code shall be separated from the main app and reused whenever possible.

The plugin itself is an Angular library that could be kept and maintained in a separate repository. Builder is building it as an application to benefit the build process of an Angular application optimization and bundling.

Since plugin is just an Angular library it could be published to NPM and installed in a different plugin as a dependency. Building the consumer plugin you could drop the dependency by adding it as an external. You would benefit the version control of NPM package.

Good luck!

@restjohn
Copy link
Author

restjohn commented Apr 18, 2021

Hello and thanks for the reply. I've made an attempt to demonstrate the scenario I want to achieve: https://github.com/restjohn/ngx-plugins. If ever you have some spare moments I'd love to know your thoughts.

You'll see that the host app and shared library reside in the same workspace, while the plugin resides in a separate workspace and references the host shared library as an NPM dependency. I tried to simplify the implementation a bit by removing the custom plugin builder and using Angular's built-in ng-packagr library builder to create plugin libraries then load them from their UMD bundle. This still achieves the desired effect of omitting shared libraries from the plugin bundle, because ng-packagr does that by default.

I still have some questions to address, such as the potential need to cache the plugin modules the Angular Compiler creates in the plugin loading service, and hopefully a decent way to manage and enforce the libraries that must be shared between the host app and plugins.

Thanks again for your time.

@GEP-JOHNGOUDA-zz
Copy link

Hello @restjohn I didn't have enough time to see the entire code but here are some notes:

  1. The first thing I've done in my implementation was to get rid of SystemJS from the implementation and rely on WebPack runtime to load modules based on module loader metadata. You can achieve that by removing umd. There are couple of reason I've done so, WebPack runtime is already there, so there is no need to have extra ~12KB for module loader.
  2. As you remove the libraryTarget: 'UMD' the builder will produce the plugin with WebPack runtime, Alex took care of removing the WebPack runtime from the plugin through this code Remove Runtime
  3. Your core app, could implement a module loader atop of WebPack that sniff the runtime by injecting a self-executable dummy module to webpackJson array which will force WebPack to call your dummy module and pass webpack_require giving you an access over the WebPack runtime internals so you could require a chunk from a CDN let's say.

webpackJsonp.push([[], { '__dummy_module__': ((e, m, runtime) => { debugger; console.log(runtime); }) }, [['__dummy_module__']]])

  1. Upon having an access to WebPack runtime internals you could call runtime.e to fetch a chunk from a CDN location, or runtime('module_id') to import a module.
  2. Someone would wonder why? No dependency over unnecessary SystemJS, utilize WebPack runtime, more control over complicated use cases, e.g. multiple plugins depends on moment.js or lodash, it make sense to put those vendors on plugin and make your plugin depends on vendor plugin reducing the plugin size. Or another edge case where one of your vendor library let's rely on moment.js and you need to drop moment.js from your plugin, since it's vendor library and you don't have control over it's source code, builder make sense here not UMD chunks loaded through SystemJS.
  3. The demo you have put together is valid, the plugin is being built as an NPM package so you publish it to NPM and other plugins consume it's typing and during build they would get dropped through WebPack externals.
  4. I guess the UMD solution wouldn't scale up in edge cases such as vendors scenarios illustrated above.
  5. If you enable Ivy, it make sense to remove the dynamic exports since it's no longer required post IVY Dynamic Exports

Hope my explanation is of help to you.

@restjohn
Copy link
Author

Thanks for taking the time to look and for your detailed response!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants