Split frontend code by routes (#1955)

Split large frontend components into own bundles. This way we decrease loading times and load the bundles right as they are used. We replace SystemJS with our own implementation to load the lazy modules right as there are required.

Co-authored-by: Sebastian Sdorra <sebastian.sdorra@cloudogu.com>
This commit is contained in:
Eduard Heimbuch
2022-02-18 14:47:37 +01:00
committed by GitHub
parent dff4ccfb32
commit b85dc8f0e6
31 changed files with 1291 additions and 961 deletions

View File

@@ -23,20 +23,20 @@
*/
import { LegacyContext, useLegacyContext } from "./LegacyContext";
import * as React from "react";
import { FC } from "react";
import { renderHook } from "@testing-library/react-hooks";
import * as React from "react";
import ApiProvider from "./ApiProvider";
import { useQueryClient } from "react-query";
describe("ApiProvider tests", () => {
const createWrapper = (context?: LegacyContext): FC => {
const createWrapper = (context: LegacyContext): FC => {
return ({ children }) => <ApiProvider {...context}>{children}</ApiProvider>;
};
it("should register QueryClient", () => {
const { result } = renderHook(() => useQueryClient(), {
wrapper: createWrapper(),
wrapper: createWrapper({ initialize: () => null })
});
expect(result.current).toBeDefined();
});
@@ -48,7 +48,7 @@ describe("ApiProvider tests", () => {
};
const { result } = renderHook(() => useLegacyContext(), {
wrapper: createWrapper({ onIndexFetched }),
wrapper: createWrapper({ onIndexFetched, initialize: () => null })
});
if (result.current?.onIndexFetched) {

View File

@@ -31,9 +31,9 @@ import { reset } from "./reset";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
retry: false
}
}
});
type Props = LegacyContext & {

View File

@@ -24,12 +24,17 @@
import { IndexResources, Me } from "@scm-manager/ui-types";
import React, { createContext, FC, useContext } from "react";
import { QueryClient, useQueryClient } from "react-query";
export type LegacyContext = {
export type BaseContext = {
onIndexFetched?: (index: IndexResources) => void;
onMeFetched?: (me: Me) => void;
};
export type LegacyContext = BaseContext & {
initialize: () => void;
};
const Context = createContext<LegacyContext | undefined>(undefined);
export const useLegacyContext = () => {
@@ -40,6 +45,31 @@ export const useLegacyContext = () => {
return context;
};
export const LegacyContextProvider: FC<LegacyContext> = ({ onIndexFetched, onMeFetched, children }) => (
<Context.Provider value={{ onIndexFetched, onMeFetched }}>{children}</Context.Provider>
);
const createInitialContext = (queryClient: QueryClient, base: BaseContext): LegacyContext => {
const ctx = {
...base,
initialize: () => {
if (ctx.onIndexFetched) {
const index: IndexResources | undefined = queryClient.getQueryData("index");
if (index) {
ctx.onIndexFetched(index);
}
}
if (ctx.onMeFetched) {
const me: Me | undefined = queryClient.getQueryData("me");
if (me) {
ctx.onMeFetched(me);
}
}
}
};
return ctx;
};
export const LegacyContextProvider: FC<BaseContext> = ({ onIndexFetched, onMeFetched, children }) => {
const queryClient = useQueryClient();
const ctx = createInitialContext(queryClient, { onIndexFetched, onMeFetched });
return <Context.Provider value={ctx}>{children}</Context.Provider>;
};

View File

@@ -34,7 +34,7 @@ describe("Test base api hooks", () => {
describe("useIndex tests", () => {
fetchMock.get("/api/v2/", {
version: "x.y.z",
_links: {},
_links: {}
});
it("should return index", async () => {
@@ -48,9 +48,10 @@ describe("Test base api hooks", () => {
it("should call onIndexFetched of LegacyContext", async () => {
let index: IndexResources;
const context: LegacyContext = {
onIndexFetched: (fetchedIndex) => {
onIndexFetched: fetchedIndex => {
index = fetchedIndex;
},
initialize: () => null
};
const { result, waitFor } = renderHook(() => useIndex(), { wrapper: createWrapper(context) });
await waitFor(() => {
@@ -70,10 +71,10 @@ describe("Test base api hooks", () => {
const queryClient = new QueryClient();
queryClient.setQueryData("index", {
version: "x.y.z",
_links: {},
_links: {}
});
const { result } = renderHook(() => useIndexLink("spaceships"), {
wrapper: createWrapper(undefined, queryClient),
wrapper: createWrapper(undefined, queryClient)
});
expect(result.current).toBeUndefined();
});
@@ -86,17 +87,17 @@ describe("Test base api hooks", () => {
spaceships: [
{
name: "heartOfGold",
href: "/spaceships/heartOfGold",
href: "/spaceships/heartOfGold"
},
{
name: "razorCrest",
href: "/spaceships/razorCrest",
},
],
},
href: "/spaceships/razorCrest"
}
]
}
});
const { result } = renderHook(() => useIndexLink("spaceships"), {
wrapper: createWrapper(undefined, queryClient),
wrapper: createWrapper(undefined, queryClient)
});
expect(result.current).toBeUndefined();
});
@@ -107,12 +108,12 @@ describe("Test base api hooks", () => {
version: "x.y.z",
_links: {
spaceships: {
href: "/api/spaceships",
},
},
href: "/api/spaceships"
}
}
});
const { result } = renderHook(() => useIndexLink("spaceships"), {
wrapper: createWrapper(undefined, queryClient),
wrapper: createWrapper(undefined, queryClient)
});
expect(result.current).toBe("/api/spaceships");
});
@@ -130,12 +131,12 @@ describe("Test base api hooks", () => {
version: "x.y.z",
_links: {
spaceships: {
href: "/api/spaceships",
},
},
href: "/api/spaceships"
}
}
});
const { result } = renderHook(() => useIndexLinks(), {
wrapper: createWrapper(undefined, queryClient),
wrapper: createWrapper(undefined, queryClient)
});
expect((result.current!.spaceships as Link).href).toBe("/api/spaceships");
});
@@ -150,10 +151,10 @@ describe("Test base api hooks", () => {
it("should return version", () => {
const queryClient = new QueryClient();
queryClient.setQueryData("index", {
version: "x.y.z",
version: "x.y.z"
});
const { result } = renderHook(() => useVersion(), {
wrapper: createWrapper(undefined, queryClient),
wrapper: createWrapper(undefined, queryClient)
});
expect(result.current).toBe("x.y.z");
});
@@ -164,10 +165,10 @@ describe("Test base api hooks", () => {
const queryClient = new QueryClient();
queryClient.setQueryData("index", {
version: "x.y.z",
_links: {},
_links: {}
});
const { result } = renderHook(() => useRequiredIndexLink("spaceships"), {
wrapper: createWrapper(undefined, queryClient),
wrapper: createWrapper(undefined, queryClient)
});
expect(result.error).toBeDefined();
});
@@ -178,12 +179,12 @@ describe("Test base api hooks", () => {
version: "x.y.z",
_links: {
spaceships: {
href: "/api/spaceships",
},
},
href: "/api/spaceships"
}
}
});
const { result } = renderHook(() => useRequiredIndexLink("spaceships"), {
wrapper: createWrapper(undefined, queryClient),
wrapper: createWrapper(undefined, queryClient)
});
expect(result.current).toBe("/api/spaceships");
});
@@ -196,19 +197,19 @@ describe("Test base api hooks", () => {
version: "x.y.z",
_links: {
spaceships: {
href: "/spaceships",
},
},
href: "/spaceships"
}
}
});
const spaceship = {
name: "heartOfGold",
name: "heartOfGold"
};
fetchMock.get("/api/v2/spaceships", spaceship);
const { result, waitFor } = renderHook(() => useIndexJsonResource<typeof spaceship>("spaceships"), {
wrapper: createWrapper(undefined, queryClient),
wrapper: createWrapper(undefined, queryClient)
});
await waitFor(() => {
@@ -223,11 +224,11 @@ describe("Test base api hooks", () => {
const queryClient = new QueryClient();
queryClient.setQueryData("index", {
version: "x.y.z",
_links: {},
_links: {}
});
const { result } = renderHook(() => useIndexJsonResource<{}>("spaceships"), {
wrapper: createWrapper(undefined, queryClient),
wrapper: createWrapper(undefined, queryClient)
});
expect(result.current.isLoading).toBe(false);

View File

@@ -66,3 +66,5 @@ export * from "./compare";
export { default as ApiProvider } from "./ApiProvider";
export * from "./ApiProvider";
export * from "./LegacyContext";

View File

@@ -37,7 +37,7 @@ describe("Test login hooks", () => {
name: "tricia",
displayName: "Tricia",
groups: [],
_links: {},
_links: {}
};
describe("useMe tests", () => {
@@ -45,7 +45,7 @@ describe("Test login hooks", () => {
name: "tricia",
displayName: "Tricia",
groups: [],
_links: {},
_links: {}
});
it("should return me", async () => {
@@ -65,9 +65,10 @@ describe("Test login hooks", () => {
let me: Me;
const context: LegacyContext = {
onMeFetched: (fetchedMe) => {
onMeFetched: fetchedMe => {
me = fetchedMe;
},
initialize: () => null
};
const { result, waitFor } = renderHook(() => useMe(), { wrapper: createWrapper(context, queryClient) });
@@ -130,7 +131,7 @@ describe("Test login hooks", () => {
name: "_anonymous",
displayName: "Anonymous",
groups: [],
_links: {},
_links: {}
});
const { result } = renderHook(() => useSubject(), { wrapper: createWrapper(undefined, queryClient) });
@@ -158,8 +159,8 @@ describe("Test login hooks", () => {
cookie: true,
grant_type: "password",
username: "tricia",
password: "hitchhikersSecret!",
},
password: "hitchhikersSecret!"
}
});
// required because we invalidate the whole cache and react-query refetches the index
@@ -167,13 +168,13 @@ describe("Test login hooks", () => {
version: "x.y.z",
_links: {
login: {
href: "/second/login",
},
},
href: "/second/login"
}
}
});
const { result, waitForNextUpdate } = renderHook(() => useLogin(), {
wrapper: createWrapper(undefined, queryClient),
wrapper: createWrapper(undefined, queryClient)
});
const { login } = result.current;
expect(login).toBeDefined();
@@ -194,7 +195,7 @@ describe("Test login hooks", () => {
queryClient.setQueryData("me", tricia);
const { result } = renderHook(() => useLogin(), {
wrapper: createWrapper(undefined, queryClient),
wrapper: createWrapper(undefined, queryClient)
});
expect(result.current.login).toBeUndefined();
@@ -209,7 +210,7 @@ describe("Test login hooks", () => {
fetchMock.deleteOnce("/api/v2/logout", {});
const { result, waitForNextUpdate } = renderHook(() => useLogout(), {
wrapper: createWrapper(undefined, queryClient),
wrapper: createWrapper(undefined, queryClient)
});
const { logout } = result.current;
expect(logout).toBeDefined();
@@ -229,7 +230,7 @@ describe("Test login hooks", () => {
setEmptyIndex(queryClient);
const { result } = renderHook(() => useLogout(), {
wrapper: createWrapper(undefined, queryClient),
wrapper: createWrapper(undefined, queryClient)
});
const { logout } = result.current;