This SDK provides the tools needed to support Angular Module Federation development within Unily. It includes a schematics package that automatically sets up the scaffolding for a new micro frontend module build, it generates the essential files and configurations needed to make Angular Module Federation facilities available within the set workspace.
This project requires NodeJS and NPM to be installed. To make sure you have them available on your machine, try running the following commands:
npm -v
node -v
To run the schematics collection, first you'll need to install the following Angular development packages globally on your machine so they will provide you with the schematics command line tools:
npm install -g @angular-devkit/schematics
npm install -g @angular-devkit/schematics-cli
Next, install this SDK globally on your machine:
npm install -g @unily/angular-sdk
Target the workspace folder you wish to generate the micro frontend module in, then run the new-mfe
schematic with the following command:
cd {path}
schematics @unily/angular-sdk:new-mfe
This will prompt you to input some configuration settings for the new project:
- Project name - This will set the workspace directory name accordingly
- Angular directive selector prefix - This will enforce a linting rule across the project requiring the set value as a prefix for the 'selector' property
- Webpack server port number - This will set the port number for the module to run on
After running the schematic, the workspace will be populated with the associated file structure. Package dependencies will also be downloaded and installed automatically to prepare the newly generated module for development.
Environment configuration details can be fetched from the CMS.
To start a module on your local environment, run the following command:
Note - This will serve the module on the configured port number.
npm run start
To deploy a module, run the following commands:
npm run build
npm run deploy
This will prompt you to choose from different deployment options:
- Lazy Loaded - Choose whether the module should be lazily or eagerly loaded
- Starts enabled - Choose whether the module should be immediately enabled after being deployed
The module will then be added to the 'Application Federated Modules' page in the CMS once it's been successfully deployed.
Alternative to deploying manually, deploying via CI/CD pipelines can be achieved by calling the deploy-ci
script with
the required parameters.
To do so, update your pipeline to run the following command.
Note that the parameters passed to the npm script need to be passed to a node script hence the use of -- is very important
npm run deploy-ci -- -h https://myexamplehost-api.unily.com -c myClientId -s myClientSecret
The deploy-ci
script accepts the following options. Each option requires a value assigned to it. Options can be used
in the short or long version. Both will have the same result.
- -h, --host - The domain of your api gateway
- -c, --clientId - Your client ID. Contact customer support to provide you one.
- -s, --secret - Your secret key. Contact customer support to provide you one.
- -l, --lazy - Accepts a boolean value. True or false. Defaults to true if unspecified. If true, the module will be configured as lazy loaded. If false, it would be loaded eagerly.
- -e, --enabled - Accepts a boolean value. True or false. Defaults to true if unspecified. If true, the module will be enabled, otherwise it will not and will never be loaded.
- Deploying a module that is enabled, and eagerly loaded
npm run deploy-ci -- -h https://myexamplehost-api.unily.com -c myClientId -s myClientSecret -l false
- Deploying a module that is lazy loaded but not enabled
npm run deploy-ci -- -h https://myexamplehost-api.unily.com -c myClientId -s myClientSecret -e false
- Deploying a module that is eagerly loaded but not enabled
npm run deploy-ci -- --host https://myexamplehost-api.unily.com --clientId myClientId --secret myClientSecret --lazy false --enabled false
The public surface of a newly generated module is defined by what we expose in webpack.config.js
file.
Anything that needs to be exposed publicly by the module needs to be defined in public.module.metadata.ts
file:
Note: If you do not wish to export a component and prefer to keep it as internal, you just need to exclude it from the exports
array.
...
export const metadata = {
imports: [
BrowserModule
],
declarations: [
ButtonComponent,
],
exports: [
// On bootstrap, custom HTML elements are created for the components listed here.
// These components will be immediately available when a module is imported.
ButtonComponent,
],
providers: [
// These are the services that will be publicly exposed by this module.
// The string token allows us to have a reference to the injector without requiring any reference to a specific construct inside this module.
// Specified dependencies do not need to be exported, but they need to be specified here for the services that require them.
{
provide: 'ButtonService',
useClass: ButtonService,
deps: [IncrementService]
}
],
// We specify this to allow custom elements to be used within HTML templates.
// It suppresses Angular warnings about non-existent selectors at compile time.
schemas: [CUSTOM_ELEMENTS_SCHEMA]
} as NgModule;
...
These are the capabilities of loading a remote module:
- Can be side-loaded through CMS config ✔
- Can be imported ✔
- Can be lazy loaded ✔
- Can be deployed to a blob ✔
- Requires environment redeployment on change ❌
- Can side-load modules through CMS config ❌
Note: Remote modules do not import side-loaded modules explicitly, but inherit any side-loaded module configurations. When running stand-alone, the side-loaded modules will not be available to the remote module.
A remote module can import myFederatedModules
, applicationFederatedModules
and unilyFederatedModules
modules. Each remote module is configured separately and can import other modules' type definitions when their name is added inside module-federation/mf.config.js
file.
module.exports = {
projectName: 'my-project',
dependencies: {
// Load types from the client's blob storage.
applicationFederatedModules: [
/*
'app-state',
'ui-library'
...
*/
],
// Load types from Unily's package manager.
unilyFederatedModules: [
/*
'unily-ui-library'
...
*/
],
// Load types AND remote modules from the local environment.
// This is useful for testing and development without having to side-load modules through the CMS.
myFederatedModules: [
/*
'app-state',
'counter'
...
*/
]
},
// Authentication token needed to load types for `applicationFederatedModules` and `unilyFederatedModules`
// OR to deploy the current module.
/**
* Fetch your endpoint and auth token from
* 'My Federated Modules' or 'Application Federated Modules' pages in CMS
* by clicking on 'Get PAT Token' and copying the values provided
* in the overlay.
*/
endpoint: '',
authToken: ''
}
Modules can be either lazily or eagerly loaded. Eagerly loaded modules are imported and bootstrapped before the module importing those modules is bootstrapped. This is useful when a module specifies some global state or runs an app initialisation script. Lazily loaded modules need to be manually imported wherever they are first needed using a dynamic import as follows:
...
import { RemoteNgModule, importRemote } from '@unily/remote-service';
...
// Dynamically import the module - this lazily loads the module when the importing module is bootstrapped.
// By this point, we can already use any exported components.
const uiLibrary = await importRemote<RemoteNgModule>('ui-library');
const counter = await importRemote<RemoteNgModule>('counter');
...
@NgModule({
...
providers: [
...
// add the service provider for the desired service from the imported module
uiLibrary.getProviders('ButtonService'),
...
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
...
private readonly counterService: CounterService;
constructor(
...
// inject the service using the provided injection token...
@Inject('ButtonService') private readonly buttonService: ButtonService,
...
) {
// ...or get the service directly without providing or injecting it.
this.counterService = counter.getService('CounterService');
}
...
More information about Angular Module Federation and its setup can be found in this Wiki page.