init project
Some checks failed
No response / noResponse (push) Has been cancelled
CI / Continuous releases (push) Has been cancelled
CI / test-dev (macos-latest) (push) Has been cancelled
CI / test-dev (ubuntu-latest) (push) Has been cancelled
CI / test-dev (windows-latest) (push) Has been cancelled
Maintenance / main (push) Has been cancelled
Scorecards supply-chain security / Scorecards analysis (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled

This commit is contained in:
how2ice
2025-12-12 14:26:25 +09:00
commit 005cf56baf
43188 changed files with 1079531 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
# @mui/material-nextjs
The official Material UI integration with Next.js. For the full documentation, visit [Next.js integration](https://mui.com/material-ui/guides/nextjs/) page.

View File

@@ -0,0 +1,76 @@
{
"name": "@mui/material-nextjs",
"version": "7.3.6",
"author": "MUI Team",
"description": "Collection of utilities for integration between Material UI and Next.js.",
"keywords": [
"react",
"next",
"material-ui"
],
"repository": {
"type": "git",
"url": "git+https://github.com/mui/material-ui.git",
"directory": "packages/mui-material-nextjs"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/mui/material-ui/issues"
},
"homepage": "https://mui.com/material-ui/guides/nextjs/",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"scripts": {
"build": "code-infra build",
"release": "pnpm build && pnpm publish",
"test": "pnpm --workspace-root test:unit --project \"*:@mui/material-nextjs\"",
"typescript": "tsc -p tsconfig.json",
"attw": "attw --pack ./build --exclude-entrypoints . esm modern --include-entrypoints v15-pagesRouter v15-appRouter"
},
"dependencies": {
"@babel/runtime": "^7.28.4"
},
"devDependencies": {
"@emotion/cache": "^11.14.0",
"@emotion/react": "^11.14.0",
"@emotion/server": "^11.11.0",
"@types/react": "^19.2.7",
"next": "^15.5.7",
"react": "^19.2.1"
},
"peerDependencies": {
"@emotion/cache": "^11.11.0",
"@emotion/react": "^11.11.4",
"@emotion/server": "^11.11.0",
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"next": "^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@emotion/cache": {
"optional": true
},
"@emotion/server": {
"optional": true
}
},
"sideEffects": false,
"publishConfig": {
"access": "public",
"directory": "build"
},
"engines": {
"node": ">=14.0.0"
},
"exports": {
"./v13-pagesRouter": "./src/v13-pagesRouter/index.ts",
"./*": {
"mui-src": "./src/*/index.ts"
}
}
}

View File

@@ -0,0 +1 @@
module.exports = require('next/compat/router');

View File

@@ -0,0 +1,124 @@
'use client';
import * as React from 'react';
import createCache, { EmotionCache, Options as OptionsOfCreateCache } from '@emotion/cache';
import { CacheProvider as DefaultCacheProvider } from '@emotion/react';
import { useServerInsertedHTML } from './nextNavigation.cjs';
import { useRouter as usePagesRouter } from '../nextCompatRouter.cjs';
export type AppRouterCacheProviderProps = {
/**
* These are the options passed to createCache() from 'import createCache from "@emotion/cache"'.
*/
options?: Partial<OptionsOfCreateCache> & {
/**
* If `true`, the generated styles are wrapped within `@layer mui`.
* This is useful if you want to override the Material UI's generated styles with different styling solution, like Tailwind CSS, plain CSS etc.
*/
enableCssLayer?: boolean;
};
/**
* By default <CacheProvider /> from 'import { CacheProvider } from "@emotion/react"'.
*/
CacheProvider?: React.ElementType<{ value: EmotionCache }>;
children: React.ReactNode;
};
/**
* Emotion works OK without this provider but it's recommended to use this provider to improve performance.
* Without it, Emotion will generate a new <style> tag during SSR for every component.
* See https://github.com/mui/material-ui/issues/26561#issuecomment-855286153 for why it's a problem.
*/
export default function AppRouterCacheProvider(props: AppRouterCacheProviderProps) {
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line react-hooks/rules-of-hooks
const router = usePagesRouter();
if (router) {
console.error(
[
'The App Router CacheProvider is not compatible with the Pages Router.',
'Please use the Pages Router CacheProvider from `@mui/material-ui-nextjs/vx-pagesRouter` instead.',
].join('\n'),
);
}
}
const { options, CacheProvider = DefaultCacheProvider, children } = props;
const [registry] = React.useState(() => {
const cache = createCache({ ...options, key: options?.key ?? 'mui' });
cache.compat = true;
const prevInsert = cache.insert;
let inserted: { name: string; isGlobal: boolean }[] = [];
// Override the insert method to support streaming SSR with flush().
cache.insert = (...args) => {
if (options?.enableCssLayer && !args[1].styles.match(/^@layer\s+[^{]*$/)) {
args[1].styles = `@layer mui {${args[1].styles}}`;
}
const [selector, serialized] = args;
if (cache.inserted[serialized.name] === undefined) {
inserted.push({
name: serialized.name,
isGlobal: !selector,
});
}
return prevInsert(...args);
};
const flush = () => {
const prevInserted = inserted;
inserted = [];
return prevInserted;
};
return { cache, flush };
});
useServerInsertedHTML(() => {
const inserted = registry.flush();
if (inserted.length === 0) {
return null;
}
let styles = '';
let dataEmotionAttribute = registry.cache.key;
const globals: {
name: string;
style: string;
}[] = [];
inserted.forEach(({ name, isGlobal }) => {
const style = registry.cache.inserted[name];
if (typeof style === 'string') {
if (isGlobal) {
globals.push({ name, style });
} else {
styles += style;
dataEmotionAttribute += ` ${name}`;
}
}
});
return (
<React.Fragment>
{globals.map(({ name, style }) => (
<style
nonce={options?.nonce}
key={name}
data-emotion={`${registry.cache.key}-global ${name}`}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style }}
/>
))}
{styles && (
<style
nonce={options?.nonce}
data-emotion={dataEmotionAttribute}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: styles }}
/>
)}
</React.Fragment>
);
});
return <CacheProvider value={registry.cache}>{children}</CacheProvider>;
}

