From 265ec18b5f935be200b31843e1eadb6b0ef78aa4 Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Thu, 1 Jun 2023 15:37:29 -0400 Subject: [PATCH] chore(webpack): split mirage js chunk --- package.json | 1 - src/app/Shared/Services/Api.service.tsx | 12 +---- .../Services/AuthCredentials.service.tsx | 12 +---- src/app/Shared/Services/Login.service.tsx | 11 +---- .../Services/NotificationChannel.service.tsx | 12 +---- src/app/Shared/Services/Report.service.tsx | 11 +---- src/app/Shared/Services/Settings.service.tsx | 26 +++++------ src/app/Shared/Services/Target.service.tsx | 11 +---- src/app/Shared/Services/Targets.service.tsx | 16 ++----- src/app/Shared/Services/typings.ts | 45 ------------------- src/app/index.tsx | 13 ------ src/index.tsx | 11 +---- src/mirage/{createServer.ts => index.ts} | 2 +- tsconfig.json | 1 - webpack.common.js | 10 ++--- webpack.dev.js | 1 - webpack.prod.js | 1 - 17 files changed, 29 insertions(+), 167 deletions(-) delete mode 100644 src/app/Shared/Services/typings.ts rename src/mirage/{createServer.ts => index.ts} (99%) diff --git a/package.json b/package.json index 8ddab179e6..dbdc863d9f 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,6 @@ "repository": "https://github.com/cryostatio/cryostat-web.git", "license": "UPL", "private": true, - "sideEffects": false, "scripts": { "build": "npm-run-all -l build:notests test", "build:notests": "webpack --config webpack.prod.js", diff --git a/src/app/Shared/Services/Api.service.tsx b/src/app/Shared/Services/Api.service.tsx index 4ded71d6f7..ad67eacdb6 100644 --- a/src/app/Shared/Services/Api.service.tsx +++ b/src/app/Shared/Services/Api.service.tsx @@ -41,7 +41,6 @@ import { EventType } from '@app/Events/EventTypes'; import { Notifications } from '@app/Notifications/Notifications'; import { RecordingLabel } from '@app/RecordingMetadata/RecordingLabel'; import { Rule } from '@app/Rules/Rules'; -import { Service } from '@app/Shared/Services/typings'; import { EnvironmentNode } from '@app/Topology/typings'; import { createBlobURL, jvmIdToSubdirectoryName } from '@app/utils/utils'; import { ValidatedOptions } from '@patternfly/react-core'; @@ -91,24 +90,17 @@ export const isHttpOk = (statusCode: number) => { return statusCode >= 200 && statusCode < 300; }; -export class ApiService implements Service { +export class ApiService { private readonly archiveEnabled = new ReplaySubject(1); private readonly cryostatVersionSubject = new ReplaySubject(1); private readonly grafanaDatasourceUrlSubject = new ReplaySubject(1); private readonly grafanaDashboardUrlSubject = new ReplaySubject(1); - initialized = false; constructor( private readonly target: TargetService, private readonly notifications: Notifications, private readonly login: LoginService - ) {} - - init() { - if (this.initialized) { - return; - } - this.initialized = true; + ) { // show recording archives when recordings available this.login .getSessionState() diff --git a/src/app/Shared/Services/AuthCredentials.service.tsx b/src/app/Shared/Services/AuthCredentials.service.tsx index 2a407f530a..e207d24975 100644 --- a/src/app/Shared/Services/AuthCredentials.service.tsx +++ b/src/app/Shared/Services/AuthCredentials.service.tsx @@ -39,27 +39,17 @@ import { Locations } from '@app/Settings/CredentialsStorage'; import { getFromLocalStorage } from '@app/utils/LocalStorage'; import { Observable, of } from 'rxjs'; import { ApiService } from './Api.service'; -import { Service } from './typings'; export interface Credential { username: string; password: string; } -export class AuthCredentials implements Service { +export class AuthCredentials { // TODO replace with Redux? private readonly store = new Map(); - initialized = false; - constructor(private readonly api: () => ApiService) {} - init() { - if (this.initialized) { - return; - } - this.initialized = true; - } - setCredential(targetId: string, username: string, password: string): Observable { const location = getFromLocalStorage('CREDENTIAL_LOCATION', Locations.BACKEND.key); switch (location) { diff --git a/src/app/Shared/Services/Login.service.tsx b/src/app/Shared/Services/Login.service.tsx index 4ff4ea53db..d162c16a3a 100644 --- a/src/app/Shared/Services/Login.service.tsx +++ b/src/app/Shared/Services/Login.service.tsx @@ -44,7 +44,6 @@ import { Credential, AuthCredentials } from './AuthCredentials.service'; import { isQuotaExceededError } from './Report.service'; import { SettingsService } from './Settings.service'; import { TargetService } from './Target.service'; -import { Service } from './typings'; export enum SessionState { NO_USER_SESSION, @@ -59,7 +58,7 @@ export enum AuthMethod { UNKNOWN = '', } -export class LoginService implements Service { +export class LoginService { private readonly TOKEN_KEY: string = 'token'; private readonly USER_KEY: string = 'user'; private readonly AUTH_METHOD_KEY: string = 'auth_method'; @@ -69,7 +68,6 @@ export class LoginService implements Service { private readonly username = new ReplaySubject(1); private readonly sessionState = new ReplaySubject(1); readonly authority: string; - initialized = false; constructor( private readonly target: TargetService, @@ -77,13 +75,6 @@ export class LoginService implements Service { private readonly settings: SettingsService ) { this.authority = process.env.CRYOSTAT_AUTHORITY || ''; - } - - init() { - if (this.initialized) { - return; - } - this.initialized = true; this.token.next(this.getCacheItem(this.TOKEN_KEY)); this.username.next(this.getCacheItem(this.USER_KEY)); this.authMethod.next(this.getCacheItem(this.AUTH_METHOD_KEY) as AuthMethod); diff --git a/src/app/Shared/Services/NotificationChannel.service.tsx b/src/app/Shared/Services/NotificationChannel.service.tsx index d95015502d..be6925318d 100644 --- a/src/app/Shared/Services/NotificationChannel.service.tsx +++ b/src/app/Shared/Services/NotificationChannel.service.tsx @@ -45,7 +45,6 @@ import { webSocket, WebSocketSubject } from 'rxjs/webSocket'; import { AuthMethod, LoginService, SessionState } from './Login.service'; import { Target } from './Target.service'; import { TargetDiscoveryEvent } from './Targets.service'; -import { Service } from './typings'; export enum NotificationCategory { WsClientActivity = 'WsClientActivity', @@ -323,19 +322,12 @@ interface NotificationMessageMapper { hidden?: boolean; } -export class NotificationChannel implements Service { +export class NotificationChannel { private ws: WebSocketSubject | null = null; private readonly _messages = new Subject(); private readonly _ready = new BehaviorSubject({ ready: false }); - initialized = false; - constructor(private readonly notifications: Notifications, private readonly login: LoginService) {} - - init() { - if (this.initialized) { - return; - } - this.initialized = true; + constructor(private readonly notifications: Notifications, private readonly login: LoginService) { messageKeys.forEach((value, key) => { if (!value || !value.body || !value.variant) { return; diff --git a/src/app/Shared/Services/Report.service.tsx b/src/app/Shared/Services/Report.service.tsx index 2342a0e0c2..964cec4cef 100644 --- a/src/app/Shared/Services/Report.service.tsx +++ b/src/app/Shared/Services/Report.service.tsx @@ -42,19 +42,10 @@ import { fromFetch } from 'rxjs/fetch'; import { concatMap, first, tap } from 'rxjs/operators'; import { isActiveRecording, RecordingState, Recording } from './Api.service'; import { LoginService } from './Login.service'; -import { Service } from './typings'; -export class ReportService implements Service { - initialized = false; +export class ReportService { constructor(private login: LoginService, private notifications: Notifications) {} - init() { - if (this.initialized) { - return; - } - this.initialized = true; - } - report(recording: Recording): Observable { if (!recording.reportUrl) { return throwError(() => new Error('No recording report URL')); diff --git a/src/app/Shared/Services/Settings.service.tsx b/src/app/Shared/Services/Settings.service.tsx index d8969a6948..66582aa4cc 100644 --- a/src/app/Shared/Services/Settings.service.tsx +++ b/src/app/Shared/Services/Settings.service.tsx @@ -50,7 +50,6 @@ import { RecordingAttributes, } from './Api.service'; import { NotificationCategory } from './NotificationChannel.service'; -import { Service } from './typings'; export enum FeatureLevel { DEVELOPMENT = 0, @@ -78,8 +77,16 @@ export const automatedAnalysisConfigToRecordingAttributes = ( }, } as RecordingAttributes; }; -export class SettingsService implements Service { - initialized = false; +export class SettingsService { + constructor() { + this._featureLevel$.subscribe((featureLevel: FeatureLevel) => saveToLocalStorage('FEATURE_LEVEL', featureLevel)); + this._visibleNotificationsCount$.subscribe((count: number) => + saveToLocalStorage('VISIBLE_NOTIFICATIONS_COUNT', count) + ); + this._datetimeFormat$.subscribe((format: DatetimeFormat) => saveToLocalStorage('DATETIME_FORMAT', format)); + this._theme$.subscribe((theme: ThemeSetting) => saveToLocalStorage('THEME', theme)); + } + private readonly _featureLevel$ = new BehaviorSubject( getFromLocalStorage('FEATURE_LEVEL', FeatureLevel.PRODUCTION) ); @@ -94,19 +101,6 @@ export class SettingsService implements Service { private readonly _theme$ = new BehaviorSubject(getFromLocalStorage('THEME', ThemeSetting.AUTO)); - init() { - if (this.initialized) { - return; - } - this.initialized = true; - this._featureLevel$.subscribe((featureLevel: FeatureLevel) => saveToLocalStorage('FEATURE_LEVEL', featureLevel)); - this._visibleNotificationsCount$.subscribe((count: number) => - saveToLocalStorage('VISIBLE_NOTIFICATIONS_COUNT', count) - ); - this._datetimeFormat$.subscribe((format: DatetimeFormat) => saveToLocalStorage('DATETIME_FORMAT', format)); - this._theme$.subscribe((theme: ThemeSetting) => saveToLocalStorage('THEME', theme)); - } - media(query: string): Observable { const mediaQuery = window.matchMedia(query); return fromEvent(mediaQuery, 'change').pipe(startWith(mediaQuery)); diff --git a/src/app/Shared/Services/Target.service.tsx b/src/app/Shared/Services/Target.service.tsx index 290111a5fa..cdccf322a5 100644 --- a/src/app/Shared/Services/Target.service.tsx +++ b/src/app/Shared/Services/Target.service.tsx @@ -36,7 +36,6 @@ * SOFTWARE. */ import { Observable, Subject, BehaviorSubject } from 'rxjs'; -import { Service } from './typings'; export const NO_TARGET = {} as Target; @@ -74,19 +73,11 @@ export interface Target { }; } -class TargetService implements Service { +class TargetService { private readonly _target: Subject = new BehaviorSubject(NO_TARGET); private readonly _authFailure: Subject = new Subject(); private readonly _authRetry: Subject = new Subject(); private readonly _sslFailure: Subject = new Subject(); - initialized = false; - - init() { - if (this.initialized) { - return; - } - this.initialized = true; - } setTarget(target: Target): void { if (target === NO_TARGET || !!target.connectUrl) { diff --git a/src/app/Shared/Services/Targets.service.tsx b/src/app/Shared/Services/Targets.service.tsx index 30c3fa43af..7ccd624e7f 100644 --- a/src/app/Shared/Services/Targets.service.tsx +++ b/src/app/Shared/Services/Targets.service.tsx @@ -44,30 +44,22 @@ import { ApiService } from './Api.service'; import { LoginService, SessionState } from './Login.service'; import { NotificationCategory, NotificationChannel } from './NotificationChannel.service'; import { Target } from './Target.service'; -import { Service } from './typings'; export interface TargetDiscoveryEvent { kind: 'LOST' | 'FOUND' | 'MODIFIED'; serviceRef: Target; } -export class TargetsService implements Service { +export class TargetsService { private readonly _targets$: BehaviorSubject = new BehaviorSubject([] as Target[]); - initialized = false; constructor( private readonly api: ApiService, private readonly notifications: Notifications, - private readonly login: LoginService, + login: LoginService, private readonly notificationChannel: NotificationChannel - ) {} - - init() { - if (this.initialized) { - return; - } - this.initialized = true; - this.login + ) { + login .getSessionState() .pipe(concatMap((sessionState) => (sessionState === SessionState.USER_SESSION ? this.queryForTargets() : EMPTY))) .subscribe(() => { diff --git a/src/app/Shared/Services/typings.ts b/src/app/Shared/Services/typings.ts deleted file mode 100644 index 940301258e..0000000000 --- a/src/app/Shared/Services/typings.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright The Cryostat Authors - * - * The Universal Permissive License (UPL), Version 1.0 - * - * Subject to the condition set forth below, permission is hereby granted to any - * person obtaining a copy of this software, associated documentation and/or data - * (collectively the "Software"), free of charge and under any and all copyright - * rights in the Software, and any and all patent rights owned or freely - * licensable by each licensor hereunder covering either (i) the unmodified - * Software as contributed to or provided by such licensor, or (ii) the Larger - * Works (as defined below), to deal in both - * - * (a) the Software, and - * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - * one is included with the Software (each a "Larger Work" to which the Software - * is contributed by such licensors), - * - * without restriction, including without limitation the rights to copy, create - * derivative works of, display, perform, and distribute the Software and make, - * use, sell, offer for sale, import, export, have made, and have sold the - * Software and the Larger Work(s), and to sublicense the foregoing rights on - * either these or other terms. - * - * This license is subject to the following condition: - * The above copyright notice and either this complete permission notice or at - * a minimum a reference to the UPL must be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -export interface Service { - init: () => void; - initialized: boolean; -} - -export const isService = (obj: { init?: unknown; initialized?: boolean }) => { - return obj.init && typeof obj.init === 'function' && typeof obj.initialized === 'boolean'; -}; diff --git a/src/app/index.tsx b/src/app/index.tsx index c819241388..fe389e72f5 100644 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -51,8 +51,6 @@ import * as React from 'react'; import { Provider } from 'react-redux'; import { BrowserRouter as Router } from 'react-router-dom'; import { JoyrideProvider } from './Joyride/JoyrideProvider'; -import { isService } from './Shared/Services/typings'; -import { fakeServices } from './utils/fakeData'; export const App: React.FC = () => ( @@ -69,14 +67,3 @@ export const App: React.FC = () => ( ); - -// Initialize services -// Services will be initialized once (i.e. duplicate initialization is ignored) -export const initilizeServices = () => { - Object.values(defaultServices) - .filter(isService) - .map((s) => s.init()); - Object.values(fakeServices) - .filter(isService) - .map((s) => s.init()); -}; diff --git a/src/index.tsx b/src/index.tsx index b648d1df37..86c2f3c2f7 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -36,8 +36,7 @@ * SOFTWARE. */ -import { App, initilizeServices } from '@app/index'; -import startMirage from '@mirage/createServer'; +import { App } from '@app/index'; import React from 'react'; import ReactDOM from 'react-dom'; @@ -55,12 +54,4 @@ if (process.env.NODE_ENV !== 'production') { axe(React, ReactDOM, 1000, config); } -if (process.env.PREVIEW === 'true') { - // Start Mirage - startMirage(); -} - -// Initialize services -initilizeServices(); - ReactDOM.render(, document.getElementById('root') as HTMLElement); diff --git a/src/mirage/createServer.ts b/src/mirage/index.ts similarity index 99% rename from src/mirage/createServer.ts rename to src/mirage/index.ts index f3ef916e59..bd905c52fb 100644 --- a/src/mirage/createServer.ts +++ b/src/mirage/index.ts @@ -643,4 +643,4 @@ export const startMirage = ({ environment = 'development' } = {}) => { }); }; -export default startMirage; +startMirage({ environment: process.env.NODE_ENV }); diff --git a/tsconfig.json b/tsconfig.json index 0d01f45cbc..7fd31a00dd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,7 +21,6 @@ "resolveJsonModule": true, "paths": { "@app/*": ["src/app/*"], - "@mirage/*": ["src/mirage/*"], "@test/*": ["src/test/*"], "@i18n/*": ["src/i18n/*"], "@assets/*": ["node_modules/@patternfly/react-core/dist/styles/assets/*"] diff --git a/webpack.common.js b/webpack.common.js index 0dae94c541..37d95ac2cf 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -9,7 +9,11 @@ module.exports = (env) => { return { context: __dirname, entry: { - app: path.resolve(__dirname, 'src', 'index.tsx') + app: { + import: path.resolve(__dirname, 'src', 'index.tsx'), + dependOn: process.env.PREVIEW? 'mirage': undefined + }, + ...(process.env.PREVIEW? {mirage: path.resolve(__dirname, 'src', 'mirage', 'index.ts')}: {}) }, plugins: [ new HtmlWebpackPlugin({ @@ -62,10 +66,6 @@ module.exports = (env) => { } ] }, - { - test: /i18n\/\w*\.ts$/, - sideEffects: true, - }, { test: /\.(svg|ttf|eot|woff|woff2)$/, // only process modules with this loader diff --git a/webpack.dev.js b/webpack.dev.js index 4d1da2b1f1..988571c022 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -28,7 +28,6 @@ module.exports = merge(common('development'), { rules: [ { test: /\.css$/, - sideEffects: true, include: [ path.resolve(__dirname, 'src'), path.resolve(__dirname, 'node_modules/patternfly'), diff --git a/webpack.prod.js b/webpack.prod.js index b26e724871..eb79f1229e 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -50,7 +50,6 @@ module.exports = merge(common('production'), { rules: [ { test: /\.css$/, - sideEffects: true, include: [ path.resolve(__dirname, 'src'), path.resolve(__dirname, 'node_modules/patternfly'),