mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-14 04:26:02 +08:00
fix(FE): AppRoutes is refactored (#260)
* react-app-env.d.ts is moved to the typings * webpack config for development and production is updated * extra browser router component is removed * loable component is made * spinner component is updated * route are updated * routes are imported is Loadable fashion with chunkName * AppRoute is updated * AppWrapper is changed to AppRouter * merge conflits are resolved * Loadable component is updated Co-authored-by: Ankit Nayan <ankit@signoz.io>
This commit is contained in:
parent
f394f72bfb
commit
9008d19a7b
43
frontend/src/AppRoutes/index.tsx
Normal file
43
frontend/src/AppRoutes/index.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import React, { Suspense } from "react";
|
||||||
|
import ROUTES from "constants/routes";
|
||||||
|
import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom";
|
||||||
|
import Spinner from "components/Spinner";
|
||||||
|
import NotFound from "components/NotFound";
|
||||||
|
|
||||||
|
import { IS_LOGGED_IN } from "constants/auth";
|
||||||
|
|
||||||
|
import AppLayout from "modules/AppLayout";
|
||||||
|
import { RouteProvider } from "modules/RouteProvider";
|
||||||
|
import routes from "./routes";
|
||||||
|
|
||||||
|
const App = () => (
|
||||||
|
<BrowserRouter basename="/">
|
||||||
|
<RouteProvider>
|
||||||
|
<AppLayout>
|
||||||
|
<Suspense fallback={<Spinner size="large" tip="Loading..." />}>
|
||||||
|
<Switch>
|
||||||
|
{routes.map(({ path, component, exact }) => {
|
||||||
|
return <Route exact={exact} path={path} component={component} />;
|
||||||
|
})}
|
||||||
|
|
||||||
|
{/* This logic should be moved to app layout */}
|
||||||
|
<Route
|
||||||
|
path="/"
|
||||||
|
exact
|
||||||
|
render={() => {
|
||||||
|
return localStorage.getItem(IS_LOGGED_IN) === "yes" ? (
|
||||||
|
<Redirect to={ROUTES.APPLICATION} />
|
||||||
|
) : (
|
||||||
|
<Redirect to={ROUTES.SIGN_UP} />
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Route path="*" component={NotFound} />
|
||||||
|
</Switch>
|
||||||
|
</Suspense>
|
||||||
|
</AppLayout>
|
||||||
|
</RouteProvider>
|
||||||
|
</BrowserRouter>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default App;
|
70
frontend/src/AppRoutes/routes.ts
Normal file
70
frontend/src/AppRoutes/routes.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import {
|
||||||
|
ServiceMetricsPage,
|
||||||
|
ServiceMapPage,
|
||||||
|
TraceDetailPage,
|
||||||
|
TraceGraphPage,
|
||||||
|
UsageExplorerPage,
|
||||||
|
ServicesTablePage,
|
||||||
|
SignupPage,
|
||||||
|
SettingsPage,
|
||||||
|
InstrumentationPage,
|
||||||
|
} from "pages";
|
||||||
|
import ROUTES from "constants/routes";
|
||||||
|
import { RouteProps } from "react-router-dom";
|
||||||
|
|
||||||
|
const routes: AppRoutes[] = [
|
||||||
|
{
|
||||||
|
component: SignupPage,
|
||||||
|
path: ROUTES.SIGN_UP,
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: ServicesTablePage,
|
||||||
|
path: ROUTES.APPLICATION,
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ROUTES.SERVICE_METRICS,
|
||||||
|
exact: true,
|
||||||
|
component: ServiceMetricsPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ROUTES.SERVICE_MAP,
|
||||||
|
component: ServiceMapPage,
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ROUTES.TRACE_GRAPH,
|
||||||
|
exact: true,
|
||||||
|
component: TraceGraphPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ROUTES.SETTINGS,
|
||||||
|
exact: true,
|
||||||
|
component: SettingsPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ROUTES.USAGE_EXPLORER,
|
||||||
|
exact: true,
|
||||||
|
component: UsageExplorerPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ROUTES.INSTRUMENTATION,
|
||||||
|
exact: true,
|
||||||
|
component: InstrumentationPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ROUTES.TRACES,
|
||||||
|
exact: true,
|
||||||
|
component: TraceDetailPage,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
interface AppRoutes {
|
||||||
|
component: RouteProps["component"];
|
||||||
|
path: RouteProps["path"];
|
||||||
|
exact: RouteProps["exact"];
|
||||||
|
isPrivate?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default routes;
|
17
frontend/src/components/Loadable/index.tsx
Normal file
17
frontend/src/components/Loadable/index.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { lazy, ComponentType } from "react";
|
||||||
|
|
||||||
|
function Loadable(importPath: {
|
||||||
|
(): LoadableProps;
|
||||||
|
}): React.LazyExoticComponent<LazyComponent> {
|
||||||
|
const LazyComponent = lazy(() => importPath());
|
||||||
|
|
||||||
|
return LazyComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
type LazyComponent = ComponentType<Record<string, unknown>>;
|
||||||
|
|
||||||
|
type LoadableProps = Promise<{
|
||||||
|
default: LazyComponent;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export default Loadable;
|
@ -1,42 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Spin } from 'antd';
|
|
||||||
import styled from "styled-components";
|
|
||||||
import { LoadingOutlined } from '@ant-design/icons';
|
|
||||||
const antIcon = <LoadingOutlined style={{ fontSize: 24 }} spin />;
|
|
||||||
|
|
||||||
const SpinerStyle = styled.div`
|
|
||||||
position: fixed;
|
|
||||||
z-index: 999;
|
|
||||||
height: 4em;
|
|
||||||
// width: 4em;
|
|
||||||
overflow: visible;
|
|
||||||
margin: auto;
|
|
||||||
top: 0;
|
|
||||||
left: 50%;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const CustomSpinner = ({
|
|
||||||
size,
|
|
||||||
tip,
|
|
||||||
}:{
|
|
||||||
size:string,
|
|
||||||
tip:string,
|
|
||||||
})=>{
|
|
||||||
return(
|
|
||||||
<>
|
|
||||||
<SpinerStyle>
|
|
||||||
<Spin size={size} tip={tip} indicator={antIcon}/>
|
|
||||||
</SpinerStyle>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DefaultSpinner = ()=>{
|
|
||||||
return(
|
|
||||||
<>
|
|
||||||
<Spin indicator={antIcon}/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
19
frontend/src/components/Spinner/index.tsx
Normal file
19
frontend/src/components/Spinner/index.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Spin, SpinProps } from "antd";
|
||||||
|
import { LoadingOutlined } from "@ant-design/icons";
|
||||||
|
|
||||||
|
import { SpinerStyle } from "./styles";
|
||||||
|
|
||||||
|
const Spinner = ({ size, tip, height }: SpinnerProps): JSX.Element => (
|
||||||
|
<SpinerStyle height={height}>
|
||||||
|
<Spin spinning size={size} tip={tip} indicator={<LoadingOutlined spin />} />
|
||||||
|
</SpinerStyle>
|
||||||
|
);
|
||||||
|
|
||||||
|
interface SpinnerProps {
|
||||||
|
size?: SpinProps["size"];
|
||||||
|
tip?: SpinProps["tip"];
|
||||||
|
height?: React.CSSProperties["height"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Spinner;
|
16
frontend/src/components/Spinner/styles.ts
Normal file
16
frontend/src/components/Spinner/styles.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import React from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
height: React.CSSProperties["height"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SpinerStyle = styled.div<Props>`
|
||||||
|
z-index: 999;
|
||||||
|
overflow: visible;
|
||||||
|
margin: auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: ${({ height = "100vh" }) => height};
|
||||||
|
`;
|
@ -3,18 +3,15 @@ import ReactDOM from "react-dom";
|
|||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
import { ThemeSwitcherProvider } from "react-css-theme-switcher";
|
import { ThemeSwitcherProvider } from "react-css-theme-switcher";
|
||||||
import store from "store";
|
import store from "store";
|
||||||
import AppWrapper from "modules/AppWrapper";
|
import AppRoutes from "AppRoutes";
|
||||||
import "assets/index.css";
|
import "assets/index.css";
|
||||||
import { BrowserRouter as Router } from "react-router-dom";
|
|
||||||
import themes from "themes";
|
import themes from "themes";
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<ThemeSwitcherProvider themeMap={themes} defaultTheme="dark">
|
<ThemeSwitcherProvider themeMap={themes} defaultTheme="dark">
|
||||||
<Router basename="/">
|
<AppRoutes />
|
||||||
<AppWrapper />
|
|
||||||
</Router>
|
|
||||||
</ThemeSwitcherProvider>
|
</ThemeSwitcherProvider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
import React, { Suspense } from "react";
|
|
||||||
import { useThemeSwitcher } from "react-css-theme-switcher";
|
|
||||||
import ROUTES from "constants/routes";
|
|
||||||
import { IS_LOGGED_IN } from "constants/auth";
|
|
||||||
import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom";
|
|
||||||
import { CustomSpinner } from "components/Spiner";
|
|
||||||
|
|
||||||
import BaseLayout from "./BaseLayout";
|
|
||||||
import {
|
|
||||||
ServiceMetrics,
|
|
||||||
ServiceMap,
|
|
||||||
TraceDetail,
|
|
||||||
TraceGraph,
|
|
||||||
UsageExplorer,
|
|
||||||
ServicesTable,
|
|
||||||
Signup,
|
|
||||||
SettingsPage,
|
|
||||||
InstrumentationPage,
|
|
||||||
} from "pages";
|
|
||||||
import { RouteProvider } from "./RouteProvider";
|
|
||||||
import NotFound from "components/NotFound";
|
|
||||||
|
|
||||||
const App = () => {
|
|
||||||
const { status } = useThemeSwitcher();
|
|
||||||
|
|
||||||
if (status === "loading") {
|
|
||||||
return <CustomSpinner size="large" tip="Loading..." />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BrowserRouter>
|
|
||||||
<RouteProvider>
|
|
||||||
<BaseLayout>
|
|
||||||
<Suspense fallback={<CustomSpinner size="large" tip="Loading..." />}>
|
|
||||||
<Switch>
|
|
||||||
<Route path={ROUTES.SIGN_UP} exact component={Signup} />
|
|
||||||
<Route path={ROUTES.APPLICATION} exact component={ServicesTable} />
|
|
||||||
<Route path={ROUTES.SERVICE_METRICS} exact component={ServiceMetrics} />
|
|
||||||
<Route path={ROUTES.SERVICE_MAP} exact component={ServiceMap} />
|
|
||||||
<Route path={ROUTES.TRACES} exact component={TraceDetail} />
|
|
||||||
<Route path={ROUTES.TRACE_GRAPH} exact component={TraceGraph} />
|
|
||||||
<Route path={ROUTES.SETTINGS} exact component={SettingsPage} />
|
|
||||||
<Route
|
|
||||||
path={ROUTES.INSTRUMENTATION}
|
|
||||||
exact
|
|
||||||
component={InstrumentationPage}
|
|
||||||
/>
|
|
||||||
<Route path={ROUTES.USAGE_EXPLORER} exact component={UsageExplorer} />
|
|
||||||
<Route
|
|
||||||
path="/"
|
|
||||||
exact
|
|
||||||
render={() => {
|
|
||||||
return localStorage.getItem(IS_LOGGED_IN) === "yes" ? (
|
|
||||||
<Redirect to={ROUTES.APPLICATION} />
|
|
||||||
) : (
|
|
||||||
<Redirect to={ROUTES.SIGN_UP} />
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Route path="*" exact component={NotFound} />
|
|
||||||
</Switch>
|
|
||||||
</Suspense>
|
|
||||||
</BaseLayout>
|
|
||||||
</RouteProvider>
|
|
||||||
</BrowserRouter>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default App;
|
|
@ -3,13 +3,13 @@ import { NavLink } from "react-router-dom";
|
|||||||
import { Button, Space, Table } from "antd";
|
import { Button, Space, Table } from "antd";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
import Spinner from "components/Spinner";
|
||||||
import { SKIP_ONBOARDING } from "constants/onboarding";
|
import { SKIP_ONBOARDING } from "constants/onboarding";
|
||||||
import ROUTES from "constants/routes";
|
import ROUTES from "constants/routes";
|
||||||
import { getServicesList, GlobalTime } from "store/actions";
|
import { getServicesList, GlobalTime } from "store/actions";
|
||||||
import { servicesListItem } from "store/actions/MetricsActions";
|
import { servicesListItem } from "store/actions/MetricsActions";
|
||||||
import { StoreState } from "store/reducers";
|
import { StoreState } from "store/reducers";
|
||||||
import { CustomModal } from "components/Modal";
|
import { CustomModal } from "components/Modal";
|
||||||
import { CustomSpinner, DefaultSpinner } from "components/Spiner";
|
|
||||||
|
|
||||||
interface ServicesTableProps {
|
interface ServicesTableProps {
|
||||||
servicesList: servicesListItem[];
|
servicesList: servicesListItem[];
|
||||||
@ -124,7 +124,7 @@ const _ServicesTable = (props: ServicesTableProps) => {
|
|||||||
}, [props.servicesList, errorObject]);
|
}, [props.servicesList, errorObject]);
|
||||||
|
|
||||||
if (!initialDataFetch) {
|
if (!initialDataFetch) {
|
||||||
return <CustomSpinner size="large" tip="Fetching data..." />;
|
return <Spinner height="90vh" size="large" tip="Fetching data..." />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refetchFromBackend && !skipOnboarding) {
|
if (refetchFromBackend && !skipOnboarding) {
|
||||||
@ -150,7 +150,7 @@ const _ServicesTable = (props: ServicesTableProps) => {
|
|||||||
allowFullScreen
|
allowFullScreen
|
||||||
></iframe>
|
></iframe>
|
||||||
<div style={{ margin: "20px 0" }}>
|
<div style={{ margin: "20px 0" }}>
|
||||||
<DefaultSpinner />
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
No instrumentation data.
|
No instrumentation data.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { RouteComponentProps } from "react-router-dom";
|
import { RouteComponentProps, withRouter } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
GlobalTime,
|
GlobalTime,
|
||||||
serviceMapStore,
|
serviceMapStore,
|
||||||
@ -13,8 +13,8 @@ import { StoreState } from "store/reducers";
|
|||||||
import { getZoomPx, getGraphData, getTooltip, transformLabel } from "./utils";
|
import { getZoomPx, getGraphData, getTooltip, transformLabel } from "./utils";
|
||||||
import SelectService from "./SelectService";
|
import SelectService from "./SelectService";
|
||||||
import { ForceGraph2D } from "react-force-graph";
|
import { ForceGraph2D } from "react-force-graph";
|
||||||
|
import Spinner from "components/Spinner";
|
||||||
import { useRoute } from "modules/RouteProvider";
|
import { useRoute } from "modules/RouteProvider";
|
||||||
import { CustomSpinner } from "components/Spiner";
|
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
.force-graph-container .graph-tooltip {
|
.force-graph-container .graph-tooltip {
|
||||||
@ -78,7 +78,7 @@ const ServiceMap = (props: ServiceMapProps) => {
|
|||||||
fgRef.current && fgRef.current.d3Force("charge").strength(-400);
|
fgRef.current && fgRef.current.d3Force("charge").strength(-400);
|
||||||
});
|
});
|
||||||
if (!serviceMap.items.length || !serviceMap.services.length) {
|
if (!serviceMap.items.length || !serviceMap.services.length) {
|
||||||
return <CustomSpinner size="large" tip="Loading..." />;
|
return <Spinner size="large" tip="Loading..." />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const zoomToService = (value: string) => {
|
const zoomToService = (value: string) => {
|
||||||
@ -150,7 +150,9 @@ const mapStateToProps = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps, {
|
export default withRouter(
|
||||||
getServiceMapItems: getServiceMapItems,
|
connect(mapStateToProps, {
|
||||||
getDetailedServiceMapItems: getDetailedServiceMapItems,
|
getServiceMapItems: getServiceMapItems,
|
||||||
})(ServiceMap);
|
getDetailedServiceMapItems: getDetailedServiceMapItems,
|
||||||
|
})(ServiceMap),
|
||||||
|
);
|
||||||
|
@ -1,28 +1,61 @@
|
|||||||
import React from "react";
|
import Loadable from "./components/Loadable";
|
||||||
|
|
||||||
export const ServiceMetrics = React.lazy(
|
export const ServiceMetricsPage = Loadable(
|
||||||
() => import("modules/Metrics/ServiceMetricsDef"),
|
() =>
|
||||||
);
|
import(
|
||||||
export const ServiceMap = React.lazy(
|
/* webpackChunkName: "ServiceMetricsPage" */ "modules/Metrics/ServiceMetricsDef"
|
||||||
() => import("modules/Servicemap/ServiceMap"),
|
),
|
||||||
);
|
|
||||||
export const TraceDetail = React.lazy(
|
|
||||||
() => import("modules/Traces/TraceDetail"),
|
|
||||||
);
|
|
||||||
export const TraceGraph = React.lazy(
|
|
||||||
() => import("modules/Traces/TraceGraphDef"),
|
|
||||||
);
|
|
||||||
export const UsageExplorer = React.lazy(
|
|
||||||
() => import("modules/Usage/UsageExplorerDef"),
|
|
||||||
);
|
|
||||||
export const ServicesTable = React.lazy(
|
|
||||||
() => import("modules/Metrics/ServicesTableDef"),
|
|
||||||
);
|
|
||||||
export const Signup = React.lazy(() => import("modules/Auth/Signup"));
|
|
||||||
export const SettingsPage = React.lazy(
|
|
||||||
() => import("modules/Settings/settingsPage"),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export const InstrumentationPage = React.lazy(
|
export const ServiceMapPage = Loadable(
|
||||||
() => import("modules/add-instrumentation/instrumentationPage"),
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "ServiceMapPage" */ "modules/Servicemap/ServiceMap"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const TraceDetailPage = Loadable(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "TraceDetailPage" */ "modules/Traces/TraceDetail"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const TraceGraphPage = Loadable(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "TraceGraphPage" */ "modules/Traces/TraceGraphDef"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const UsageExplorerPage = Loadable(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "UsageExplorerPage" */ "modules/Usage/UsageExplorerDef"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ServicesTablePage = Loadable(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "ServicesTablePage" */ "modules/Metrics/ServicesTableDef"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const SignupPage = Loadable(
|
||||||
|
() => import(/* webpackChunkName: "SignupPage" */ "modules/Auth/Signup"),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const SettingsPage = Loadable(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "SettingsPage" */ "modules/Settings/settingsPage"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const InstrumentationPage = Loadable(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "InstrumentationPage" */ "modules/add-instrumentation/instrumentationPage"
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
@ -29,7 +29,9 @@ module.exports = {
|
|||||||
port: portFinderSync.getPort(3000),
|
port: portFinderSync.getPort(3000),
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
filename: "js/bundle.[chunkhash].min.js",
|
filename: ({ chunk: { name, hash } }) => {
|
||||||
|
return `js/${name}-${hash}.js`;
|
||||||
|
},
|
||||||
path: resolve(__dirname, "./build"),
|
path: resolve(__dirname, "./build"),
|
||||||
publicPath: "/",
|
publicPath: "/",
|
||||||
},
|
},
|
||||||
|
@ -11,7 +11,9 @@ module.exports = {
|
|||||||
devtool: "source-map",
|
devtool: "source-map",
|
||||||
entry: resolve(__dirname, "./src/index.tsx"),
|
entry: resolve(__dirname, "./src/index.tsx"),
|
||||||
output: {
|
output: {
|
||||||
filename: "js/bundle.[chunkhash].min.js",
|
filename: ({ chunk: { name, hash } }) => {
|
||||||
|
return `js/${name}-${hash}.js`;
|
||||||
|
},
|
||||||
path: resolve(__dirname, "./build"),
|
path: resolve(__dirname, "./build"),
|
||||||
publicPath: "/",
|
publicPath: "/",
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user