View File

@@ -0,0 +1,2 @@
export { default as AppRouterCacheProvider } from './appRouterV13';
export * from './appRouterV13';

View File

@@ -0,0 +1 @@
module.exports = require('next/navigation');

View File

@@ -0,0 +1,34 @@
import createCache from '@emotion/cache';
const isBrowser = typeof document !== 'undefined';
// On the client side, Create a meta tag at the top of the <head> and set it as insertionPoint.
// This assures that MUI styles are loaded first.
// It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
export default function createEmotionCache(
options?: { enableCssLayer?: boolean } & Parameters<typeof createCache>[0],
) {
let insertionPoint;
if (isBrowser) {
const emotionInsertionPoint = document.querySelector<HTMLMetaElement>(
'meta[name="emotion-insertion-point"]',
);
insertionPoint = emotionInsertionPoint ?? undefined;
}
const { enableCssLayer, ...other } = options ?? {};
const emotionCache = createCache({ key: 'mui', insertionPoint, ...other });
if (enableCssLayer) {
const prevInsert = emotionCache.insert;
emotionCache.insert = (...args) => {
// ignore styles that contain layer order (`@layer a, b, c;` without `{`)
if (!args[1].styles.match(/^@layer\s+(?:[^{]*?)$/)) {
args[1].styles = `@layer mui {${args[1].styles}}`;
}
return prevInsert(...args);
};
}
return emotionCache;
}

View File

@@ -0,0 +1,3 @@
export * from './pagesRouterV13Document';
export * from './pagesRouterV13App';
export { default as createEmotionCache } from './createCache';

View File

@@ -0,0 +1 @@
module.exports = require('next/document');

View File

@@ -0,0 +1,29 @@
import * as React from 'react';
import { CacheProvider, EmotionCache } from '@emotion/react';
import createEmotionCache from './createCache';
import { useRouter as usePagesRouter } from '../nextCompatRouter.cjs';
export interface EmotionCacheProviderProps {
emotionCache?: EmotionCache;
}
const defaultEmotionCache = createEmotionCache();
export function AppCacheProvider({
emotionCache = defaultEmotionCache,
children,
}: React.PropsWithChildren<EmotionCacheProviderProps>) {
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line react-hooks/rules-of-hooks
const router = usePagesRouter();
if (!router) {
console.error(
[
'The Pages router CacheProvider is not compatible with the App router.',
'Please use the App Router CacheProvider from `@mui/material-ui-nextjs/vx-appRouter` instead.',
].join('n'),
);
}
}
return <CacheProvider value={emotionCache}>{children}</CacheProvider>;
}

View File

