Commit ddb3ee61e74c52bda6eef1ec5e6a8f0e2cf88f65

Authored by Igor Kulikov
1 parent 674512dd

UI: Optimizations - AOT + JIT

... ... @@ -22,6 +22,7 @@
22 22 "main": "src/main.ts",
23 23 "polyfills": "src/polyfills.ts",
24 24 "tsConfig": "src/tsconfig.app.json",
  25 + "aot": true,
25 26 "assets": [
26 27 "src/thingsboard.ico",
27 28 "src/assets",
... ... @@ -161,6 +162,7 @@
161 162 "serve": {
162 163 "builder": "@angular-builders/custom-webpack:dev-server",
163 164 "options": {
  165 + "aot": true,
164 166 "browserTarget": "thingsboard:build",
165 167 "proxyConfig": "proxy.conf.js"
166 168 },
... ...
... ... @@ -14,8 +14,10 @@
14 14 * limitations under the License.
15 15 */
16 16 const CompressionPlugin = require("compression-webpack-plugin");
  17 +const TerserPlugin = require("terser-webpack-plugin");
17 18 const webpack = require("webpack");
18 19 const dirTree = require("directory-tree");
  20 +const AngularCompilerPlugin = require('@ngtools/webpack');
19 21
20 22 var langs = [];
21 23
... ... @@ -25,12 +27,14 @@ dirTree("./src/assets/locale/", {extensions: /\.json$/}, (item) => {
25 27 langs.push(item.name.slice(item.name.lastIndexOf("-") + 1, -5));
26 28 });
27 29
28   -module.exports = {
29   - plugins: [
  30 +module.exports = (config, options) => {
  31 + config.plugins.push(
30 32 new webpack.DefinePlugin({
31 33 TB_VERSION: JSON.stringify(require("./package.json").version),
32 34 SUPPORTED_LANGS: JSON.stringify(langs),
33   - }),
  35 + })
  36 + );
  37 + config.plugins.push(
34 38 new CompressionPlugin({
35 39 filename: "[path][base].gz[query]",
36 40 algorithm: "gzip",
... ... @@ -38,6 +42,21 @@ module.exports = {
38 42 threshold: 10240,
39 43 minRatio: 0.8,
40 44 deleteOriginalAssets: false,
41   - }),
42   - ],
  45 + })
  46 + );
  47 +
  48 + if (config.mode === 'production') {
  49 + const index = config.plugins.findIndex(p => p instanceof AngularCompilerPlugin.AngularCompilerPlugin);
  50 + const angularCompilerOptions = config.plugins[index]._options;
  51 + angularCompilerOptions.emitClassMetadata = true;
  52 + angularCompilerOptions.emitNgModuleScope = true;
  53 + config.plugins.splice(index, 1);
  54 + config.plugins.push(new AngularCompilerPlugin.AngularCompilerPlugin(angularCompilerOptions));
  55 + const terserPluginOptions = config.optimization.minimizer[1].options;
  56 + delete terserPluginOptions.terserOptions.compress.global_defs.ngJitMode;
  57 + terserPluginOptions.terserOptions.compress.side_effects = false;
  58 + config.optimization.minimizer.splice(1, 1);
  59 + config.optimization.minimizer.push(new TerserPlugin(terserPluginOptions));
  60 + }
  61 + return config;
43 62 };
... ...
... ... @@ -100,6 +100,7 @@
100 100 "@angular/cli": "^10.1.5",
101 101 "@angular/compiler-cli": "^10.1.5",
102 102 "@angular/language-service": "^10.1.5",
  103 + "@ngtools/webpack": "10.1.5",
