mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
refactor: use react router
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
package com.reajason.javaweb.boot.controller;
|
||||
|
||||
import com.reajason.javaweb.boot.entity.Config;
|
||||
import com.reajason.javaweb.memshell.Packers;
|
||||
import com.reajason.javaweb.memshell.Server;
|
||||
import com.reajason.javaweb.memshell.ShellTool;
|
||||
import com.reajason.javaweb.memshell.server.AbstractShell;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -20,8 +18,28 @@ import java.util.*;
|
||||
@RequestMapping("/config")
|
||||
@CrossOrigin("*")
|
||||
public class ConfigController {
|
||||
|
||||
@RequestMapping("/servers")
|
||||
public Map<String, List<String>> getServers() {
|
||||
Map<String, List<String>> servers = new LinkedHashMap<>();
|
||||
for (Server server : Server.values()) {
|
||||
if (server.getShell() != null) {
|
||||
Set<String> supportedShellTypes = server.getShell().getShellInjectorMapping().getSupportedShellTypes();
|
||||
servers.put(server.name(), supportedShellTypes.stream().toList());
|
||||
}
|
||||
}
|
||||
return servers;
|
||||
}
|
||||
|
||||
@RequestMapping("/packers")
|
||||
public List<String> getPackers() {
|
||||
return Arrays.stream(Packers.values())
|
||||
.filter(packers -> packers.getParentPacker() == null)
|
||||
.map(Packers::name).toList();
|
||||
}
|
||||
|
||||
@RequestMapping
|
||||
public ResponseEntity<?> config() {
|
||||
public Map<String, Map<?, ?>> config() {
|
||||
Map<String, Map<?, ?>> coreMap = new HashMap<>(16);
|
||||
for (Server value : Server.values()) {
|
||||
AbstractShell shell = value.getShell();
|
||||
@@ -38,21 +56,6 @@ public class ConfigController {
|
||||
}
|
||||
coreMap.put(value.name(), map);
|
||||
}
|
||||
Config config = new Config();
|
||||
Map<String, List<String>> servers = new LinkedHashMap<>();
|
||||
for (Server server : Server.values()) {
|
||||
if (server.getShell() != null) {
|
||||
Set<String> supportedShellTypes = server.getShell().getShellInjectorMapping().getSupportedShellTypes();
|
||||
servers.put(server.name(), supportedShellTypes.stream().toList());
|
||||
}
|
||||
}
|
||||
config.setServers(servers);
|
||||
config.setCore(coreMap);
|
||||
config.setPackers(
|
||||
Arrays.stream(Packers.values())
|
||||
.filter(packers -> packers.getParentPacker() == null)
|
||||
.map(Packers::name).toList()
|
||||
);
|
||||
return ResponseEntity.ok(config);
|
||||
return coreMap;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.reajason.javaweb.boot.controller;
|
||||
|
||||
import com.reajason.javaweb.memshell.MemShellGenerator;
|
||||
import com.reajason.javaweb.boot.dto.GenerateRequest;
|
||||
import com.reajason.javaweb.boot.dto.GenerateResponse;
|
||||
import com.reajason.javaweb.memshell.MemShellGenerator;
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
@@ -10,7 +10,6 @@ import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||
import com.reajason.javaweb.memshell.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import com.reajason.javaweb.memshell.packer.jar.JarPacker;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Base64;
|
||||
@@ -24,18 +23,18 @@ import java.util.Base64;
|
||||
@CrossOrigin("*")
|
||||
public class GeneratorController {
|
||||
@PostMapping
|
||||
public ResponseEntity<?> generate(@RequestBody GenerateRequest request) {
|
||||
public GenerateResponse generate(@RequestBody GenerateRequest request) {
|
||||
ShellConfig shellConfig = request.getShellConfig();
|
||||
ShellToolConfig shellToolConfig = request.parseShellToolConfig();
|
||||
InjectorConfig injectorConfig = request.getInjectorConfig();
|
||||
GenerateResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
|
||||
Packer packer = request.getPacker().getInstance();
|
||||
if (packer instanceof JarPacker) {
|
||||
return ResponseEntity.ok(new GenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult))));
|
||||
return new GenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult)));
|
||||
} else if (packer instanceof AggregatePacker) {
|
||||
return ResponseEntity.ok(new GenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult)));
|
||||
return new GenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult));
|
||||
} else {
|
||||
return ResponseEntity.ok(new GenerateResponse(generateResult, packer.pack(generateResult)));
|
||||
return new GenerateResponse(generateResult, packer.pack(generateResult));
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
+9
-12
@@ -15,17 +15,16 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@tanstack/router-plugin": "^1.114.32",
|
||||
"@types/node": "^22.13.17",
|
||||
"@types/node": "^22.14.0",
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-copy-to-clipboard": "^5.0.7",
|
||||
"@types/react-dom": "^19.1.1",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"rimraf": "^6.0.1",
|
||||
"tailwindcss": "^4.1.1",
|
||||
"typescript": "^5.8.2",
|
||||
"vite": "^6.2.4",
|
||||
"tailwindcss": "^4.1.3",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^6.2.5",
|
||||
"vite-bundle-visualizer": "^1.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -56,19 +55,15 @@
|
||||
"@radix-ui/react-toggle": "^1.1.2",
|
||||
"@radix-ui/react-toggle-group": "^1.1.2",
|
||||
"@radix-ui/react-tooltip": "^1.1.8",
|
||||
"@tailwindcss/vite": "^4.1.1",
|
||||
"@tanstack/react-query": "^5.71.3",
|
||||
"@tanstack/react-router": "^1.114.32",
|
||||
"@tanstack/router-devtools": "^1.114.32",
|
||||
"@tailwindcss/vite": "^4.1.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"embla-carousel-react": "^8.5.2",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"i18next": "^24.2.3",
|
||||
"input-otp": "^1.4.2",
|
||||
"lucide-react": "^0.487.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.1.0",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-day-picker": "9.6.4",
|
||||
@@ -76,8 +71,10 @@
|
||||
"react-hook-form": "^7.55.0",
|
||||
"react-i18next": "^15.4.1",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"react-router": "^7.5.0",
|
||||
"react-router-dom": "^7.5.0",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"recharts": "^2.15.1",
|
||||
"recharts": "^2.15.2",
|
||||
"sonner": "^2.0.3",
|
||||
"tailwind-merge": "^3.1.0",
|
||||
"tailwind-scrollbar": "^4.0.2",
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { LanguageSwitcher } from "@/components/language-switcher";
|
||||
import { ModeToggle } from "@/components/mode-toggle.tsx";
|
||||
import { ThemeProvider } from "@/components/theme-provider.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import VersionBadge from "@/components/version-badge";
|
||||
import { GitHubIcon } from "@/icon";
|
||||
import { Outlet, createRootRoute } from "@tanstack/react-router";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Outlet } from "react-router-dom";
|
||||
|
||||
export const Route = createRootRoute({
|
||||
component: RootComponent,
|
||||
});
|
||||
|
||||
function RootComponent() {
|
||||
export default function RootLayout() {
|
||||
return (
|
||||
<ThemeProvider defaultTheme="system" storageKey="vite-ui-theme">
|
||||
<Toaster />
|
||||
+5
-19
@@ -1,37 +1,23 @@
|
||||
import { RouterProvider, createRouter } from "@tanstack/react-router";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { routeTree } from "./routeTree.gen";
|
||||
import "./index.css";
|
||||
import { TailwindIndicator } from "@/components/tailwind-indicator.tsx";
|
||||
import { env } from "@/config.ts";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
import i18n from "./i18n/i18n";
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
const router = createRouter({
|
||||
routeTree,
|
||||
basepath: env.BASE_PATH,
|
||||
defaultPreload: "intent",
|
||||
});
|
||||
|
||||
declare module "@tanstack/react-router" {
|
||||
interface Register {
|
||||
router: typeof router;
|
||||
}
|
||||
}
|
||||
import { RouterProvider } from "react-router-dom";
|
||||
import { router } from "./router";
|
||||
import { QueryProvider } from "./providers/query-client-provider";
|
||||
|
||||
const rootElement = document.getElementById("app") as HTMLElement;
|
||||
|
||||
if (!rootElement.innerHTML) {
|
||||
const root = ReactDOM.createRoot(rootElement);
|
||||
root.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<QueryProvider>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<RouterProvider router={router} />
|
||||
{env.MODE !== "production" && <TailwindIndicator />}
|
||||
</I18nextProvider>
|
||||
</QueryClientProvider>,
|
||||
</QueryProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,41 +10,56 @@ import {
|
||||
ConfigResponseType,
|
||||
GenerateResponse,
|
||||
GenerateResult,
|
||||
MainConfig,
|
||||
PackerConfig,
|
||||
ServerConfig,
|
||||
ShellToolType,
|
||||
} from "@/types/shell.ts";
|
||||
import { transformToPostData } from "@/utils/transformer.ts";
|
||||
import { customValidation, transformToPostData } from "@/utils/transformer.ts";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { LoaderCircle, WandSparklesIcon } from "lucide-react";
|
||||
import { useState, useTransition } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
export const Route = createFileRoute("/")({
|
||||
component: IndexComponent,
|
||||
});
|
||||
export default function IndexPage() {
|
||||
const { data: serverConfig } = useQuery<ServerConfig>({
|
||||
queryKey: ["serverConfig"],
|
||||
queryFn: async () => {
|
||||
const response = await fetch(`${env.API_URL}/config/servers`);
|
||||
return await response.json();
|
||||
},
|
||||
});
|
||||
|
||||
function IndexComponent() {
|
||||
const { data } = useQuery<ConfigResponseType>({
|
||||
queryKey: ["config"],
|
||||
const { data: mainConfig } = useQuery<MainConfig>({
|
||||
queryKey: ["mainConfig"],
|
||||
queryFn: async () => {
|
||||
const response = await fetch(`${env.API_URL}/config`);
|
||||
return await response.json();
|
||||
},
|
||||
});
|
||||
|
||||
const { data: packerConfig } = useQuery<PackerConfig>({
|
||||
queryKey: ["packerConfig"],
|
||||
queryFn: async () => {
|
||||
const response = await fetch(`${env.API_URL}/config/packers`);
|
||||
return await response.json();
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useTranslation();
|
||||
const form = useForm<FormSchema>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
server: "",
|
||||
server: "Tomcat",
|
||||
targetJdkVersion: "50",
|
||||
debug: false,
|
||||
bypassJavaModule: false,
|
||||
shellClassName: "",
|
||||
shellTool: "Godzilla",
|
||||
shellType: "",
|
||||
shellType: "Listener",
|
||||
urlPattern: "/*",
|
||||
godzillaPass: "pass",
|
||||
godzillaKey: "key",
|
||||
@@ -64,40 +79,11 @@ function IndexComponent() {
|
||||
const [packMethod, setPackMethod] = useState<string>("");
|
||||
const [isActionPending, startTransition] = useTransition();
|
||||
|
||||
function customValidation(values: FormSchema) {
|
||||
if (values.shellType.endsWith("Servlet") && (values.urlPattern === "/*" || !values.urlPattern)) {
|
||||
toast.warning(t("tips.servletUrlPattern"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (values.shellType.endsWith("ControllerHandler") && (values.urlPattern === "/*" || !values.urlPattern)) {
|
||||
toast.warning(t("tips.controllerUrlPattern"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
(values.shellType === "HandlerMethod" || values.shellType === "HandlerFunction") &&
|
||||
(values.urlPattern === "/*" || !values.urlPattern)
|
||||
) {
|
||||
toast.warning(t("tips.handlerUrlPattern"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (values.shellTool === ShellToolType.Custom && !values.shellClassBase64) {
|
||||
toast.warning(t("tips.customShellClass"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function onSubmit(values: FormSchema) {
|
||||
const onSubmit = async (data: FormSchema) => {
|
||||
startTransition(async () => {
|
||||
if (!customValidation(values)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const postData = transformToPostData(values);
|
||||
try {
|
||||
customValidation(t, data);
|
||||
const postData = transformToPostData(data);
|
||||
const response = await fetch(`${env.API_URL}/generate`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -105,31 +91,30 @@ function IndexComponent() {
|
||||
},
|
||||
body: JSON.stringify(postData),
|
||||
});
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
if (response.ok) {
|
||||
const json: GenerateResponse = await response.json();
|
||||
setPackResult(json.packResult);
|
||||
setAllPackResults(json.allPackResults);
|
||||
setGenerateResult(json.generateResult);
|
||||
setPackMethod(values.packingMethod);
|
||||
toast.success(t("success.generated"));
|
||||
} else {
|
||||
|
||||
if (!response.ok) {
|
||||
const json: APIErrorResponse = await response.json();
|
||||
toast.error(t("errors.generationFailed", { error: json.error }));
|
||||
}
|
||||
} catch (err) {
|
||||
const error = err as Error;
|
||||
toast.error(t("errors.generationFailed", { error: error.message }));
|
||||
|
||||
const result = (await response.json()) as GenerateResponse;
|
||||
setGenerateResult(result.generateResult);
|
||||
setPackResult(result.packResult);
|
||||
setAllPackResults(result.allPackResults);
|
||||
setPackMethod(data.packingMethod);
|
||||
toast.success(t("success.generated"));
|
||||
} catch (error) {
|
||||
toast.error(t("errors.generationFailed", { error: (error as Error).message }));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col xl:flex-row gap-4 p-4">
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col xl:flex-row gap-4 p-4">
|
||||
<div className="w-full xl:w-1/2 space-y-4">
|
||||
<MainConfigCard servers={data?.servers} mainConfig={data?.core} form={form} />
|
||||
<PackageConfigCard packerConfig={data?.packers} form={form} />
|
||||
<MainConfigCard servers={serverConfig} mainConfig={mainConfig} form={form} />
|
||||
<PackageConfigCard packerConfig={packerConfig} form={form} />
|
||||
<Button className="w-full" type="submit" disabled={isActionPending}>
|
||||
{isActionPending ? <LoaderCircle className="animate-spin" /> : <WandSparklesIcon />}
|
||||
{t("buttons.generate")}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
retry: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export function QueryProvider({ children }: { children: ReactNode }) {
|
||||
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/* eslint-disable */
|
||||
|
||||
// @ts-nocheck
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
|
||||
// This file was automatically generated by TanStack Router.
|
||||
// You should NOT make any changes in this file as it will be overwritten.
|
||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||
|
||||
// Import Routes
|
||||
|
||||
import { Route as rootRoute } from "./routes/__root";
|
||||
import { Route as IndexImport } from "./routes/index";
|
||||
|
||||
// Create/Update Routes
|
||||
|
||||
const IndexRoute = IndexImport.update({
|
||||
id: "/",
|
||||
path: "/",
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any);
|
||||
|
||||
// Populate the FileRoutesByPath interface
|
||||
|
||||
declare module "@tanstack/react-router" {
|
||||
interface FileRoutesByPath {
|
||||
"/": {
|
||||
id: "/";
|
||||
path: "/";
|
||||
fullPath: "/";
|
||||
preLoaderRoute: typeof IndexImport;
|
||||
parentRoute: typeof rootRoute;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Create and export the route tree
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
"/": typeof IndexRoute;
|
||||
}
|
||||
|
||||
export interface FileRoutesByTo {
|
||||
"/": typeof IndexRoute;
|
||||
}
|
||||
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRoute;
|
||||
"/": typeof IndexRoute;
|
||||
}
|
||||
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath;
|
||||
fullPaths: "/";
|
||||
fileRoutesByTo: FileRoutesByTo;
|
||||
to: "/";
|
||||
id: "__root__" | "/";
|
||||
fileRoutesById: FileRoutesById;
|
||||
}
|
||||
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute;
|
||||
}
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
};
|
||||
|
||||
export const routeTree = rootRoute._addFileChildren(rootRouteChildren)._addFileTypes<FileRouteTypes>();
|
||||
|
||||
/* ROUTE_MANIFEST_START
|
||||
{
|
||||
"routes": {
|
||||
"__root__": {
|
||||
"filePath": "__root.tsx",
|
||||
"children": [
|
||||
"/"
|
||||
]
|
||||
},
|
||||
"/": {
|
||||
"filePath": "index.tsx"
|
||||
}
|
||||
}
|
||||
}
|
||||
ROUTE_MANIFEST_END */
|
||||
@@ -0,0 +1,22 @@
|
||||
import { createBrowserRouter } from "react-router-dom";
|
||||
import { env } from "@/config";
|
||||
import RootLayout from "@/components/layouts/root-layout";
|
||||
import IndexPage from "@/pages";
|
||||
|
||||
export const router = createBrowserRouter(
|
||||
[
|
||||
{
|
||||
path: "/",
|
||||
element: <RootLayout />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <IndexPage />,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
{
|
||||
basename: env.BASE_PATH,
|
||||
}
|
||||
);
|
||||
@@ -1,5 +1,27 @@
|
||||
import { FormSchema } from "@/types/schema.ts";
|
||||
import { InjectorConfig, ShellConfig, ShellToolConfig } from "@/types/shell.ts";
|
||||
import { InjectorConfig, ShellConfig, ShellToolConfig, ShellToolType } from "@/types/shell.ts";
|
||||
import { TFunction } from "i18next";
|
||||
|
||||
export function customValidation(t: TFunction<"translation", undefined>, values: FormSchema) {
|
||||
if (values.shellType.endsWith("Servlet") && (values.urlPattern === "/*" || !values.urlPattern)) {
|
||||
throw new Error(t("tips.servletUrlPattern"));
|
||||
}
|
||||
|
||||
if (values.shellType.endsWith("ControllerHandler") && (values.urlPattern === "/*" || !values.urlPattern)) {
|
||||
throw new Error(t("tips.controllerUrlPattern"));
|
||||
}
|
||||
|
||||
if (
|
||||
(values.shellType === "HandlerMethod" || values.shellType === "HandlerFunction") &&
|
||||
(values.urlPattern === "/*" || !values.urlPattern)
|
||||
) {
|
||||
throw new Error(t("tips.handlerUrlPattern"));
|
||||
}
|
||||
|
||||
if (values.shellTool === ShellToolType.Custom && !values.shellClassBase64) {
|
||||
throw new Error(t("tips.customShellClass"));
|
||||
}
|
||||
}
|
||||
|
||||
export function transformToPostData(formValue: FormSchema) {
|
||||
const shellConfig: ShellConfig = {
|
||||
|
||||
+1
-2
@@ -1,12 +1,11 @@
|
||||
import path from "node:path";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import { TanStackRouterVite } from "@tanstack/router-plugin/vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { defineConfig } from "vite";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [TanStackRouterVite({ autoCodeSplitting: true }), react(), tailwindcss()],
|
||||
plugins: [react(), tailwindcss()],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "./src"),
|
||||
|
||||
Reference in New Issue
Block a user