A way to build Angular plugin with AOT and standalone deploy

In this article i will show you how to build angular UMD plugin with AOT (Ahead-of-Time) support and standalone deploy. When i said standalone deploy i mean that the main app haven’t direct link to plugin library and has independent own release cycle. I tried to find some solutions and all of them use the angular compiler in production but i don’t want to include the compiler in production bundle.

This example based on standard build way angular library which angular cli support from version 6 and simple rollup command for build UMD bundle. All source code you can find there https://github.com/iwnow/angular-plugin-example 👀.

First lets start with generate main angular app and the plugin library

ng new angular-plugin-example
cd angular-plugin-example
ng generate library plugin

Now we have the main app and the plugin. We want to load the plugin in runtime but the main app should not have any knows about the plugin library, so lazy module is not our way.

To do that we should build an UMD bundle that exports our plugin with module factory and components factory (AOT), so lets build our library

ng build plugin

After build we are interesting in files in directory dist/plugin/esm2015/lib/. By default we have no .ngfactory.js files, it means we have no factories for our components. To build factories we need to edit the projects/plugin/tsconfig.lib.json and set skipTemplateCodegen to false.

"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": false,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"enableResourceInlining": true
}

Now if we run build then we get factories for our module and components.

After build factories we need to concat all factories files into single UMD bundle, for do this i will use rollup with simple command:

rollup dist/plugin/esm2015/lib/plugin.module.ngfactory.js \
--file src/assets/plugin.module.umd.js \
--format umd \
--name 'app.plugin'

This command bundling all needed files into single file and copy it to assets folder to simply load from main app. Now we have a file that consist our plugin library with export factories for angular module and components.

All we need is simply to load that file into our main app and create NgModuleRef with this command:

const pluginModuleRef = pluginModuleFactory.create(appInjector);

So how we get the reference of plugin module factory? To do that we can load the file with fetch API for example like this:

fetch('/assets/plugin.module.umd.js')
.then(response => response.text())
.then(source => {
//eval source and get plugin module factory from exports object
});

In file we can see, that our factory is export like PluginModuleNgFactory:

So the whole code that instantiate the component from plugin may look like this:

In the above code you will see how we load UMD module with angular AOT component factory and create the component in viewContainerRef of the main app. Demo available on github: https://github.com/iwnow/angular-plugin-example. Hope it helps you to find the right way!👻