103 104 "@types/canvas-gauges": "^2.1.2",
104 105 "@types/flot": "^0.0.31",
105 106 "@types/jasmine": "^3.5.12",
... ...
... ... @@ -446,8 +446,12 @@ export class AuthService {
446 446 const refreshTokenValid = AuthService.isTokenValid('refresh_token');
447 447 this.setUserFromJwtToken(null, null, false);
448 448 if (!refreshTokenValid) {
449   - this.refreshTokenSubject.error(new Error(this.translate.instant('access.refresh-token-expired')));
450   - this.refreshTokenSubject = null;
  449 + this.translate.get('access.refresh-token-expired').subscribe(
  450 + (translation) => {
  451 + this.refreshTokenSubject.error(new Error(translation));
  452 + this.refreshTokenSubject = null;
  453 + }
  454 + );
451 455 } else {
452 456 const refreshTokenRequest = {
453 457 refreshToken
... ...
... ... @@ -59,37 +59,40 @@ export class DynamicComponentFactoryService {
59 59 template: string,
60 60 modules?: Type<any>[]): Observable<ComponentFactory<T>> {
61 61 const dymamicComponentFactorySubject = new ReplaySubject<ComponentFactory<T>>();
62   - const comp = this.createDynamicComponent(componentType, template);
63   - let moduleImports: Type<any>[] = [CommonModule];
64   - if (modules) {
65   - moduleImports = [...moduleImports, ...modules];
66   - }
67   - // noinspection AngularInvalidImportedOrDeclaredSymbol
68   - @NgModule({
69   - declarations: [comp],
70   - imports: moduleImports
71   - })
72   - class DynamicComponentInstanceModule extends DynamicComponentModule {}
73   - try {
74   - this.compiler.compileModuleAsync(DynamicComponentInstanceModule).then(
75   - (module) => {
76   - const moduleRef = module.create(this.injector);
77   - const factory = moduleRef.componentFactoryResolver.resolveComponentFactory(comp);
78   - this.dynamicComponentModulesMap.set(factory, {
79   - moduleRef,
80   - moduleType: module.moduleType
81   - });
82   - dymamicComponentFactorySubject.next(factory);
83   - dymamicComponentFactorySubject.complete();
  62 + import('@angular/compiler').then(
  63 + () => {
  64 + const comp = this.createDynamicComponent(componentType, template);
  65 + let moduleImports: Type<any>[] = [CommonModule];
  66 + if (modules) {
  67 + moduleImports = [...moduleImports, ...modules];
84 68 }
85   - ).catch(
86   - (e) => {
  69 + // noinspection AngularInvalidImportedOrDeclaredSymbol
  70 + const dynamicComponentInstanceModule = NgModule({
  71 + declarations: [comp],
  72 + imports: moduleImports
  73 + })(class DynamicComponentInstanceModule extends DynamicComponentModule {});
  74 + try {
  75 + this.compiler.compileModuleAsync(dynamicComponentInstanceModule).then(
  76 + (module) => {
  77 + const moduleRef = module.create(this.injector);
  78 + const factory = moduleRef.componentFactoryResolver.resolveComponentFactory(comp);
  79 + this.dynamicComponentModulesMap.set(factory, {
  80 + moduleRef,
  81 + moduleType: module.moduleType
  82 + });
  83 + dymamicComponentFactorySubject.next(factory);
  84 + dymamicComponentFactorySubject.complete();
  85 + }
  86 + ).catch(
  87 + (e) => {
  88 + dymamicComponentFactorySubject.error(e);
  89 + }
  90 + );
  91 + } catch (e) {
87 92 dymamicComponentFactorySubject.error(e);
88 93 }
89   - );
90   - } catch (e) {
91   - dymamicComponentFactorySubject.error(e);
92   - }
  94 + }
  95 + );
93 96 return dymamicComponentFactorySubject.asObservable();
94 97 }
95 98
... ...
... ... @@ -78,28 +78,31 @@ export class ResourcesService {
78 78 (module) => {
79 79 const modules = this.extractNgModules(module);
80 80 if (modules.length) {
81   - const tasks: Promise<ModuleWithComponentFactories<any>>[] = [];
82   - for (const m of modules) {
83   - tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m));
84   - }
85   - forkJoin(tasks).subscribe((compiled) => {
86   - try {
87   - const componentFactories: ComponentFactory<any>[] = [];
88   - for (const c of compiled) {
89   - c.ngModuleFactory.create(this.injector);
90   - componentFactories.push(...c.componentFactories);
  81 + import('@angular/compiler').then(
  82 + () => {
  83 + const tasks: Promise<ModuleWithComponentFactories<any>>[] = [];
  84 + for (const m of modules) {
  85 + tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m));
91 86 }
92   - this.loadedFactories[url].next(componentFactories);
93   - this.loadedFactories[url].complete();
94   - } catch (e) {
95   - this.loadedFactories[url].error(new Error(`Unable to init module from url: ${url}`));
96   - delete this.loadedFactories[url];
97   - }
98   - },
99   - (e) => {
100   - this.loadedFactories[url].error(new Error(`Unable to compile module from url: ${url}`));
101   - delete this.loadedFactories[url];
102   - });
  87 + forkJoin(tasks).subscribe((compiled) => {
  88 + try {
  89 + const componentFactories: ComponentFactory<any>[] = [];
  90 + for (const c of compiled) {
  91 + c.ngModuleFactory.create(this.injector);
  92 + componentFactories.push(...c.componentFactories);
  93 + }
  94 + this.loadedFactories[url].next(componentFactories);
  95 + this.loadedFactories[url].complete();
  96 + } catch (e) {
  97 + this.loadedFactories[url].error(new Error(`Unable to init module from url: ${url}`));
  98 + delete this.loadedFactories[url];
  99 + }
  100 + },
  101 + (e) => {
  102 + this.loadedFactories[url].error(new Error(`Unable to compile module from url: ${url}`));
  103 + delete this.loadedFactories[url];
  104 + }); }
  105 + );
103 106 } else {
104 107 this.loadedFactories[url].error(new Error(`Module '${url}' doesn't have default export!`));
105 108 delete this.loadedFactories[url];
... ... @@ -133,26 +136,30 @@ export class ResourcesService {
133 136 } catch (e) {
134 137 }
135 138 if (modules && modules.length) {
136   - const tasks: Promise<ModuleWithComponentFactories<any>>[] = [];
137   - for (const m of modules) {
138   - tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m));
139   - }
140   - forkJoin(tasks).subscribe((compiled) => {
141   - try {
142   - for (const c of compiled) {
143   - c.ngModuleFactory.create(this.injector);
144   - }
145   - this.loadedModules[url].next(modules);
146   - this.loadedModules[url].complete();
147   - } catch (e) {
148   - this.loadedModules[url].error(new Error(`Unable to init module from url: ${url}`));
149   - delete this.loadedModules[url];
  139 + import('@angular/compiler').then(
  140 + () => {
  141 + const tasks: Promise<ModuleWithComponentFactories<any>>[] = [];
  142 + for (const m of modules) {
  143 + tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m));
150 144 }
151   - },
152   - (e) => {
153   - this.loadedModules[url].error(new Error(`Unable to compile module from url: ${url}`));
154   - delete this.loadedModules[url];
155   - });
  145 + forkJoin(tasks).subscribe((compiled) => {
  146 + try {
  147 + for (const c of compiled) {
  148 + c.ngModuleFactory.create(this.injector);
  149 + }
  150 + this.loadedModules[url].next(modules);
  151 + this.loadedModules[url].complete();
  152 + } catch (e) {
  153 + this.loadedModules[url].error(new Error(`Unable to init module from url: ${url}`));
  154 + delete this.loadedModules[url];
  155 + }
  156 + },
  157 + (e) => {
  158 + this.loadedModules[url].error(new Error(`Unable to compile module from url: ${url}`));
  159 + delete this.loadedModules[url];
  160 + });
  161 + }
  162 + );
156 163 } else {
157 164 this.loadedModules[url].error(new Error(`Module '${url}' doesn't have default export or not NgModule!`));
158 165 delete this.loadedModules[url];
... ...
... ... @@ -77,7 +77,7 @@ export class ComplexFilterPredicateComponent implements ControlValueAccessor, On
77 77 this.complexFilterPredicate = predicate;
78 78 }
79 79
80   - private openComplexFilterDialog() {
  80 + public openComplexFilterDialog() {
81 81 this.dialog.open<ComplexFilterPredicateDialogComponent, ComplexFilterPredicateDialogData,
82 82 ComplexFilterPredicateInfo>(ComplexFilterPredicateDialogComponent, {
83 83 disableClose: true,
... ...
... ... @@ -59,7 +59,7 @@ export class FilterTextComponent implements ControlValueAccessor, OnInit {
59 59
60 60 requiredClass = false;
61 61
62   - private filterText: string;
  62 + public filterText: string;
63 63
64 64 private propagateChange = (v: any) => { };
65 65
... ...
... ... @@ -38,7 +38,7 @@ export interface AlarmRuleConditionDialogData {
38 38 selector: 'tb-alarm-rule-condition-dialog',
39 39 templateUrl: './alarm-rule-condition-dialog.component.html',
40 40 providers: [{provide: ErrorStateMatcher, useExisting: AlarmRuleConditionDialogComponent}],
41   - styleUrls: ['/alarm-rule-condition-dialog.component.scss']
  41 + styleUrls: ['./alarm-rule-condition-dialog.component.scss']
42 42 })
43 43 export class AlarmRuleConditionDialogComponent extends DialogComponent<AlarmRuleConditionDialogComponent, AlarmCondition>
44 44 implements OnInit, ErrorStateMatcher {
... ...
... ... @@ -15,7 +15,7 @@
15 15 ///
16 16
17 17 import { MatDialogRef } from '@angular/material/dialog';
18   -import { Inject, InjectionToken } from '@angular/core';
  18 +import { Directive, Inject, InjectionToken } from '@angular/core';
19 19 import { Store } from '@ngrx/store';
20 20 import { AppState } from '@core/core.state';
21 21 import { Router } from '@angular/router';
... ... @@ -30,6 +30,7 @@ export interface CustomDialogData {
30 30 [key: string]: any;
31 31 }
32 32
  33 +@Directive()
33 34 export class CustomDialogComponent extends PageComponent {
34 35
35 36 [key: string]: any;
... ...
... ... @@ -154,7 +154,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
154 154 private alarmsTitlePattern: string;
155 155
156 156 private displayDetails = true;
157   - private allowAcknowledgment = true;
  157 + public allowAcknowledgment = true;
158 158 private allowClear = true;
159 159
160 160 private defaultPageSize = 10;
... ...
... ... @@ -186,11 +186,16 @@ export type TripAnimationSettings = {
186 186 usePointAsAnchor: boolean;
187 187 normalizationStep: number;
188 188 showPolygon: boolean;
  189 + showLabel: boolean;
  190 + showTooltip: boolean;
189 191 latKeyName: string;
190 192 lngKeyName: string;
191 193 rotationAngle: number;
192 194 label: string;
193 195 tooltipPattern: string;
  196 + tooltipColor: string;
  197 + tooltipOpacity: number;
  198 + tooltipFontColor: string;
194 199 useTooltipFunction: boolean;
195 200 useLabelFunction: boolean;
196 201 pointAsAnchorFunction: GenericFunction;
... ...
... ... @@ -102,11 +102,11 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
102 102 ctx: WidgetContext;
103 103
104 104 private formResize$: ResizeObserver;
105   - private settings: MultipleInputWidgetSettings;
  105 + public settings: MultipleInputWidgetSettings;
106 106 private widgetConfig: WidgetConfig;
107 107 private subscription: IWidgetSubscription;
108 108 private datasources: Array<Datasource>;
109   - private sources: Array<MultipleInputWidgetSource> = [];
  109 + public sources: Array<MultipleInputWidgetSource> = [];
110 110
111 111 isVerticalAlignment: boolean;
112 112 inputWidthSettings: string;
... ...
... ... @@ -126,7 +126,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
126 126 private defaultPageSize = 10;
127 127 private defaultSortOrder = '-0';
128 128 private hideEmptyLines = false;
129   - private showTimestamp = true;
  129 + public showTimestamp = true;
130 130 private dateFormatFilter: string;
131 131
132 132 private searchAction: WidgetAction = {
... ...
... ... @@ -36,12 +36,14 @@ import { JsonFormProps } from './react/json-form.models';
36 36 import inspector from 'schema-inspector';
37 37 import * as tinycolor_ from 'tinycolor2';
38 38 import { DialogService } from '@app/core/services/dialog.service';
39   -import * as React from 'react';
40   -import * as ReactDOM from 'react-dom';
41   -import ReactSchemaForm from './react/json-form-react';
  39 +// import * as React from 'react';
  40 +// import * as ReactDOM from 'react-dom';
  41 +// import ReactSchemaForm from './react/json-form-react';
42 42 import JsonFormUtils from './react/json-form-utils';
43 43 import { JsonFormComponentData } from './json-form-component.models';
44 44 import { GroupInfo } from '@shared/models/widget.models';
  45 +import { Observable } from 'rxjs/internal/Observable';
  46 +import { forkJoin, from } from 'rxjs';
45 47
46 48 const tinycolor = tinycolor_;
47 49
... ... @@ -252,11 +254,35 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato
252 254 if (destroy) {
253 255 this.destroyReactSchemaForm();
254 256 }
255   - ReactDOM.render(React.createElement(ReactSchemaForm, this.formProps), this.reactRootElmRef.nativeElement);
  257 +
  258 + // import ReactSchemaForm from './react/json-form-react';
  259 + const reactSchemaFormObservables: Observable<any>[] = [];
  260 + reactSchemaFormObservables.push(from(import('react')));
  261 + reactSchemaFormObservables.push(from(import('react-dom')));
  262 + reactSchemaFormObservables.push(from(import('./react/json-form-react')));
  263 + forkJoin(reactSchemaFormObservables).subscribe(
  264 + (modules) => {
  265 + const react = modules[0];
  266 + const reactDom = modules[1];
  267 + const jsonFormReact = modules[2].default;
  268 + reactDom.render(react.createElement(jsonFormReact, this.formProps), this.reactRootElmRef.nativeElement);
  269 + }
  270 + );
  271 + /* import('./react/json-form-react').then(
  272 + (mod) => {
  273 + ReactDOM.render(React.createElement(mod.default, this.formProps), this.reactRootElmRef.nativeElement);
  274 + }
  275 + );*/
  276 + // ReactDOM.render(React.createElement(ReactSchemaForm, this.formProps), this.reactRootElmRef.nativeElement);
256 277 }
257 278
258 279 private destroyReactSchemaForm() {
259   - ReactDOM.unmountComponentAtNode(this.reactRootElmRef.nativeElement);
  280 + import('react-dom').then(
  281 + (reactDom) => {
  282 + reactDom.unmountComponentAtNode(this.reactRootElmRef.nativeElement);
  283 + }
  284 + );
  285 + // ReactDOM.unmountComponentAtNode(this.reactRootElmRef.nativeElement);
260 286 }
261 287
262 288 private validateModel(): boolean {
... ...
... ... @@ -16,7 +16,7 @@
16 16
17 17 import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
18 18 import { coerceBooleanProperty } from '@angular/cdk/coercion';
19   -import Raphael, { RaphaelElement, RaphaelPaper, RaphaelSet } from 'raphael';
  19 +import { RaphaelElement, RaphaelPaper, RaphaelSet } from 'raphael';
20 20 import * as tinycolor_ from 'tinycolor2';
21 21
22 22 const tinycolor = tinycolor_;
... ... @@ -90,10 +90,14 @@ export class LedLightComponent implements OnInit, AfterViewInit, OnChanges {
90 90 if (this.paper) {
91 91 this.paper.remove();
92 92 }
93   - this.paper = Raphael($('#canvas_container', this.elementRef.nativeElement)[0], this.canvasSize, this.canvasSize);
94   - const center = this.canvasSize / 2;
95   - this.circleElement = this.paper.circle(center, center, this.radius);
96   - this.draw();
  93 + import('raphael').then(
  94 + (raphael) => {
  95 + this.paper = raphael.default($('#canvas_container', this.elementRef.nativeElement)[0], this.canvasSize, this.canvasSize);
  96 + const center = this.canvasSize / 2;
  97 + this.circleElement = this.paper.circle(center, center, this.radius);
  98 + this.draw();
  99 + }
  100 + );
97 101 }
98 102
99 103 private draw() {
... ...