@@ -0,0 +1,127 @@
import * as React from 'react';
import { AppType } from 'next/app';
import { EmotionCache } from '@emotion/react';
import createEmotionServer from '@emotion/server/create-instance';
import type { DocumentContext, DocumentInitialProps } from 'next/document';
import nextDocument from './nextDocument.cjs';
import { EmotionCacheProviderProps } from './pagesRouterV13App';
import createEmotionCache from './createCache';
const Document = nextDocument.default || nextDocument;
interface Plugin {
enhanceApp: (
App: React.ComponentType<React.ComponentProps<AppType>>,
) => (props: any) => React.JSX.Element;
resolveProps: (initialProps: DocumentInitialProps) => Promise<DocumentInitialProps>;
}
/**
* A utility to compose multiple `getInitialProps` functions.
*/
export function createGetInitialProps(plugins: Plugin[]) {
return async function getInitialProps(ctx: DocumentContext) {
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => plugins.reduce((result, plugin) => plugin.enhanceApp(result), App),
});
const initialProps = await Document.getInitialProps(ctx);
const finalProps = await plugins.reduce(
async (result, plugin) => plugin.resolveProps(await result),
Promise.resolve(initialProps),
);
return finalProps;
};
}
export interface DocumentHeadTagsProps {
emotionStyleTags: React.ReactElement<unknown>[];
}
export function DocumentHeadTags(props: DocumentHeadTagsProps) {
return (
<React.Fragment>
<meta name="emotion-insertion-point" content="" />
{props.emotionStyleTags}
</React.Fragment>
);
}
// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with static-site generation (SSG).
export async function documentGetInitialProps(
ctx: DocumentContext,
options?: {
emotionCache?: EmotionCache;
plugins?: Plugin[];
},
) {
// Resolution order
//
// On the server:
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. document.getInitialProps
// 4. app.render
// 5. page.render
// 6. document.render
//
// On the server with error:
// 1. document.getInitialProps
// 2. app.render
// 3. page.render
// 4. document.render
//
// On the client
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. app.render
// 4. page.render
// You can consider sharing the same Emotion cache between all the SSR requests to speed up performance.
// However, be aware that it can have global side effects.
const cache = options?.emotionCache ?? createEmotionCache();
// The createEmotionServer has to be called directly after the cache creation due to the side effect of cache.compat = true,
// otherwise the <style> tag will not come with the HTML string from the server.
const { extractCriticalToChunks } = createEmotionServer(cache);
return createGetInitialProps([
{
enhanceApp: (
App: React.ComponentType<React.ComponentProps<AppType> & EmotionCacheProviderProps>,
) =>
function EnhanceApp(props) {
return <App emotionCache={cache} {...props} />;
},
resolveProps: async (initialProps) => {
const { styles } = extractCriticalToChunks(initialProps.html);
return {
...initialProps,
emotionStyleTags: styles.map((style) => {
if (!style.css.trim()) {
return null;
}
const isLayerOrderRule = style.css.startsWith('@layer') && !style.css.match(/\{.*\}/);
return (
<style
// If the style is a layer order rule, prefix with the cache key to let Emotion hydrate this node.
// Otherwise, Emotion will hydrate only the non-global styles and they will override the layer order rule.
data-emotion={`${isLayerOrderRule ? `${cache.key} ` : ''}${style.key} ${style.ids.join(' ')}`}
key={style.key}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style.css }}
nonce={cache.nonce}
/>
);
}),
};
},
},
...(options?.plugins ?? []),
])(ctx) as Promise<DocumentInitialProps & DocumentHeadTagsProps>;
}

View File

@@ -0,0 +1 @@
export * from '../v13-appRouter';

View File

@@ -0,0 +1 @@
export * from '../v13-pagesRouter';

View File

@@ -0,0 +1 @@
export * from '../v13-appRouter';

View File

@@ -0,0 +1 @@
export * from '../v13-pagesRouter';

View File

@@ -0,0 +1 @@
export * from '../v13-appRouter';

View File

@@ -0,0 +1 @@
export * from '../v13-pagesRouter';

View File

@@ -0,0 +1,15 @@
{
// This config is for emitting declarations (.d.ts) only
// Actual .ts source files are transpiled via babel
"extends": "./tsconfig.json",
"compilerOptions": {
"composite": true,
"declaration": true,
"noEmit": false,
"emitDeclarationOnly": true,
"outDir": "build/esm",
"rootDir": "./src"
},
"include": ["./src/**/*"],
"exclude": ["src/**/*.spec.*", "src/**/*.test.*"]
}

View File

@@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"types": ["react", "vitest/globals", "node", "next"]
},
"include": ["src/**/*"]
}

View File

@@ -0,0 +1,4 @@
// eslint-disable-next-line import/no-relative-packages
import sharedConfig from '../../vitest.shared.mts';
export default sharedConfig(import.meta.url, { jsdom: true });