diff --git a/ui/src/app/apidocs/components/apiDocs.tsx b/ui/src/app/apidocs/components/apiDocs.tsx
index 81f543a3cc71..9e6f8676b1e8 100644
--- a/ui/src/app/apidocs/components/apiDocs.tsx
+++ b/ui/src/app/apidocs/components/apiDocs.tsx
@@ -1,8 +1,7 @@
import {Page} from 'argo-ui';
import * as React from 'react';
-import SwaggerUI from 'swagger-ui-react';
-import 'swagger-ui-react/swagger-ui.css';
import {uiUrl} from '../../shared/base';
+import {Loading} from '../../shared/components/loading';
import {useCollectEvent} from '../../shared/components/use-collect-event';
export const ApiDocs = () => {
@@ -10,8 +9,22 @@ export const ApiDocs = () => {
return (
-
+
);
};
+
+// lazy load SwaggerUI as it is infrequently used and imports very large components (which can be split into a separate bundle)
+const LazySwaggerUI = React.lazy(() => {
+ import('swagger-ui-react/swagger-ui.css');
+ return import('swagger-ui-react');
+});
+
+function SuspenseSwaggerUI(props: any) {
+ return (
+ }>
+
+
+ );
+}
diff --git a/ui/src/app/cron-workflows/components/pretty-schedule.tsx b/ui/src/app/cron-workflows/components/pretty-schedule.tsx
index 9d833d6ba478..f24a46db6e77 100644
--- a/ui/src/app/cron-workflows/components/pretty-schedule.tsx
+++ b/ui/src/app/cron-workflows/components/pretty-schedule.tsx
@@ -1,7 +1,7 @@
-import React = require('react');
-import {WarningIcon} from '../../shared/components/fa-icons';
+import x from 'cronstrue';
+import * as React from 'react';
-const x = require('cronstrue');
+import {WarningIcon} from '../../shared/components/fa-icons';
/*
https://github.com/bradymholt/cRonstrue
@@ -12,7 +12,7 @@ const x = require('cronstrue');
sometime it'll not work as expected. Therefore, we must let the user know about this.
*/
-export const PrettySchedule = ({schedule}: {schedule: string}) => {
+export function PrettySchedule({schedule}: {schedule: string}) {
try {
if (schedule.split(' ').length >= 6) {
throw new Error('cron schedules must consist of 5 values only');
@@ -26,4 +26,4 @@ export const PrettySchedule = ({schedule}: {schedule: string}) => {
);
}
-};
+}
diff --git a/ui/src/app/cron-workflows/components/schedule-validator.tsx b/ui/src/app/cron-workflows/components/schedule-validator.tsx
index ebba3a631e26..2521c8671d85 100644
--- a/ui/src/app/cron-workflows/components/schedule-validator.tsx
+++ b/ui/src/app/cron-workflows/components/schedule-validator.tsx
@@ -1,9 +1,9 @@
-import React = require('react');
-import {SuccessIcon, WarningIcon} from '../../shared/components/fa-icons';
+import x from 'cronstrue';
+import * as React from 'react';
-const x = require('cronstrue');
+import {SuccessIcon, WarningIcon} from '../../shared/components/fa-icons';
-export const ScheduleValidator = ({schedule}: {schedule: string}) => {
+export function ScheduleValidator({schedule}: {schedule: string}) {
try {
if (schedule.split(' ').length >= 6) {
throw new Error('cron schedules must consist of 5 values only');
@@ -20,4 +20,4 @@ export const ScheduleValidator = ({schedule}: {schedule: string}) => {
);
}
-};
+}
diff --git a/ui/src/app/event-flow/components/event-flow-details/event-flow-page.tsx b/ui/src/app/event-flow/components/event-flow-details/event-flow-page.tsx
index c54abe189a4a..00b1e2643ee7 100644
--- a/ui/src/app/event-flow/components/event-flow-details/event-flow-page.tsx
+++ b/ui/src/app/event-flow/components/event-flow-details/event-flow-page.tsx
@@ -1,6 +1,6 @@
import {Page, SlidingPanel, Tabs} from 'argo-ui';
import {useContext, useEffect, useState} from 'react';
-import React = require('react');
+import * as React from 'react';
import {RouteComponentProps} from 'react-router-dom';
import {Observable} from 'rxjs';
import {filter, map} from 'rxjs/operators';
@@ -34,7 +34,7 @@ import {ID} from './id';
require('./event-flow-page.scss');
-export const EventFlowPage = ({history, location, match}: RouteComponentProps) => {
+export function EventFlowPage({history, location, match}: RouteComponentProps) {
// boiler-plate
const {navigation} = useContext(Context);
const queryParams = new URLSearchParams(location.search);
@@ -355,4 +355,4 @@ export const EventFlowPage = ({history, location, match}: RouteComponentProps
);
-};
+}
diff --git a/ui/src/app/reports/components/report-container.tsx b/ui/src/app/reports/components/report-container.tsx
index 564c4522eab6..1a14d2eee11d 100644
--- a/ui/src/app/reports/components/report-container.tsx
+++ b/ui/src/app/reports/components/report-container.tsx
@@ -1,9 +1,20 @@
import * as React from 'react';
import {Route, RouteComponentProps, Switch} from 'react-router';
-import {Reports} from './reports';
+import {Loading} from '../../shared/components/loading';
export const ReportsContainer = (props: RouteComponentProps) => (
-
+
);
+
+// lazy load Reports as it is infrequently used and imports large Chart components (which can be split into a separate bundle)
+const LazyReports = React.lazy(() => import('./reports'));
+
+function SuspenseReports(props: RouteComponentProps) {
+ return (
+ }>
+
+
+ );
+}
diff --git a/ui/src/app/reports/components/reports.tsx b/ui/src/app/reports/components/reports.tsx
index 4edd31459c0d..3154a87a9057 100644
--- a/ui/src/app/reports/components/reports.tsx
+++ b/ui/src/app/reports/components/reports.tsx
@@ -131,3 +131,5 @@ export function Reports({match, location, history}: RouteComponentProps) {
);
}
+
+export default Reports;
diff --git a/ui/src/app/shared/components/chat-button.tsx b/ui/src/app/shared/components/chat-button.tsx
index 883ff2f9fefc..99e32d9232b9 100644
--- a/ui/src/app/shared/components/chat-button.tsx
+++ b/ui/src/app/shared/components/chat-button.tsx
@@ -1,9 +1,9 @@
-import React = require('react');
+import * as React from 'react';
import {useEffect, useState} from 'react';
import {Link} from '../../../models';
import {services} from '../services';
-export const ChatButton = () => {
+export function ChatButton() {
const [link, setLink] = useState();
useEffect(() => {
@@ -27,4 +27,4 @@ export const ChatButton = () => {
);
-};
+}
diff --git a/ui/src/app/shared/components/links.tsx b/ui/src/app/shared/components/links.tsx
index ed4ae6154a67..52cd4677e1c2 100644
--- a/ui/src/app/shared/components/links.tsx
+++ b/ui/src/app/shared/components/links.tsx
@@ -1,6 +1,6 @@
import {ObjectMeta} from 'argo-ui/src/models/kubernetes';
import {useEffect, useState} from 'react';
-import React = require('react');
+import * as React from 'react';
import {Link, Workflow} from '../../../models';
import {services} from '../services';
import {Button} from './button';
diff --git a/ui/src/app/shared/components/object-parser.ts b/ui/src/app/shared/components/object-parser.ts
index d5cec314858a..0f3efae0b16a 100644
--- a/ui/src/app/shared/components/object-parser.ts
+++ b/ui/src/app/shared/components/object-parser.ts
@@ -1,4 +1,4 @@
-import jsyaml = require('js-yaml');
+import jsyaml from 'js-yaml';
export function parse(value: string) {
if (value.startsWith('{')) {
diff --git a/ui/src/app/shared/cron.tsx b/ui/src/app/shared/cron.tsx
index 392ec619ff4b..0de584f82dc9 100644
--- a/ui/src/app/shared/cron.tsx
+++ b/ui/src/app/shared/cron.tsx
@@ -1,4 +1,4 @@
-import parser = require('cron-parser');
+import parser from 'cron-parser';
export function getNextScheduledTime(schedule: string, tz: string): Date {
let out: Date;
diff --git a/ui/src/app/tsconfig.json b/ui/src/app/tsconfig.json
index 057b976de35b..124756d87831 100644
--- a/ui/src/app/tsconfig.json
+++ b/ui/src/app/tsconfig.json
@@ -3,7 +3,8 @@
"outDir": "./../../dist/app",
"sourceMap": true,
"noImplicitAny": true,
- "module": "commonjs",
+ "module": "ES2020", // must be ES2020+ for dynamic imports: https://github.com/Microsoft/TypeScript/issues/16675, https://github.com/webpack/webpack/issues/5703#issuecomment-357512412
+ "moduleResolution": "node",
"esModuleInterop": true,
"target": "es5",
"jsx": "react",
diff --git a/ui/src/app/workflows/components/workflow-logs-viewer/workflow-logs-viewer.tsx b/ui/src/app/workflows/components/workflow-logs-viewer/workflow-logs-viewer.tsx
index b2c94abd83ff..8a227247401a 100644
--- a/ui/src/app/workflows/components/workflow-logs-viewer/workflow-logs-viewer.tsx
+++ b/ui/src/app/workflows/components/workflow-logs-viewer/workflow-logs-viewer.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import {useContext, useEffect, useState} from 'react';
import {Autocomplete} from 'argo-ui';
-import moment = require('moment-timezone');
+import moment from 'moment-timezone';
import {Observable} from 'rxjs';
import {map, publishReplay, refCount} from 'rxjs/operators';
import * as models from '../../../../models';