浏览代码

Migrate React app to Vite (#2153)

* Ejected CRA

* Cleanup

* jest

* Cleanup

* refactor cluster widgets

* configure build

* configure build

* configure coverage

* Experiment with lazy

* add ace to vendor

* Cleanup
Oleg Shur 3 年之前
父节点
当前提交
1b71ccb975

+ 2 - 2
kafka-ui-api/pom.xml

@@ -401,8 +401,8 @@
                         <configuration>
                             <workingDirectory>../kafka-ui-react-app</workingDirectory>
                             <environmentVariables>
-                                <REACT_APP_TAG>${project.version}</REACT_APP_TAG>
-                                <REACT_APP_COMMIT>${git.commit.id.abbrev}</REACT_APP_COMMIT>
+                                <VITE_TAG>${project.version}</VITE_TAG>
+                                <VITE_COMMIT>${git.commit.id.abbrev}</VITE_COMMIT>
                             </environmentVariables>
                         </configuration>
                         <executions>

+ 1 - 1
kafka-ui-contract/pom.xml

@@ -138,7 +138,7 @@
                         <configuration>
                             <workingDirectory>../kafka-ui-react-app</workingDirectory>
                             <environmentVariables>
-                                <REACT_APP_TAG>${project.version}</REACT_APP_TAG>
+                                <VITE_TAG>${project.version}</VITE_TAG>
                             </environmentVariables>
                         </configuration>
                         <executions>

+ 7 - 0
kafka-ui-react-app/.babelrc

@@ -0,0 +1,7 @@
+{
+  "presets": [
+    "@babel/preset-env",
+    "@babel/preset-react",
+    "@babel/preset-typescript"
+  ]
+}

+ 1 - 2
kafka-ui-react-app/README.md

@@ -41,8 +41,7 @@ npm run gen:sources
 
 Create or update existing `.env.local` file with
 ```
-HTTPS=true # if needed
-DEV_PROXY= https://api.server # your API server
+VITE_DEV_PROXY= https://api.server # your API server
 ```
 
 Run the application

+ 14 - 0
kafka-ui-react-app/config/jest/cssTransform.js

@@ -0,0 +1,14 @@
+'use strict';
+
+// This is a custom Jest transformer turning style imports into empty objects.
+// http://facebook.github.io/jest/docs/en/webpack.html
+
+module.exports = {
+  process() {
+    return 'module.exports = {};';
+  },
+  getCacheKey() {
+    // The output is always the same.
+    return 'cssTransform';
+  },
+};

+ 5 - 4
kafka-ui-react-app/public/index.html → kafka-ui-react-app/index.html

@@ -2,10 +2,10 @@
 <html lang="en">
   <head>
     <meta charset="utf-8" />
-    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" sizes="any"><!-- 32×32 -->
-    <link rel="icon" href="%PUBLIC_URL%/favicon/icon.svg" type="image/svg+xml">
-    <link rel="apple-touch-icon" href="%PUBLIC_URL%/favicon/apple-touch-icon.png"><!-- 180×180 -->
-    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
+    <link rel="icon" href="/favicon.ico" sizes="any"><!-- 32×32 -->
+    <link rel="icon" href="/favicon/icon.svg" type="image/svg+xml">
+    <link rel="apple-touch-icon" href="/favicon/apple-touch-icon.png"><!-- 180×180 -->
+    <link rel="manifest" href="/manifest.json" />
 
     <meta name="viewport" content="width=device-width, initial-scale=1" />
     <title>UI for Apache Kafka</title>
@@ -16,5 +16,6 @@
   <body>
     <noscript>You need to enable JavaScript to run this app.</noscript>
     <div id="root"></div>
+    <script type="module" src="/src/index.tsx"></script>
   </body>
 </html>

+ 32 - 0
kafka-ui-react-app/jest.config.ts

@@ -0,0 +1,32 @@
+import type { Config } from '@jest/types';
+
+export default {
+  roots: ['<rootDir>/src'],
+  collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}', '!src/**/*.d.ts'],
+  coveragePathIgnorePatterns: [
+    '/node_modules/',
+    '<rootDir>/src/generated-sources/',
+    '<rootDir>/vite.config.ts',
+    '<rootDir>/src/index.tsx',
+    '<rootDir>/src/serviceWorker.ts',
+  ],
+  setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],
+  testMatch: [
+    '<rootDir>/src/**/__{test,tests}__/**/*.{spec,test}.{js,jsx,ts,tsx}',
+  ],
+  testEnvironment: 'jsdom',
+  transform: {
+    '\\.[jt]sx?$': 'babel-jest',
+    '^.+\\.css$': '<rootDir>/config/jest/cssTransform.js',
+  },
+  transformIgnorePatterns: [
+    '[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$',
+    '^.+\\.module\\.(css|sass|scss)$',
+  ],
+  modulePaths: ['<rootDir>/src'],
+  watchPlugins: [
+    'jest-watch-typeahead/filename',
+    'jest-watch-typeahead/testname',
+  ],
+  resetMocks: true,
+} as Config.InitialOptions;

