Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 26 additions & 9 deletions babel.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ function resolveAliasPath(relativeToBabelConf) {
export default function getBabelConfig(api) {
const baseConfig = getBaseConfig(api);

// Covers: docs prod build (NODE_ENV=production), package esm build (BABEL_ENV=stable),
// package cjs build (BABEL_ENV=node). Excludes docs dev, tests, coverage.
const isProductionBuild = api.env(['production', 'stable', 'node']);

const defaultAlias = {
'@mui/material': resolveAliasPath('./packages/mui-material/src'),
'@mui/internal-core-docs': resolveAliasPath('./packages-internal/core-docs/src'),
Expand All @@ -43,7 +47,7 @@ export default function getBabelConfig(api) {
};

/** @type {babel.PluginItem[]} */
const plugins = [
const prodOnlyPlugins = [
[
'@mui/internal-babel-plugin-minify-errors',
{
Expand All @@ -55,21 +59,34 @@ export default function getBabelConfig(api) {
],
];

const excludedBasePlugins = new Set([
'@mui/internal-babel-plugin-display-name',
// Inlining MUI_VERSION, etc only matters for shipped bundles.
// Dev reads process.env at runtime without needing substitution.
...(isProductionBuild ? [] : ['babel-plugin-transform-inline-environment-variables']),
]);

const basePlugins = (baseConfig.plugins || []).filter(
(/** @type {[unknown, unknown, string]} */ [, , pluginName]) =>
pluginName !== '@mui/internal-babel-plugin-display-name',
!excludedBasePlugins.has(pluginName),
);
basePlugins.push(...plugins);

if (isProductionBuild) {
basePlugins.push(...prodOnlyPlugins);
}

return {
...baseConfig,
plugins: basePlugins,
overrides: [
{
exclude: /\.test\.(m?js|ts|tsx)$/,
plugins: ['@babel/plugin-transform-react-constant-elements'],
},
],
// `@babel/plugin-transform-react-constant-elements` hoists static JSX — prod-only optimization.
overrides: isProductionBuild
? [
{
exclude: /\.test\.(m?js|ts|tsx)$/,
plugins: ['@babel/plugin-transform-react-constant-elements'],
},
]
: [],
env: {
development: {
plugins: [
Expand Down
157 changes: 117 additions & 40 deletions docs/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,76 @@ const workspaceRoot = path.join(currentDirectory, '../');
const pkgContent = fs.readFileSync(path.resolve(workspaceRoot, 'package.json'), 'utf8');
const pkg = JSON.parse(pkgContent);

// Shared alias list. Used by both turbopack (relative paths from `docs/`)
// and webpack (absolute paths via `path.resolve`).
const aliasEntries: ReadonlyArray<readonly [string, string]> = [
['@mui/material', 'packages/mui-material/src'],
['@mui/material/package.json', 'packages/mui-material/package.json'],
['@mui/internal-core-docs', 'packages-internal/core-docs/src'],
['@mui/icons-material', 'packages/mui-icons-material/lib/index.mjs'],
['@mui/lab', 'packages/mui-lab/src'],
['@mui/styled-engine', 'packages/mui-styled-engine/src'],
['@mui/system', 'packages/mui-system/src'],
['@mui/system/package.json', 'packages/mui-system/package.json'],
['@mui/private-theming', 'packages/mui-private-theming/src'],
['@mui/utils', 'packages/mui-utils/src'],
['@mui/material-nextjs', 'packages/mui-material-nextjs/src'],
];

const turbopackResolveAlias: Record<string, string> = {
...Object.fromEntries(aliasEntries.map(([name, rel]) => [name, `../${rel}`])),
// Mirrors the `docs` alias from babel.config.mjs / babel-plugin-module-resolver.
docs: '.',
};

const markdownLoaderBase = {
workspaceRoot,
languagesInProgress: [],
packages: [
{
productId: 'material-ui',
paths: [
path.join(workspaceRoot, 'packages/mui-lab/src'),
path.join(workspaceRoot, 'packages/mui-material/src'),
],
},
],
env: {
SOURCE_CODE_REPO: 'https://github.com/mui/material-ui',
LIB_VERSION: pkg.version,
},
};

export default withDocsInfra({
webpack: (config: NextConfig, options): NextConfig => {
turbopack: {
resolveAlias: turbopackResolveAlias,
resolveExtensions: ['.mjs', '.tsx', '.ts', '.jsx', '.js', '.json'],
rules: {
// Turbopack requires serializable loader options, so `ignoreLanguagePages`
// (a function) is omitted. Safe while docs is English-only in SSR.
'*.md': [
// `.md?muiMarkdown` → markdown loader (mirrors the webpack `oneOf` first branch).
{
condition: { query: /[?&]muiMarkdown(?=&|$)/ },
loaders: [{ loader: '@mui/internal-markdown/loader', options: markdownLoaderBase }],
as: '*.js',
},
// Non-muiMarkdown `.md` (e.g. `import terms from './terms.md'`) → raw source.
// `{ not: 'foreign' }` keeps raw-loader away from node_modules / Next.js internals.
{
condition: {
all: [{ not: 'foreign' }, { not: { query: /[?&]muiMarkdown(?=&|$)/ } }],
},
loaders: ['raw-loader'],
as: '*.js',
},
],
},
},
webpack: (
config: Parameters<NonNullable<NextConfig['webpack']>>[0],
options: Parameters<NonNullable<NextConfig['webpack']>>[1],
) => {
const plugins = config.plugins.slice();

if (process.env.DOCS_STATS_ENABLED && !options.isServer) {
Expand Down Expand Up @@ -75,39 +143,38 @@ export default withDocsInfra({
rule.resourceQuery = { not: [/raw/] };
});

// Webpack alias matching is order-sensitive (first match wins for prefix
// aliases), so list more specific paths (`@mui/material/package.json`)
// before broader ones (`@mui/material`). We sort by key length desc to
// guarantee this regardless of how `aliasEntries` is declared.
const sharedWebpackAliases = [...aliasEntries]
.sort(([a], [b]) => b.length - a.length)
.map(([name, rel]) => [name, path.resolve(workspaceRoot, rel)] as const);

const webpackAliases: Record<string, string> = {
// Exact-match overrides (with the `$` suffix) for bare imports.
// Pin `@mui/material` to its compiled `index.js` and `@mui/icons-material`
// to the ESM index so `import * as mui from '@mui/icons-material'`
// doesn't land on the CJS index and break namespace interop.
'@mui/material$': path.resolve(workspaceRoot, 'packages/mui-material/src/index.js'),
'@mui/icons-material$': path.resolve(
workspaceRoot,
'packages/mui-icons-material/lib/index.mjs',
),
...Object.fromEntries(sharedWebpackAliases),
// Bare `@mui/icons-material` should resolve to the lib dir (not the ESM
// index), so deep imports `@mui/icons-material/Add` work.
'@mui/icons-material': path.resolve(workspaceRoot, 'packages/mui-icons-material/lib'),
};

return {
...config,
plugins,
resolve: {
...config.resolve,
// resolve .tsx first
alias: {
...config.resolve.alias,

// for 3rd party packages with dependencies in this repository
'@mui/material$': path.resolve(workspaceRoot, 'packages/mui-material/src/index.js'),
'@mui/material/package.json': path.resolve(
workspaceRoot,
'packages/mui-material/package.json',
),
'@mui/material': path.resolve(workspaceRoot, 'packages/mui-material/src'),

'@mui/internal-core-docs': path.resolve(workspaceRoot, 'packages-internal/core-docs/src'),
'@mui/icons-material$': path.resolve(
workspaceRoot,
'packages/mui-icons-material/lib/index.mjs',
),
'@mui/icons-material': path.resolve(workspaceRoot, 'packages/mui-icons-material/lib'),
'@mui/lab': path.resolve(workspaceRoot, 'packages/mui-lab/src'),
'@mui/styled-engine': path.resolve(workspaceRoot, 'packages/mui-styled-engine/src'),
'@mui/system/package.json': path.resolve(
workspaceRoot,
'packages/mui-system/package.json',
),
'@mui/system': path.resolve(workspaceRoot, 'packages/mui-system/src'),
'@mui/private-theming': path.resolve(workspaceRoot, 'packages/mui-private-theming/src'),
'@mui/utils': path.resolve(workspaceRoot, 'packages/mui-utils/src'),
'@mui/material-nextjs': path.resolve(workspaceRoot, 'packages/mui-material-nextjs/src'),
...webpackAliases,
},
extensions: [
'.mjs',
Expand All @@ -131,18 +198,10 @@ export default withDocsInfra({
{
loader: require.resolve('@mui/internal-markdown/loader'),
options: {
workspaceRoot,
...markdownLoaderBase,
// Function form is allowed under webpack; turbopack
// requires serialisable options so it's omitted there.
ignoreLanguagePages: () => false,
languagesInProgress: [],
packages: [
{
productId: 'material-ui',
paths: [
path.join(workspaceRoot, 'packages/mui-lab/src'),
path.join(workspaceRoot, 'packages/mui-material/src'),
],
},
],
env: {
SOURCE_CODE_REPO: options.config.env.SOURCE_CODE_REPO,
LIB_VERSION: options.config.env.LIB_VERSION,
Expand All @@ -161,7 +220,12 @@ export default withDocsInfra({
{
test: /\.(js|mjs|tsx|ts)$/,
resourceQuery: { not: [/raw/] },
include: [workspaceRoot],
// Narrow the scope to fixed directories
include: [
path.join(workspaceRoot, 'docs'),
path.join(workspaceRoot, 'packages'),
path.join(workspaceRoot, 'packages-internal'),
],
exclude: /(node_modules|mui-icons-material)/,
use: options.defaultLoaders.babel,
},
Expand All @@ -187,7 +251,20 @@ export default withDocsInfra({
},
// Ensure CSS from the Data Grid packages is included in the build:
// https://github.com/mui/mui-x/issues/17427#issuecomment-2813967605
transpilePackages: ['@mui/x-data-grid', '@mui/x-data-grid-pro', '@mui/x-data-grid-premium'],
// `@mui/x-*` entries: keep their `@mui/material/*` subpath imports
// inside the bundler so the source aliases above win; otherwise resolution
// falls back to the pnpm symlink (→ `packages/mui-material/build/`), which
// is empty unless the package has been built.
transpilePackages: [
'@mui/x-charts',
'@mui/x-data-grid',
'@mui/x-data-grid-pro',
'@mui/x-data-grid-premium',
'@mui/x-tree-view',
'@mui/x-date-pickers',
'@mui/x-date-pickers-pro',
'@mui/x-data-grid-generator',
],
distDir: 'export',
// Next.js provides a `defaultPathMap` argument, we could simplify the logic.
// However, we don't in order to prevent any regression in the `findPages()` method.
Expand Down
4 changes: 0 additions & 4 deletions docs/nextConfigDocsInfra.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,6 @@ function withDocsInfra(nextConfig) {
: {}),
...nextConfig.experimental,
},
eslint: {
ignoreDuringBuilds: true,
...nextConfig.eslint,
},
typescript: {
// Motivated by https://github.com/vercel/next.js/issues/7687
ignoreBuildErrors: true,
Expand Down
7 changes: 4 additions & 3 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
"private": true,
"license": "MIT",
"scripts": {
"build": "rimraf ./export && cross-env NODE_ENV=production NODE_OPTIONS=--max_old_space_size=8192 next build && pnpm build-sw",
"build": "rimraf ./export && cross-env NODE_ENV=production NODE_OPTIONS=--max_old_space_size=8192 next build --webpack && pnpm build-sw",
"build:turbopack": "rimraf ./export && cross-env NODE_ENV=production NODE_OPTIONS=--max_old_space_size=8192 next build --turbopack && pnpm build-sw",
"build:clean": "rimraf .next && pnpm build",
"build-sw": "node ./scripts/buildServiceWorker.js",
"dev": "next dev",
"dev": "next dev --turbopack",
"deploy": "git fetch upstream master && git push -f material-ui-docs FETCH_HEAD:latest",
"icons": "rimraf --glob public/static/icons/* && node ./scripts/buildIcons.js",
"start": "serve ./export",
Expand Down Expand Up @@ -70,7 +71,7 @@
"lz-string": "^1.5.0",
"markdown-to-jsx": "^9.7.16",
"material-ui-popup-state": "^5.3.7",
"next": "^15.5.16",
"next": "^16.2.6",
"notistack": "3.0.2",
"nprogress": "^0.2.0",
"postcss": "^8.5.14",
Expand Down
14 changes: 11 additions & 3 deletions docs/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
DocsApp,
createGetInitialProps,
printConsoleBanner,
reportWebVitals,
reportWebVitals as _reportWebVitals,
} from '@mui/internal-core-docs/DocsApp';
import {
DEFAULT_DOCS_CONFIG,
Expand Down Expand Up @@ -36,11 +36,17 @@ import {
muiSvgWordmarkString,
} from '@mui/internal-core-docs/svgIcons';

import { fontClasses as _fontClasses } from '@mui/internal-core-docs/nextFonts';
import versionsJson from '../versions.json';
import '../public/static/components-gallery/base-theme.css';
import './global.css';

export { fontClasses } from '@mui/internal-core-docs/nextFonts';
// Workaround: turbopack's pages-router Custom App detection misfires when
// `_app.tsx` contains a re-export of an imported binding (either
// `export ... from`, or `import { x } from ...; export { x }`), causing
// the global CSS imports above to be rejected. Re-export as a fresh local
// binding to keep the file recognized as the Custom App.
export const fontClasses = _fontClasses;

// Remove the license warning from demonstration purposes
LicenseInfo.setLicenseKey(process.env.NEXT_PUBLIC_MUI_LICENSE!);
Expand Down Expand Up @@ -318,4 +324,6 @@ MyApp.getInitialProps = createGetInitialProps({
versions: versionsJson.versions,
});

export { reportWebVitals };
// See note above about turbopack re-export detection — wrap rather than
// `export { reportWebVitals }` so _app.tsx stays the Custom App.
export const reportWebVitals: typeof _reportWebVitals = (...args) => _reportWebVitals(...args);
2 changes: 1 addition & 1 deletion docs/pages/experiments/docs/data-grid-premium.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import jsonPageContent from './data-grid-premium.json';
import descriptions from './data-grid-premium-translation.json';

export default function Page() {
return <ApiPage descriptions={{ en: descriptions }} pageContent={jsonPageContent} />;
return <ApiPage descriptions={descriptions} pageContent={jsonPageContent} />;
}
10 changes: 3 additions & 7 deletions docs/pages/material-ui/api/accordion-actions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { ApiPage } from '@mui/internal-core-docs/ApiPage';
import { mapApiPageTranslations } from '@mui/internal-core-docs/mapApiPageTranslations';
import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations';
import translation from 'docs/translations/api-docs/accordion-actions/accordion-actions.json';
import jsonPageContent from './accordion-actions.json';

export default function Page(props) {
Expand All @@ -9,12 +10,7 @@ export default function Page(props) {
}

export async function getStaticProps() {
const req = require.context(
'docs/translations/api-docs/accordion-actions',
false,
/\.\/accordion-actions.*\.json$/,
);
const descriptions = mapApiPageTranslations(req);
const descriptions = mapApiPageTranslation(translation);

return { props: { descriptions } };
}
10 changes: 3 additions & 7 deletions docs/pages/material-ui/api/accordion-details.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { ApiPage } from '@mui/internal-core-docs/ApiPage';
import { mapApiPageTranslations } from '@mui/internal-core-docs/mapApiPageTranslations';
import { mapApiPageTranslation } from '@mui/internal-core-docs/mapApiPageTranslations';
import translation from 'docs/translations/api-docs/accordion-details/accordion-details.json';
import jsonPageContent from './accordion-details.json';

export default function Page(props) {
Expand All @@ -9,12 +10,7 @@ export default function Page(props) {
}

export async function getStaticProps() {
const req = require.context(
'docs/translations/api-docs/accordion-details',
false,
/\.\/accordion-details.*\.json$/,
);
const descriptions = mapApiPageTranslations(req);
const descriptions = mapApiPageTranslation(translation);

return { props: { descriptions } };
}
Loading
Loading