281 Angular 14 Library, Lazy loading, Internationalization - chempkovsky/CS82ANGULAR GitHub Wiki
- Create Angular Repository
- Create subprojects
- Generate routing module and component for lib
- Modify generated routing module
- Add component to the app
- Modify AppComponent html
- Modify public api file
- Build the Lib
- Modify routing module of the app
- Test app
- Add Internationalization
- Modify LazycompComponent ts
- Modify ShellcompComponent ts
- Modify LazycompComponent html
- Modify ShellcompComponent html
- Try to build lib
- Modify public api file and build
- Try to serve app
- Modify polyfills file and serve app
- Modify Angular json
- extract i18n
- translate i18n
- Test app with i18n
- Reading
- We start with a file
- Create new folder
- Install babel
- Create the file for testing
- Run the first test
- Run the second test
- Run the third test
- Run the fourth test
- Run the fifth test
- Run updater js anywhere
- run windows terminal, cd the drive and folder you like
- our choice was
E:\
- our choice was
- run the command
ng new test-lib --no-create-application
cd test-lib
- we continue with the current folder
- in our case it is
e:\test-lib
- in our case it is
- run the following commands
ng generate library lazy-lib
ng generate application lazy-shell
- we continue with the current folder
- in our case it is
e:\test-lib
- in our case it is
- run the following commands
ng generate module lazymodule --routing --project lazy-lib
ng generate component lazymodule/lazycomp --project lazy-lib
- modify
projects/lazy-lib/src/lib/lazymodule/lazymodule-routing.module.ts
file as follows
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LazycompComponent } from './lazycomp/lazycomp.component';
const routes: Routes = [
{
path: '',
component: LazycompComponent,
pathMatch: 'full'
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class LazymoduleRoutingModule { }
- we continue with the current folder
- in our case it is
e:\test-lib
- in our case it is
- run the following command
ng generate component shellcomp --project lazy-shell
Modify projects/lazy-shell/src/app/app.component.html
file as follows
<p>Shell AppComponent works!</p>
<a routerLink="/">Go to ShellComponent</a><br>
<a routerLink="/lazycomponent">Go to Lazy comp</a>
<router-outlet></router-outlet>
Modify projects/lazy-lib/src/public-api.ts
file as follows
/*
* Public API Surface of lazy-lib
*/
// export * from './lib/lazy-lib.service';
// export * from './lib/lazy-lib.component';
// export * from './lib/lazy-lib.module';
export * from './lib/lazymodule/lazymodule.module';
- we continue with the current folder
- in our case it is
e:\test-lib
- in our case it is
- run the following command
ng build --project lazy-lib
Modify projects/lazy-shell/src/app/app-routing.module.ts
file as follows
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ShellcompComponent } from './shellcomp/shellcomp.component';
const routes: Routes = [
{
path: '',
component: ShellcompComponent,
pathMatch: 'full'
},
{
path: 'lazycomponent',
loadChildren: () => import('lazy-lib').then(m=>m.LazymoduleModule)
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
- we continue with the current folder
- in our case it is
e:\test-lib
- in our case it is
- run the following command
ng serve -o --project lazy-shell
- make sure the navigation is working properly !!!
- we continue with the current folder
- in our case it is
e:\test-lib
- in our case it is
- run the following command
ng add @angular/localize
- Here is a responce
E:\test-lib>ng add @angular/localize
i Using package manager: npm
√ Found compatible package version: @angular/[email protected].
√ Package information loaded.
The package @angular/[email protected] will be installed and executed.
Would you like to proceed? Yes
√ Packages successfully installed.
Option "project" is required.
Modify projects\lazy-lib\src\lib\lazymodule\lazycomp\lazycomp.component.ts
file as follows
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'lib-lazycomp',
templateUrl: './lazycomp.component.html',
styleUrls: ['./lazycomp.component.css']
})
export class LazycompComponent implements OnInit {
additionaltitle: string='';
frases: {[key:string]: string} = {
'additionaltitle': $localize`:Here is a title to translate for LazycompComponent@@LazycompComponent.title-to-translate-for-LazycompComponent:Here is a title to translate for LazycompComponent`,
};
constructor() { }
ngOnInit(): void {
this.additionaltitle = this.frases['additionaltitle'];
}
}
Modify projects\lazy-shell\src\app\shellcomp\shellcomp.component.ts
file as follows
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-shellcomp',
templateUrl: './shellcomp.component.html',
styleUrls: ['./shellcomp.component.css']
})
export class ShellcompComponent implements OnInit {
additionaltitle: string='';
frases: {[key:string]: string} = {
'additionaltitle': $localize`:Here is a title to translate for ShellcompComponent@@ShellcompComponent.title-to-translate-for-ShellcompComponent:Here is a title to translate for ShellcompComponent`,
};
constructor() { }
ngOnInit(): void {
this.additionaltitle = this.frases['additionaltitle'];
}
}
Modify projects\lazy-lib\src\lib\lazymodule\lazycomp\lazycomp.component.html
file as follows
<p>lazycomp works!</p>
<p>{{ additionaltitle }}</p>
Modify projects\lazy-shell\src\app\shellcomp\shellcomp.component.html
file as follows
<p>shellcomp works!</p>
<p>{{ additionaltitle }}</p>
- we continue with the current folder
- in our case it is
e:\test-lib
- in our case it is
- run the following command
ng build --project lazy-lib
- Here is a response
PS E:\test-lib> ng build --project lazy-lib
Building Angular Package
------------------------------------------------------------------------------
Building entry point 'lazy-lib'
------------------------------------------------------------------------------
✖ Compiling with Angular sources in Ivy partial compilation mode.
projects/lazy-lib/src/lib/lazymodule/lazycomp/lazycomp.component.ts:10:24 - error TS2304: Cannot find name '$localize'.
10 'additionaltitle': $localize`:Here is a title to translate for LazycompComponent@@LazycompComponent.title-to-translate-for-LazycompComponent:Here is a title to translate for LazycompComponent`,
~~~~~~~~~
Modify projects/lazy-lib/src/public-api.ts
file as follows
/*
* Public API Surface of lazy-lib
*/
//export * from './lib/lazy-lib.service';
//export * from './lib/lazy-lib.component';
//export * from './lib/lazy-lib.module';
import '@angular/localize/init';
export * from './lib/lazymodule/lazymodule.module';
- we continue with the current folder
- in our case it is
e:\test-lib
- in our case it is
- run the following command
ng build --project lazy-lib
- Here is a response
PS E:\test-lib> ng build --project lazy-lib
Building Angular Package
------------------------------------------------------------------------------
Building entry point 'lazy-lib'
------------------------------------------------------------------------------
✔ Compiling with Angular sources in Ivy partial compilation mode.
✔ Generating FESM2020
✔ Generating FESM2015
✔ Copying assets
✔ Writing package manifest
✔ Built lazy-lib
------------------------------------------------------------------------------
Built Angular Package
- from: E:\test-lib\projects\lazy-lib
- to: E:\test-lib\dist\lazy-lib
------------------------------------------------------------------------------
Build at: 2022-10-13T12:48:15.754Z - Time: 7490ms
- we continue with the current folder
- in our case it is
e:\test-lib
- in our case it is
- run the following command
ng serve -o --project lazy-shell
The error will be shown in the console
main.ts:11 ERROR Error: Uncaught (in promise): Error: It looks like your application or one of its dependencies is using i18n.
Angular 9 introduced a global `$localize()` function that needs to be loaded.
Please run `ng add @angular/localize` from the Angular CLI.
(For non-CLI projects, add `import '@angular/localize/init';` to your `polyfills.ts` file.
For server-side rendering applications add the import to your `main.server.ts` file.)
Error: It looks like your application or one of its dependencies is using i18n.
Angular 9 introduced a global `$localize()` function that needs to be loaded.
Please run `ng add @angular/localize` from the Angular CLI.
(For non-CLI projects, add `import '@angular/localize/init';` to your `polyfills.ts` file.
For server-side rendering applications add the import to your `main.server.ts` file.)
at _global.$localize (core.mjs:29729:15)
at new ShellcompComponent (shellcomp.component.ts:11:33)
at NodeInjectorFactory.ShellcompComponent_Factory [as factory] (shellcomp.component.ts:16:4)
...
Modify projects\lazy-shell\src\polyfills.ts
file as follows
import 'zone.js'; // Included with Angular CLI.
import '@angular/localize/init';
- we continue with the current folder
- in our case it is
e:\test-lib
- in our case it is
- run the following command
ng serve -o --project lazy-shell
- There is no error!!!
Modify angular.json
file as follows
- under projects:lazy-shell add
"i18n": {
"sourceLocale": {
"code": "en",
"baseHref": ""
},
"locales": {
"ru": {
"translation": "projects/lazy-shell/src/locale/messages.ru.xlf",
"baseHref": ""
}
}
},
- under projects:lazy-shell:architect:build:options add
"localize": true,
"aot": true,
- under projects:lazy-shell:architect:build:configurations:development add
"localize": false,
- under projects:lazy-shell:architect:build:configurations add
"ru": {
"localize": ["ru"]
},
"en": {
"localize": ["en"]
}
- under projects:lazy-shell:architect:serve:configurations add
"ru": {
"browserTarget": "lazy-shell:build:development,ru"
},
"en": {
"browserTarget": "lazy-shell:build:development,en"
}
- we continue with the current folder
- in our case it is
e:\test-lib
- in our case it is
- run the following command
ng extract-i18n --project lazy-shell --output-path projects/lazy-shell/src/locale
- Here is
projects\lazy-shell\src\locale\messages.xlf
file
<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="ng2.template">
<body>
<trans-unit id="LazycompComponent.title-to-translate-for-LazycompComponent" datatype="html">
<source>Here is a title to translate for LazycompComponent</source>
<context-group purpose="location">
<context context-type="sourcefile">projects/lazy-lib/src/lib/lazymodule/lazycomp/lazycomp.component.ts</context>
<context context-type="linenumber">10,17</context>
</context-group>
<note priority="1" from="description">Here is a title to translate for LazycompComponent</note>
</trans-unit>
<trans-unit id="ShellcompComponent.title-to-translate-for-ShellcompComponent" datatype="html">
<source>Here is a title to translate for ShellcompComponent</source>
<context-group purpose="location">
<context context-type="sourcefile">projects/lazy-shell/src/app/shellcomp/shellcomp.component.ts</context>
<context context-type="linenumber">11</context>
</context-group>
<note priority="1" from="description">Here is a title to translate for ShellcompComponent</note>
</trans-unit>
</body>
</file>
</xliff>
- For understanding: the following fragment is from the library !!!!
<trans-unit id="LazycompComponent.title-to-translate-for-LazycompComponent" datatype="html">
...
</trans-unit>
- we continue with the current folder
- in our case it is
e:\test-lib
- in our case it is
- run the following commands
npm install -g xlf-auto-translate
xlf-auto-translate -i projects/lazy-shell/src/locale/messages.xlf -o projects/lazy-shell/src/locale/messages.ru.xlf -f en -t ru
- we continue with the current folder
- in our case it is
e:\test-lib
- in our case it is
- run the following commands
ng serve -o --project lazy-shell --configuration=ru
- Everything is fine
- we do not need to make separate i18n-translation of the library
- The notation of Lazy Loading is as follows:
{
path: 'lazycomponent',
loadChildren: () => import('lazy-lib').then(m=>m.LazymoduleModule)
}
- The last requires a proper definition of
public-api.ts
for each library of the app.- In case of the custom code generators it requires the parsing of
public-api.ts
-file. :(
- In case of the custom code generators it requires the parsing of
- The typical file will be as follows
const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');
module.exports = withModuleFederationPlugin({
name: 'test_module_federation',
exposes: {
'./Themodule11': './projects/mfe1/src/app/components/md11/md11.module',
'./Themodule12': './projects/mfe1/src/app/components/md12/lazymodule.module1',
},
shared: {
...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }),
},
});
- we need to update the body of
exposes
-property
- lets create
test
- folder on the diske:
e:
mkdir test
cd test
- we continue with the current folder
- in our case it is
e:\test
- in our case it is
- run the following commands
npm install --save-dev @babel/core
- we continue with the current folder
- in our case it is
e:\test
- in our case it is
- create
E:\test\updater.js
-file with the following content
const code = ` const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack'); module.exports = withModuleFederationPlugin({ name: 'test_module_federation', exposes: { './Themodule11': './projects/mfe1/src/app/components/md11/md11.module', './Themodule12': './projects/mfe1/src/app/components/md12/lazymodule.module1', }, shared: { ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }), }, }); `; var fullPath = process.argv[2]; var lastName = ''; let indexOfLastDelimeter = fullPath.lastIndexOf('/'); if(indexOfLastDelimeter > -1) { lastName = fullPath.substring(indexOfLastDelimeter+1); } else { lastName = fullPath; } const parserInst = require('@babel/parser'); const babelInst = require("@babel/core"); const ast = parserInst.parse(code, { sourceType: "module", plugins: ["flow"] }); let isNotUpdated = true; let isExposesPresent = false; const StringLiteralVisitor = {StringLiteral(path) { if(path.node.value.endsWith('/'+ lastName) || (path.node.value === lastName)) { path.node.value = fullPath; isNotUpdated = false; } }}; const ObjectPropertyVisitor = {ObjectProperty(path) { if(path.node.value.value.endsWith('/'+ lastName) || (path.node.value.value === lastName)) { path.node.value.value = fullPath; isNotUpdated = false; } }}; const IdentifierVisitor = { Identifier(path) { if(path.node.name === "exposes") { path.parentPath.traverse(ObjectPropertyVisitor); isExposesPresent = true; } }}; babelInst.traverse(ast, IdentifierVisitor); if(isNotUpdated) { if(isExposesPresent) { babelInst.traverse(ast, { Identifier: function(path) { if(path.node.name === "exposes") { path.parentPath.node.value.properties.push(babelInst.types.objectProperty(babelInst.types.stringLiteral(fullPath),babelInst.types.stringLiteral(fullPath))); } }, }); } else { babelInst.traverse(ast, { CallExpression: function(path) { if(path.node.callee.type === 'Identifier') { if (path.node.callee.name === 'withModuleFederationPlugin') { if (path.node.arguments.count < 1) { path.node.arguments.push( babelInst.types.objectExpression([ babelInst.types.objectProperty( // key babelInst.types.identifier("exposes"), // value babelInst.types.objectExpression([babelInst.types.objectProperty(babelInst.types.stringLiteral(fullPath),babelInst.types.stringLiteral(fullPath))]) ) ]) ); } else { path.node.arguments = [ babelInst.types.objectExpression([ babelInst.types.objectProperty( // key babelInst.types.identifier("exposes"), // value babelInst.types.objectExpression([ babelInst.types.objectProperty(babelInst.types.stringLiteral(fullPath),babelInst.types.stringLiteral(fullPath)) ]) ) ]) ]; } } } } }); } } const result = babelInst.transformFromAst(ast); console.log(result.code);
- the first test modifies existing line
- we continue with the current folder
- in our case it is
e:\test
- in our case it is
- run the following commands
node updater.js "./projects/folder_for_test/lazymodule.module"
- Here is a response
PS E:\test> node updater.js "./projects/folder_for_test/lazymodule.module"
const {
shareAll,
withModuleFederationPlugin
} = require('@angular-architects/module-federation/webpack');
module.exports = withModuleFederationPlugin({
name: 'test_module_federation',
exposes: {
'./Themodule11': './projects/mfe1/src/app/components/md11/md11.module',
'./Themodule12': "./projects/folder_for_test/lazymodule.module"
},
shared: {
...shareAll({
singleton: true,
strictVersion: true,
requiredVersion: 'auto'
})
}
});
PS E:\test>
- the second test adds new line
- we continue with the current folder
- in our case it is
e:\test
- in our case it is
- run the following command
node updater.js "./projects/folder_for_test/lazymodule.module000"
- the third test adds new line into empty
exposes
- modify the
code
as follows
- modify the
const code = ` const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack'); module.exports = withModuleFederationPlugin({ name: 'test_module_federation', exposes: {}, shared: { ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }), }, }); `;
- we continue with the current folder
- in our case it is
e:\test
- in our case it is
- run the following command
node updater.js "./projects/folder_for_test/lazymodule.module000"
- the fourth test adds
exposes
-def- modify the
code
as follows
- modify the
const code = ` const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack'); module.exports = withModuleFederationPlugin({ name: 'test_module_federation', shared: { ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }), }, }); `;
- we continue with the current folder
- in our case it is
e:\test
- in our case it is
- run the following command
node updater.js "./projects/folder_for_test/lazymodule.module000"
- the fifths test modifies
{}
-empty object- modify the
code
as follows
- modify the
const code = ` const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack'); module.exports = withModuleFederationPlugin({}); `;
- run the following command
node updater.js "./projects/folder_for_test/lazymodule.module000"
- the sixth test modifies method call without params
- modify the
code
as follows
- modify the
const code = ` const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack'); module.exports = withModuleFederationPlugin(); `;
- run the following command
node updater.js "./projects/folder_for_test/lazymodule.module000"
- copy
updater.js
into new empty folder - we continue with the current folder
- in our case it is
e:\test
- in our case it is
- run the following commands
mkdir e:\test1
copy updater.js e:\test1\updater.js
cd e:\test1
node updater.js "./projects/folder_for_test/lazymodule.module000"
- here is a response
e:\test1>node updater.js "./projects/folder_for_test/lazymodule.module000"
node:internal/modules/cjs/loader:959
throw err;
^
Error: Cannot find module '@babel/parser'
Require stack:
- e:\test1\updater.js
←[90m at Function.Module._resolveFilename (node:internal/modules/cjs/loader:956:15)←[39m
←[90m at Function.Module._load (node:internal/modules/cjs/loader:804:27)←[39m
←[90m at Module.require (node:internal/modules/cjs/loader:1028:19)←[39m
←[90m at require (node:internal/modules/cjs/helpers:102:18)←[39m
at Object.<anonymous> ←[90m(e:\test1\←[39mupdater.js:21:20←[90m)←[39m
←[90m at Module._compile (node:internal/modules/cjs/loader:1126:14)←[39m
←[90m at Object.Module._extensions..js (node:internal/modules/cjs/loader:1180:10)←[39m
←[90m at Module.load (node:internal/modules/cjs/loader:1004:32)←[39m
←[90m at Function.Module._load (node:internal/modules/cjs/loader:839:12)←[39m
←[90m at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)←[39m {
code: ←[32m'MODULE_NOT_FOUND'←[39m,
requireStack: [ ←[32m'e:\\test1\\updater.js'←[39m ]
}
- Modify two lines of the
e:\test1\updater.js
as follows
const parserInst = require('e:/test/node_modules/@babel/parser');
const babelInst = require(`e:/test/node_modules/@babel/core`);
- run the command
node updater.js "./projects/folder_for_test/lazymodule.module000"
- There is no error!!!
- The typical 'public-api.ts'-file will be as follows
import '@angular/localize/init'
export * from './lib/lazymodule/lazymodule.module'
const code = ` import '@angular/localize/init' export * from './lib/lazymodule/lazymodule.module' `; var fullPath = process.argv[2]; var lastName = ''; let indexOfLastDelimeter = fullPath.lastIndexOf('/'); if(indexOfLastDelimeter > -1) { lastName = fullPath.substring(indexOfLastDelimeter+1); } else { lastName = fullPath; } const parserInst = require('@babel/parser') const babelInst = require("@babel/core"); const ast = parserInst.parse(code, { sourceType: "module", plugins: ["flow"] }); let isNotUpdated = true; let isExposesPresent = false; const StringLiteralVisitor = { StringLiteral(path) { if(path.node.value.endsWith('/'+ lastName) || (path.node.value === lastName)) { path.node.value = fullPath; isNotUpdated = false; } } }; const ExportVisitor = { ExportAllDeclaration(path) { path.traverse(StringLiteralVisitor); }, ExportNamedDeclaration(path) { path.traverse(StringLiteralVisitor); }, ExportDefaultDeclaration(path) { path.traverse(StringLiteralVisitor); }, }; babelInst.traverse(ast, ExportVisitor); const ProgramVisitor = { Program(path) { if(path.node.body) { path.node.body.push(babelInst.types.exportAllDeclaration(babelInst.types.stringLiteral(fullPath))); } else { path.node.body = [babelInst.types.exportAllDeclaration(babelInst.types.stringLiteral(fullPath))]; } } } if(isNotUpdated) { babelInst.traverse(ast, ProgramVisitor); } const result = babelInst.transformFromAst(ast); console.log(result.code);
-
the file above passes three tests
- modify existing line
- add new line
- add new line to empty file
-
here are two of the tests
PS E:\test3> node updater02.js "./projects/folder_for_test/lazymodule.module"
import '@angular/localize/init';
export * from "./projects/folder_for_test/lazymodule.module";
PS E:\test3> node updater02.js "./projects/folder_for_test/lazymodule.module1"
import '@angular/localize/init';
export * from './lib/lazymodule/lazymodule.module';
export * from "./projects/folder_for_test/lazymodule.module1";
PS E:\test3>
- the last test for
const code
as follows:
const code = ``;
- here is a result
PS E:\test3> node updater02.js "./projects/folder_for_test/lazymodule.module1"
export * from "./projects/folder_for_test/lazymodule.module1";
PS E:\test3>