文件差异内容过多而无法显示
+ 665 - 564
kafka-ui-react-app/package-lock.json


+ 22 - 24
kafka-ui-react-app/package.json

@@ -4,6 +4,7 @@
   "homepage": "./",
   "private": true,
   "dependencies": {
+    "@babel/core": "^7.16.0",
     "@fortawesome/fontawesome-free": "^6.1.1",
     "@hookform/error-message": "^2.0.0",
     "@hookform/resolvers": "^2.7.1",
@@ -11,14 +12,16 @@
     "@rooks/use-outside-click-ref": "^4.10.1",
     "@testing-library/react": "^13.2.0",
     "@types/yup": "^0.29.13",
+    "@vitejs/plugin-react": "^1.3.2",
     "ace-builds": "^1.4.12",
     "ajv": "^8.6.3",
+    "babel-jest": "^27.4.2",
     "bulma": "^0.9.3",
     "classnames": "^2.2.6",
     "dayjs": "^1.11.2",
-    "eslint-import-resolver-node": "^0.3.6",
-    "eslint-import-resolver-typescript": "^2.7.1",
     "fetch-mock": "^9.11.0",
+    "jest": "^27.4.3",
+    "jest-watch-typeahead": "^1.0.0",
     "json-schema-faker": "^0.5.0-rcv.39",
     "lodash": "^4.17.21",
     "node-fetch": "^2.6.1",
@@ -34,28 +37,31 @@
     "react-router-dom": "^6.3.0",
     "redux": "^4.1.1",
     "redux-thunk": "^2.3.0",
-    "sass": "^1.43.4",
+    "sass": "^1.52.3",
     "styled-components": "^5.3.1",
     "use-debounce": "^8.0.1",
     "uuid": "^8.3.1",
+    "vite": "^2.9.11",
+    "vite-tsconfig-paths": "^3.5.0",
+    "whatwg-fetch": "^3.6.2",
     "yup": "^0.32.9"
   },
   "lint-staged": {
     "*.{js,ts,jsx,tsx}": [
-      "eslint -c .eslintrc.json --fix",
+      "eslint --fix",
       "npm test -- --bail --findRelatedTests --watchAll=false"
     ]
   },
   "scripts": {
-    "start": "react-scripts start",
+    "start": "vite",
     "gen:sources": "rimraf src/generated-sources && openapi-generator-cli generate",
-    "build": "react-scripts build",
+    "build": "vite build",
     "lint": "eslint --ext .tsx,.ts src/",
     "lint:fix": "eslint --ext .tsx,.ts src/ --fix",
     "lint:CI": "eslint --ext .tsx,.ts src/ --max-warnings=0",
-    "test": "react-scripts test",
-    "test:CI": "CI=true npm test  -- --coverage --ci --testResultsProcessor=\"jest-sonar-reporter\" --watchAll=false",
-    "eject": "react-scripts eject",
+    "test": "jest --watch",
+    "test:coverage": "jest --watchAll --coverage",
+    "test:CI": "CI=true npm run test:coverage -- --ci --testResultsProcessor=\"jest-sonar-reporter\" --watchAll=false",
     "tsc": "tsc",
     "prepare": "cd .. && husky install kafka-ui-react-app/.husky",
     "pre-commit": "npm run tsc --noEmit && lint-staged"
@@ -63,19 +69,10 @@
   "eslintConfig": {
     "extends": "react-app"
   },
-  "browserslist": {
-    "production": [
-      ">0.2%",
-      "not dead",
-      "not op_mini all"
-    ],
-    "development": [
-      "last 1 chrome version",
-      "last 1 firefox version",
-      "last 1 safari version"
-    ]
-  },
   "devDependencies": {
+    "@babel/preset-env": "^7.18.2",
+    "@babel/preset-react": "^7.17.12",
+    "@babel/preset-typescript": "^7.17.12",
     "@jest/types": "^28.1.0",
     "@openapitools/openapi-generator-cli": "^2.5.1",
     "@testing-library/dom": "^8.11.1",
@@ -96,10 +93,13 @@
     "@typescript-eslint/eslint-plugin": "^5.10.0",
     "@typescript-eslint/parser": "^5.27.0",
     "dotenv": "^16.0.1",
-    "eslint": "^8.15.0",
+    "eslint": "^8.3.0",
     "eslint-config-airbnb": "^19.0.4",
     "eslint-config-airbnb-typescript": "^17.0.0",
     "eslint-config-prettier": "^8.5.0",
+    "eslint-config-react-app": "^7.0.1",
+    "eslint-import-resolver-node": "^0.3.6",
+    "eslint-import-resolver-typescript": "^2.7.1",
     "eslint-plugin-import": "^2.26.0",
     "eslint-plugin-jest-dom": "^4.0.2",
     "eslint-plugin-jsx-a11y": "^6.5.1",
@@ -107,13 +107,11 @@
     "eslint-plugin-react": "^7.29.4",
     "eslint-plugin-react-hooks": "^4.5.0",
     "fetch-mock-jest": "^1.5.1",
-    "http-proxy-middleware": "^2.0.6",
     "husky": "^7.0.1",
     "jest-sonar-reporter": "^2.0.0",
     "jest-styled-components": "^7.0.8",
     "lint-staged": "^12.1.2",
     "prettier": "^2.3.1",
-    "react-scripts": "5.0.1",
     "redux-mock-store": "^1.5.4",
     "rimraf": "^3.0.2",
     "ts-jest": "^28.0.3",

+ 1 - 1
kafka-ui-react-app/sonar-project.properties

@@ -2,7 +2,7 @@ sonar.projectKey=com.provectus:kafka-ui_frontend
 sonar.organization=provectus
 
 sonar.sources=.
-sonar.exclusions=**/__tests__/**,**/__test__/**,src/serviceWorker.ts,src/setupTests.ts,src/setupProxy.js,**/fixtures.ts,src/lib/testHelpers.tsx,src/index.tsx
+sonar.exclusions=**/__tests__/**,**/__test__/**,src/serviceWorker.ts,src/setupTests.ts,src/setupProxy.js,**/fixtures.ts,src/lib/testHelpers.tsx,src/index.tsx,vite.config.ts,config/**
 
 sonar.typescript.lcov.reportPaths=./coverage/lcov.info
 sonar.testExecutionReportPaths=./test-report.xml

+ 10 - 7
kafka-ui-react-app/src/components/Cluster/Cluster.tsx

@@ -18,18 +18,21 @@ import {
   clusterTopicsRelativePath,
   getNonExactPath,
 } from 'lib/paths';
-import Brokers from 'components/Brokers/Brokers';
-import Topics from 'components/Topics/Topics';
-import Schemas from 'components/Schemas/Schemas';
-import Connect from 'components/Connect/Connect';
 import ClusterContext from 'components/contexts/ClusterContext';
-import ConsumersGroups from 'components/ConsumerGroups/ConsumerGroups';
-import KsqlDb from 'components/KsqlDb/KsqlDb';
 import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
 import { BreadcrumbRoute } from 'components/common/Breadcrumb/Breadcrumb.route';
 import { BreadcrumbProvider } from 'components/common/Breadcrumb/Breadcrumb.provider';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 
+const Brokers = React.lazy(() => import('components/Brokers/Brokers'));
+const Topics = React.lazy(() => import('components/Topics/Topics'));
+const Schemas = React.lazy(() => import('components/Schemas/Schemas'));
+const Connect = React.lazy(() => import('components/Connect/Connect'));
+const KsqlDb = React.lazy(() => import('components/KsqlDb/KsqlDb'));
+const ConsumerGroups = React.lazy(
+  () => import('components/ConsumerGroups/ConsumerGroups')
+);
+
 const Cluster: React.FC = () => {
   const { clusterName } = useAppParams<ClusterNameRoute>();
   const isReadOnly = useSelector(getClustersReadonlyStatus(clusterName));
@@ -87,7 +90,7 @@ const Cluster: React.FC = () => {
               path={getNonExactPath(clusterConsumerGroupsRelativePath)}
               element={
                 <BreadcrumbRoute>
-                  <ConsumersGroups />
+                  <ConsumerGroups />
                 </BreadcrumbRoute>
               }
             />

+ 15 - 9
kafka-ui-react-app/src/components/Cluster/__tests__/Cluster.spec.tsx

@@ -59,19 +59,19 @@ describe('Cluster', () => {
     await act(() => renderComponent(clusterBrokersPath('second')));
     expect(screen.getByText(CLusterCompText.Brokers)).toBeInTheDocument();
   });
-  it('renders Topics', () => {
-    renderComponent(clusterTopicsPath('second'));
+  it('renders Topics', async () => {
+    await act(() => renderComponent(clusterTopicsPath('second')));
     expect(screen.getByText(CLusterCompText.Topics)).toBeInTheDocument();
   });
-  it('renders ConsumerGroups', () => {
-    renderComponent(clusterConsumerGroupsPath('second'));
+  it('renders ConsumerGroups', async () => {
+    await act(() => renderComponent(clusterConsumerGroupsPath('second')));
     expect(
       screen.getByText(CLusterCompText.ConsumerGroups)
     ).toBeInTheDocument();
   });
 
   describe('configured features', () => {
-    it('does not render Schemas if SCHEMA_REGISTRY is not configured', () => {
+    it('does not render Schemas if SCHEMA_REGISTRY is not configured', async () => {
       store.dispatch(
         fetchClusters.fulfilled(
           [
@@ -83,7 +83,7 @@ describe('Cluster', () => {
           '123'
         )
       );
-      renderComponent(clusterSchemasPath('second'));
+      await act(() => renderComponent(clusterSchemasPath('second')));
       expect(
         screen.queryByText(CLusterCompText.Schemas)
       ).not.toBeInTheDocument();
@@ -100,7 +100,9 @@ describe('Cluster', () => {
           '123'
         )
       );
-      renderComponent(clusterSchemasPath(onlineClusterPayload.name));
+      await act(() =>
+        renderComponent(clusterSchemasPath(onlineClusterPayload.name))
+      );
       expect(screen.getByText(CLusterCompText.Schemas)).toBeInTheDocument();
     });
     it('renders Connect if KAFKA_CONNECT is configured', async () => {
@@ -115,7 +117,9 @@ describe('Cluster', () => {
           'requestId'
         )
       );
-      renderComponent(clusterConnectsPath(onlineClusterPayload.name));
+      await act(() =>
+        renderComponent(clusterConnectsPath(onlineClusterPayload.name))
+      );
       expect(screen.getByText(CLusterCompText.Connect)).toBeInTheDocument();
     });
     it('renders KSQL if KSQL_DB is configured', async () => {
@@ -130,7 +134,9 @@ describe('Cluster', () => {
           'requestId'
         )
       );
-      renderComponent(clusterKsqlDbPath(onlineClusterPayload.name));
+      await act(() =>
+        renderComponent(clusterKsqlDbPath(onlineClusterPayload.name))
+      );
       expect(screen.getByText(CLusterCompText.KsqlDb)).toBeInTheDocument();
     });
   });

+ 5 - 17
kafka-ui-react-app/src/components/Dashboard/ClustersWidget/ClustersWidget.tsx

@@ -1,6 +1,5 @@
 import React from 'react';
 import { chunk } from 'lodash';
-import { v4 } from 'uuid';
 import * as Metrics from 'components/common/Metrics';
 import { Cluster } from 'generated-sources';
 import { Tag } from 'components/common/Tag/Tag.styled';
@@ -19,11 +18,6 @@ interface Props {
   offlineClusters: Cluster[];
 }
 
-interface ChunkItem {
-  id: string;
-  data: Cluster[];
-}
-
 const ClustersWidget: React.FC<Props> = ({
   clusters,
   onlineClusters,
@@ -31,17 +25,11 @@ const ClustersWidget: React.FC<Props> = ({
 }) => {
   const [showOfflineOnly, setShowOfflineOnly] = React.useState<boolean>(false);
 
-  const clusterList: ChunkItem[] = React.useMemo(() => {
-    let list = clusters;
-
+  const clusterList = React.useMemo(() => {
     if (showOfflineOnly) {
-      list = offlineClusters;
+      return chunk(offlineClusters, 2);
     }
-
-    return chunk(list, 2).map((data) => ({
-      id: v4(),
-      data,
-    }));
+    return chunk(clusters, 2);
   }, [clusters, offlineClusters, showOfflineOnly]);
 
   const handleSwitch = () => setShowOfflineOnly(!showOfflineOnly);
@@ -69,7 +57,7 @@ const ClustersWidget: React.FC<Props> = ({
         <label>Only offline clusters</label>
       </S.SwitchWrapper>
       {clusterList.map((chunkItem) => (
-        <Table key={chunkItem.id} isFullwidth>
+        <Table key={chunkItem.map(({ name }) => name).join('-')} isFullwidth>
           <thead>
             <tr>
               <TableHeaderCell title="Cluster name" />
@@ -82,7 +70,7 @@ const ClustersWidget: React.FC<Props> = ({
             </tr>
           </thead>
           <tbody>
-            {chunkItem.data.map((cluster) => (
+            {chunkItem.map((cluster) => (
               <tr key={cluster.name}>
                 <S.TableCell maxWidth="99px" width="350">
                   {cluster.readOnly && <Tag color="blue">readonly</Tag>}{' '}

+ 0 - 0
kafka-ui-react-app/src/components/Topics/shared/Form/__tests__/TopicForm.styled.tsx → kafka-ui-react-app/src/components/Topics/shared/Form/__tests__/TopicForm.styled.spec.tsx


+ 0 - 6
kafka-ui-react-app/src/index.tsx

@@ -3,7 +3,6 @@ import { createRoot } from 'react-dom/client';
 import { BrowserRouter } from 'react-router-dom';
 import { Provider } from 'react-redux';
 import { QueryClient, QueryClientProvider } from 'react-query';
-import * as serviceWorker from 'serviceWorker';
 import App from 'components/App';
 import { store } from 'redux/store';
 import 'theme/index.scss';
@@ -24,8 +23,3 @@ root.render(
     </BrowserRouter>
   </Provider>
 );
-
-// If you want your app to work offline and load faster, you can change
-// unregister() to register() below. Note this comes with some pitfalls.
-// Learn more about service workers: https://bit.ly/CRA-PWA
-serviceWorker.unregister();

+ 2 - 2
kafka-ui-react-app/src/lib/constants.ts

@@ -56,8 +56,8 @@ export const PER_PAGE = 25;
 export const GIT_REPO_LINK = 'https://github.com/provectus/kafka-ui';
 export const GIT_REPO_LATEST_RELEASE_LINK =
   'https://api.github.com/repos/provectus/kafka-ui/releases/latest';
-export const GIT_TAG = process.env.REACT_APP_TAG;
-export const GIT_COMMIT = process.env.REACT_APP_COMMIT;
+export const GIT_TAG = process.env.VITE_TAG;
+export const GIT_COMMIT = process.env.VITE_COMMIT;
 
 export const BREADCRUMB_DEFINITIONS: BreadcrumbDefinitions = {
   Ksqldb: 'ksqlDB',

+ 3 - 1
kafka-ui-react-app/src/react-app-env.d.ts

@@ -1 +1,3 @@
-// / <reference types="react-scripts" />
+/// <reference types="node" />
+/// <reference types="react" />
+/// <reference types="react-dom" />

+ 0 - 145
kafka-ui-react-app/src/serviceWorker.ts

@@ -1,145 +0,0 @@
-// This optional code is used to register a service worker.
-// register() is not called by default.
-
-// This lets the app load faster on subsequent visits in production, and gives
-// it offline capabilities. However, it also means that developers (and users)
-// will only see deployed updates on subsequent visits to a page, after all the
-// existing tabs open on the page have been closed, since previously cached
-// resources are updated in the background.
-
-// To learn more about the benefits of this model and instructions on how to
-// opt-in, read https://bit.ly/CRA-PWA
-/* eslint-disable no-console */
-
-const isLocalhost = Boolean(
-  window.location.hostname === 'localhost' ||
-    // [::1] is the IPv6 localhost address.
-    window.location.hostname === '[::1]' ||
-    // 127.0.0.0/8 are considered localhost for IPv4.
-    window.location.hostname.match(
-      /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
-    )
-);
-
-type Config = {
-  onSuccess?: (registration: ServiceWorkerRegistration) => void;
-  onUpdate?: (registration: ServiceWorkerRegistration) => void;
-};
-
-function registerValidSW(swUrl: string, config?: Config) {
-  navigator.serviceWorker
-    .register(swUrl)
-    .then((registration) => {
-      // eslint-disable-next-line no-param-reassign
-      registration.onupdatefound = () => {
-        const installingWorker = registration.installing;
-        if (installingWorker == null) {
-          return;
-        }
-        installingWorker.onstatechange = () => {
-          if (installingWorker.state === 'installed') {
-            if (navigator.serviceWorker.controller) {
-              // At this point, the updated precached content has been fetched,
-              // but the previous service worker will still serve the older
-              // content until all client tabs are closed.
-              console.log(
-                'New content is available and will be used when all ' +
-                  'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
-              );
-
-              // Execute callback
-              if (config && config.onUpdate) {
-                config.onUpdate(registration);
-              }
-            } else {
-              // At this point, everything has been precached.
-              // It's the perfect time to display a
-              // "Content is cached for offline use." message.
-              console.log('Content is cached for offline use.');
-
-              // Execute callback
-              if (config && config.onSuccess) {
-                config.onSuccess(registration);
-              }
-            }
-          }
-        };
-      };
-    })
-    .catch((error) => {
-      console.error('Error during service worker registration:', error);
-    });
-}
-
-function checkValidServiceWorker(swUrl: string, config?: Config) {
-  // Check if the service worker can be found. If it can't reload the page.
-  fetch(swUrl, {
-    headers: { 'Service-Worker': 'script' },
-  })
-    .then((response) => {
-      // Ensure service worker exists, and that we really are getting a JS file.
-      const contentType = response.headers.get('content-type');
-      if (
-        response.status === 404 ||
-        (contentType != null && contentType.indexOf('javascript') === -1)
-      ) {
-        // No service worker found. Probably a different app. Reload the page.
-        navigator.serviceWorker.ready.then((registration) => {
-          registration.unregister().then(() => {
-            window.location.reload();
-          });
-        });
-      } else {
-        // Service worker found. Proceed as normal.
-        registerValidSW(swUrl, config);
-      }
-    })
-    .catch(() => {
-      console.log(
-        'No internet connection found. App is running in offline mode.'
-      );
-    });
-}
-
-export function register(config?: Config) {
-  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
-    const url = process.env.PUBLIC_URL || 'localhost';
-    // The URL constructor is available in all browsers that support SW.
-    const publicUrl = new URL(url, window.location.href);
-    if (publicUrl.origin !== window.location.origin) {
-      // Our service worker won't work if PUBLIC_URL is on a different origin
-      // from what our page is served on. This might happen if a CDN is used to
-      // serve assets; see https://github.com/facebook/create-react-app/issues/2374
-      return;
-    }
-
-    window.addEventListener('load', () => {
-      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
-
-      if (isLocalhost) {
-        // This is running on localhost. Let's check if a service worker still exists or not.
-        checkValidServiceWorker(swUrl, config);
-
-        // Add some additional logging to localhost, pointing developers to the
-        // service worker/PWA documentation.
-        navigator.serviceWorker.ready.then(() => {
-          console.log(
-            'This web app is being served cache-first by a service ' +
-              'worker. To learn more, visit https://bit.ly/CRA-PWA'
-          );
-        });
-      } else {
-        // Is not localhost. Just register service worker
-        registerValidSW(swUrl, config);
-      }
-    });
-  }
-}
-
-export function unregister() {
-  if ('serviceWorker' in navigator) {
-    navigator.serviceWorker.ready.then((registration) => {
-      registration.unregister();
-    });
-  }
-}

+ 0 - 15
kafka-ui-react-app/src/setupProxy.js

@@ -1,15 +0,0 @@
-// eslint-disable-next-line @typescript-eslint/no-var-requires
-const { createProxyMiddleware } = require('http-proxy-middleware');
-
-module.exports = (app) => {
-  if (process.env.DEV_PROXY) {
-    app.use(
-      '/api',
-      createProxyMiddleware({
-        target: process.env.DEV_PROXY,
-        changeOrigin: true,
-        secure: false,
-      })
-    );
-  }
-};

+ 1 - 0
kafka-ui-react-app/src/setupTests.ts

@@ -1,3 +1,4 @@
+import 'whatwg-fetch';
 import 'jest-styled-components';
 import '@testing-library/jest-dom/extend-expect';
 import '@testing-library/jest-dom';

+ 5 - 2
kafka-ui-react-app/tsconfig.json

@@ -1,6 +1,6 @@
 {
   "compilerOptions": {
-    "target": "es5",
+    "target": "esnext",
     "lib": [
       "dom",
       "dom.iterable",
@@ -19,9 +19,12 @@
     "noEmit": true,
     "jsx": "react-jsx",
     "baseUrl": "src",
-    "noFallthroughCasesInSwitch": true
+    "noFallthroughCasesInSwitch": true,
+    "types": ["vite/client"]
   },
   "include": [
     "src",
+    "vite.config.ts",
+    "jest.config.ts",
   ]
 }

+ 55 - 0
kafka-ui-react-app/vite.config.ts

@@ -0,0 +1,55 @@
+import { defineConfig, loadEnv, UserConfigExport } from 'vite';
+import react from '@vitejs/plugin-react';
+import tsconfigPaths from 'vite-tsconfig-paths';
+
+export default defineConfig(({ mode }) => {
+  process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
+
+  const defaultConfig: UserConfigExport = {
+    plugins: [react(), tsconfigPaths()],
+    build: {
+      outDir: 'build',
+      rollupOptions: {
+        output: {
+          manualChunks: {
+            venod: [
+              'react',
+              'react-router-dom',
+              'react-dom',
+              'redux',
+              'redux-thunk',
+              'react-redux',
+              'styled-components',
+              'react-ace',
+            ],
+            lodash: ['lodash'],
+          },
+        },
+      },
+    },
+    define: {
+      'process.env.NODE_ENV': `"${mode}"`,
+      'process.env.VITE_TAG': `"${process.env.VITE_TAG}"`,
+      'process.env.GIT_COMMIT': `"${process.env.VITE_COMMIT}"`,
+    },
+  };
+  const proxy = process.env.VITE_DEV_PROXY;
+
+  if (mode === 'development' && proxy) {
+    return {
+      ...defaultConfig,
+      server: {
+        open: true,
+        proxy: {
+          '/api': {
+            target: proxy,
+            changeOrigin: true,
+            secure: false,
+          },
+        },
+      },
+    };
+  }
+
+  return defaultConfig;
+});

部分文件因为文件数量过多而无法显示