init
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
.DS_Store
|
||||
/node_modules/
|
||||
|
||||
# React Router
|
||||
/.react-router/
|
||||
/build/
|
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
8
.idea/homepage.iml
generated
Normal file
8
.idea/homepage.iml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
46
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
46
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,46 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="HttpUrlsUsage" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="ignoredUrls">
|
||||
<list>
|
||||
<option value="http://localhost" />
|
||||
<option value="http://127.0.0.1" />
|
||||
<option value="http://0.0.0.0" />
|
||||
<option value="http://www.w3.org/" />
|
||||
<option value="http://json-schema.org/draft" />
|
||||
<option value="http://java.sun.com/" />
|
||||
<option value="http://xmlns.jcp.org/" />
|
||||
<option value="http://javafx.com/javafx/" />
|
||||
<option value="http://javafx.com/fxml" />
|
||||
<option value="http://maven.apache.org/xsd/" />
|
||||
<option value="http://maven.apache.org/POM/" />
|
||||
<option value="http://www.springframework.org/schema/" />
|
||||
<option value="http://www.springframework.org/tags" />
|
||||
<option value="http://www.springframework.org/security/tags" />
|
||||
<option value="http://www.thymeleaf.org" />
|
||||
<option value="http://www.jboss.org/j2ee/schema/" />
|
||||
<option value="http://www.jboss.com/xml/ns/" />
|
||||
<option value="http://www.ibm.com/webservices/xsd" />
|
||||
<option value="http://activemq.apache.org/schema/" />
|
||||
<option value="http://schema.cloudfoundry.org/spring/" />
|
||||
<option value="http://schemas.xmlsoap.org/" />
|
||||
<option value="http://cxf.apache.org/schemas/" />
|
||||
<option value="http://primefaces.org/ui" />
|
||||
<option value="http://tiles.apache.org/" />
|
||||
<option value="http://{hostname}" />
|
||||
<option value="http://www.example.com" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyShadowingBuiltinsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="ignoredNames">
|
||||
<list>
|
||||
<option value="id" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="TsLint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
6
.idea/jsLibraryMappings.xml
generated
Normal file
6
.idea/jsLibraryMappings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<includedPredefinedLibrary name="Node.js Core" />
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/homepage.iml" filepath="$PROJECT_DIR$/.idea/homepage.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
41
README.md
Normal file
41
README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Material UI - React Router example in TypeScript
|
||||
|
||||
## How to use
|
||||
|
||||
Download the example [or clone the repo](https://github.com/mui/material-ui):
|
||||
|
||||
<!-- #target-branch-reference -->
|
||||
|
||||
```bash
|
||||
curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-react-router-ts
|
||||
cd material-ui-react-router-ts
|
||||
```
|
||||
|
||||
Install it and run:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
or:
|
||||
|
||||
<!-- #target-branch-reference -->
|
||||
|
||||
[](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-react-router-ts)
|
||||
|
||||
[](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-react-router-ts)
|
||||
|
||||
## The idea behind the example
|
||||
|
||||
<!-- #host-reference -->
|
||||
|
||||
This example demonstrates how you can use Material UI with [React Router](https://reactrouter.com/) in [TypeScript](https://github.com/Microsoft/TypeScript).
|
||||
It includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI.
|
||||
|
||||
## What's next?
|
||||
|
||||
<!-- #host-reference -->
|
||||
|
||||
You now have a working example project.
|
||||
You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section.
|
21
app/components/Copyright.tsx
Normal file
21
app/components/Copyright.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import * as React from 'react';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import MuiLink from '@mui/material/Link';
|
||||
|
||||
export default function Copyright() {
|
||||
return (
|
||||
<Typography
|
||||
variant="body2"
|
||||
align="center"
|
||||
sx={{
|
||||
color: 'text.secondary',
|
||||
}}
|
||||
>
|
||||
{'Copyright © '}
|
||||
<MuiLink color="inherit" href="https://mui.com/">
|
||||
Your Website
|
||||
</MuiLink>{' '}
|
||||
{new Date().getFullYear()}.
|
||||
</Typography>
|
||||
);
|
||||
}
|
23
app/components/ProTip.tsx
Normal file
23
app/components/ProTip.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import * as React from 'react';
|
||||
import Link from '@mui/material/Link';
|
||||
import SvgIcon, { type SvgIconProps } from '@mui/material/SvgIcon';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
function LightBulbIcon(props: SvgIconProps) {
|
||||
return (
|
||||
<SvgIcon {...props}>
|
||||
<path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6C7.8 12.16 7 10.63 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z" />
|
||||
</SvgIcon>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ProTip() {
|
||||
return (
|
||||
<Typography sx={{ mt: 6, mb: 3, color: 'text.secondary' }}>
|
||||
<LightBulbIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
||||
{'Pro tip: See more '}
|
||||
<Link href="https://mui.com/material-ui/getting-started/templates/">templates</Link>
|
||||
{' in the Material UI documentation.'}
|
||||
</Typography>
|
||||
);
|
||||
}
|
15
app/createCache.ts
Normal file
15
app/createCache.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import createCache from '@emotion/cache';
|
||||
|
||||
export default function createEmotionCache(options?: Parameters<typeof createCache>[0]) {
|
||||
const emotionCache = createCache({ key: 'mui', ...options });
|
||||
const prevInsert = emotionCache.insert;
|
||||
emotionCache.insert = (...args) => {
|
||||
// ignore styles that contain layer order (`@layer ...` without `{`)
|
||||
if (!args[1].styles.match(/^@layer\s+[^{]*$/)) {
|
||||
args[1].styles = `@layer mui {${args[1].styles}}`;
|
||||
}
|
||||
return prevInsert(...args);
|
||||
};
|
||||
|
||||
return emotionCache;
|
||||
}
|
12
app/entry.client.tsx
Normal file
12
app/entry.client.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom/client';
|
||||
import { HydratedRouter } from 'react-router/dom';
|
||||
|
||||
React.startTransition(() => {
|
||||
ReactDOM.hydrateRoot(
|
||||
document,
|
||||
<React.StrictMode>
|
||||
<HydratedRouter />
|
||||
</React.StrictMode>,
|
||||
);
|
||||
});
|
101
app/entry.server.tsx
Normal file
101
app/entry.server.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import { Transform } from 'node:stream';
|
||||
|
||||
import * as React from 'react';
|
||||
import * as ReactDOMServer from 'react-dom/server';
|
||||
import type { EntryContext } from 'react-router';
|
||||
import { ServerRouter } from 'react-router';
|
||||
import { createReadableStreamFromReadable } from '@react-router/node';
|
||||
import { isbot } from 'isbot';
|
||||
import createEmotionServer from '@emotion/server/create-instance';
|
||||
import { CacheProvider } from '@emotion/react';
|
||||
import createEmotionCache from './createCache';
|
||||
|
||||
export const streamTimeout = 5_000;
|
||||
|
||||
export default function handleRequest(
|
||||
request: Request,
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
routerContext: EntryContext,
|
||||
) {
|
||||
const cache = createEmotionCache();
|
||||
const { extractCriticalToChunks, constructStyleTagsFromChunks } = createEmotionServer(cache);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let shellRendered = false;
|
||||
const userAgent = request.headers.get('user-agent');
|
||||
|
||||
// Ensure requests from bots and SPA Mode renders wait for all content to load before responding
|
||||
// https://react.dev/reference/react-dom/server/renderToPipeableStream#waiting-for-all-content-to-load-for-crawlers-and-static-generation
|
||||
const readyOption: keyof ReactDOMServer.RenderToPipeableStreamOptions =
|
||||
(userAgent && isbot(userAgent)) || routerContext.isSpaMode ? 'onAllReady' : 'onShellReady';
|
||||
|
||||
const { pipe, abort } = ReactDOMServer.renderToPipeableStream(
|
||||
<CacheProvider value={cache}>
|
||||
<ServerRouter context={routerContext} url={request.url} />
|
||||
</CacheProvider>,
|
||||
{
|
||||
[readyOption]() {
|
||||
shellRendered = true;
|
||||
|
||||
// Collect the HTML chunks
|
||||
const chunks: Buffer[] = [];
|
||||
|
||||
// Create transform stream to collect HTML and inject styles
|
||||
const transformStream = new Transform({
|
||||
transform(chunk, _encoding, callback) {
|
||||
// Collect chunks, don't pass them through yet
|
||||
chunks.push(chunk);
|
||||
callback();
|
||||
},
|
||||
flush(callback) {
|
||||
// Combine all chunks into HTML string
|
||||
const html = Buffer.concat(chunks).toString();
|
||||
|
||||
// Extract emotion styles from the collected HTML
|
||||
const styles = constructStyleTagsFromChunks(extractCriticalToChunks(html));
|
||||
|
||||
if (styles) {
|
||||
const injectedHtml = html.replace('</head>', `${styles}</head>`);
|
||||
this.push(injectedHtml);
|
||||
} else {
|
||||
this.push(html);
|
||||
}
|
||||
|
||||
callback();
|
||||
},
|
||||
});
|
||||
|
||||
const stream = createReadableStreamFromReadable(transformStream);
|
||||
|
||||
responseHeaders.set('Content-Type', 'text/html');
|
||||
|
||||
resolve(
|
||||
new Response(stream, {
|
||||
headers: responseHeaders,
|
||||
status: responseStatusCode,
|
||||
}),
|
||||
);
|
||||
|
||||
pipe(transformStream);
|
||||
},
|
||||
onShellError(error: unknown) {
|
||||
reject(error);
|
||||
},
|
||||
onError(error: unknown) {
|
||||
responseStatusCode = 500;
|
||||
// Log streaming rendering errors from inside the shell. Don't log
|
||||
// errors encountered during initial shell rendering since they'll
|
||||
// reject and get logged in handleDocumentRequest.
|
||||
if (shellRendered) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// Abort the rendering stream after the `streamTimeout` so it has time to
|
||||
// flush down the rejected boundaries
|
||||
setTimeout(abort, streamTimeout + 1000);
|
||||
});
|
||||
}
|
92
app/root.tsx
Normal file
92
app/root.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
isRouteErrorResponse,
|
||||
Links,
|
||||
Meta,
|
||||
Outlet,
|
||||
Scripts,
|
||||
ScrollRestoration,
|
||||
} from 'react-router';
|
||||
import { CacheProvider } from '@emotion/react';
|
||||
import Box from '@mui/material/Box';
|
||||
import AppTheme from './theme';
|
||||
import createEmotionCache from './createCache';
|
||||
|
||||
import type { Route } from './+types/root';
|
||||
|
||||
export const links: Route.LinksFunction = () => [
|
||||
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
|
||||
{
|
||||
rel: 'preconnect',
|
||||
href: 'https://fonts.gstatic.com',
|
||||
crossOrigin: 'anonymous',
|
||||
},
|
||||
{
|
||||
rel: 'stylesheet',
|
||||
href: 'https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap',
|
||||
},
|
||||
];
|
||||
|
||||
export function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<Meta />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
{children}
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
const cache = createEmotionCache();
|
||||
|
||||
export default function App() {
|
||||
if (typeof window !== 'undefined') {
|
||||
return (
|
||||
<CacheProvider value={cache}>
|
||||
<AppTheme>
|
||||
<Outlet />
|
||||
</AppTheme>
|
||||
</CacheProvider>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<AppTheme>
|
||||
<Outlet />
|
||||
</AppTheme>
|
||||
);
|
||||
}
|
||||
|
||||
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
|
||||
let message = 'Oops!';
|
||||
let details = 'An unexpected error occurred.';
|
||||
let stack: string | undefined;
|
||||
|
||||
if (isRouteErrorResponse(error)) {
|
||||
message = error.status === 404 ? '404' : 'Error';
|
||||
details =
|
||||
error.status === 404 ? 'The requested page could not be found.' : error.statusText || details;
|
||||
} else if (import.meta.env.DEV && error && error instanceof Error) {
|
||||
details = error.message;
|
||||
stack = error.stack;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box component="main" sx={{ pt: 8, p: 2, maxWidth: 'lg', mx: 'auto' }}>
|
||||
<h1>{message}</h1>
|
||||
<p>{details}</p>
|
||||
{stack && (
|
||||
<Box component="pre" sx={{ width: '100%', p: 2, overflowX: 'auto' }}>
|
||||
<code>{stack}</code>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
6
app/routes.ts
Normal file
6
app/routes.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { type RouteConfig, index, route } from '@react-router/dev/routes';
|
||||
|
||||
export default [
|
||||
index('routes/home.tsx'),
|
||||
route('/about', 'routes/about.tsx'),
|
||||
] satisfies RouteConfig;
|
45
app/routes/about.tsx
Normal file
45
app/routes/about.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import * as React from 'react';
|
||||
import Container from '@mui/material/Container';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
import { Link as ReactRouterLink } from 'react-router';
|
||||
import ProTip from '~/components/ProTip';
|
||||
import Copyright from '~/components/Copyright';
|
||||
|
||||
export function meta() {
|
||||
return [
|
||||
{ title: 'About' },
|
||||
{
|
||||
name: 'description',
|
||||
content: 'About the project',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export default function About() {
|
||||
return (
|
||||
<Container maxWidth="lg">
|
||||
<Box
|
||||
sx={{
|
||||
my: 4,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography variant="h4" component="h1" sx={{ mb: 2 }}>
|
||||
Material UI - Next.js example in TypeScript
|
||||
</Typography>
|
||||
<Box sx={{ maxWidth: 'sm' }}>
|
||||
<Button variant="contained" component={ReactRouterLink} to="/">
|
||||
Go to the home page
|
||||
</Button>
|
||||
</Box>
|
||||
<ProTip />
|
||||
<Copyright />
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
}
|
43
app/routes/home.tsx
Normal file
43
app/routes/home.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import * as React from 'react';
|
||||
import Container from '@mui/material/Container';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
import Link from '@mui/material/Link';
|
||||
import { Link as ReactRouterLink } from 'react-router';
|
||||
import ProTip from '~/components/ProTip';
|
||||
import Copyright from '~/components/Copyright';
|
||||
|
||||
export function meta() {
|
||||
return [
|
||||
{ title: 'Material UI - React Router example in TypeScript' },
|
||||
{
|
||||
name: 'description',
|
||||
content: 'Welcome to Material UI - React Router example in TypeScript!',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<Container maxWidth="lg">
|
||||
<Box
|
||||
sx={{
|
||||
my: 4,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography variant="h4" component="h1" sx={{ mb: 2 }}>
|
||||
Material UI - Next.js App Router example in TypeScript
|
||||
</Typography>
|
||||
<Link to="/about" color="secondary" component={ReactRouterLink}>
|
||||
Go to the about page
|
||||
</Link>
|
||||
<ProTip />
|
||||
<Copyright />
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
}
|
24
app/theme.tsx
Normal file
24
app/theme.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import * as React from 'react';
|
||||
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
|
||||
const theme = createTheme({
|
||||
cssVariables: true,
|
||||
colorSchemes: {
|
||||
light: true,
|
||||
dark: true,
|
||||
},
|
||||
});
|
||||
|
||||
interface AppThemeProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function AppTheme({ children }: AppThemeProps) {
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
4660
package-lock.json
generated
Normal file
4660
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
33
package.json
Normal file
33
package.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "material-ui-react-router-ts",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "react-router build",
|
||||
"dev": "react-router dev",
|
||||
"start": "react-router-serve ./build/server/index.js",
|
||||
"typecheck": "react-router typegen && tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/cache": "latest",
|
||||
"@emotion/react": "latest",
|
||||
"@emotion/server": "latest",
|
||||
"@emotion/styled": "latest",
|
||||
"@mui/material": "latest",
|
||||
"@react-router/node": "latest",
|
||||
"@react-router/serve": "latest",
|
||||
"isbot": "latest",
|
||||
"react": "latest",
|
||||
"react-dom": "latest",
|
||||
"react-router": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-router/dev": "latest",
|
||||
"@types/node": "latest",
|
||||
"@types/react": "latest",
|
||||
"@types/react-dom": "latest",
|
||||
"typescript": "latest",
|
||||
"vite": "latest",
|
||||
"vite-tsconfig-paths": "latest"
|
||||
}
|
||||
}
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
7
react-router.config.ts
Normal file
7
react-router.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { Config } from '@react-router/dev/config';
|
||||
|
||||
export default {
|
||||
// Config options...
|
||||
// Server-side render by default, to enable SPA mode set this to `false`
|
||||
ssr: true,
|
||||
} satisfies Config;
|
22
tsconfig.json
Normal file
22
tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"include": ["**/*", "**/.server/**/*", "**/.client/**/*", ".react-router/types/**/*"],
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
||||
"types": ["node", "vite/client"],
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "bundler",
|
||||
"jsx": "react-jsx",
|
||||
"rootDirs": [".", "./.react-router/types"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./app/*"]
|
||||
},
|
||||
"esModuleInterop": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true
|
||||
}
|
||||
}
|
16
vite.config.ts
Normal file
16
vite.config.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { reactRouter } from '@react-router/dev/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [reactRouter(), tsconfigPaths()],
|
||||
ssr: {
|
||||
// Workaround for resolving dependencies in the server bundle
|
||||
// Without this, the React context will be different between direct import and transitive imports in development environment
|
||||
// For more information, see https://github.com/mui/material-ui/issues/45878#issuecomment-2987441663
|
||||
optimizeDeps: {
|
||||
include: ['@emotion/*', '@mui/*'],
|
||||
},
|
||||
noExternal: ['@emotion/*', '@mui/*'],
|
||||
},
|
||||
});
|
Reference in New Issue
Block a user