Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8eb79a0
Initial setup for minified error messages
Janpot Feb 18, 2025
e355a4d
fix interpollation
Janpot Feb 18, 2025
fb2f0e6
cleaner
Janpot Feb 18, 2025
e605606
codeblock
Janpot Feb 18, 2025
f6635c0
improvements
Janpot Feb 19, 2025
d180a4f
Update createPackageManifest.mts
Janpot Feb 19, 2025
d5a840f
Update createPackageManifest.mts
Janpot Feb 19, 2025
9c54353
fix export
Janpot Feb 19, 2025
cb39f96
unnecessary plugin
Janpot Feb 19, 2025
7d6b1e1
Update .markdownlint-cli2.cjs
Janpot Feb 19, 2025
d6ae187
types
Janpot Feb 19, 2025
b3be462
fix reportBrokenLinks
Janpot Feb 19, 2025
e17fb21
fix mdlint
Janpot Feb 19, 2025
7994f59
Update reportBrokenLinks.mts
Janpot Feb 19, 2025
7e31f0a
explainer
Janpot Feb 19, 2025
491b354
Update PageContent.mdx
Janpot Feb 19, 2025
7af37ce
Let's build this resolution in the babel plugin
Janpot Feb 20, 2025
b465e67
fix imports
Janpot Feb 20, 2025
05f89ec
Merge remote-tracking branch 'upstream/master' into setup-minify-erro…
Janpot Feb 20, 2025
82ab7de
Make it opt-out
Janpot Feb 20, 2025
86ff537
extension handling
Janpot Feb 26, 2025
518741b
Update docs/src/app/(public)/(content)/production-error/[code]/ErrorD…
Janpot Mar 19, 2025
247f1d9
Update packages/react/src/utils/formatErrorMessage.ts
Janpot Mar 20, 2025
8fa10ed
Update docs/src/app/(public)/(content)/production-error/[code]/PageCo…
Janpot Mar 20, 2025
16c2407
Merge remote-tracking branch 'upstream/master' into setup-minify-erro…
Janpot May 29, 2025
cf60053
Update pnpm-lock.yaml
Janpot May 29, 2025
b452a4f
Update babel.config.js
Janpot May 29, 2025
8e6427d
Update error-codes.json
Janpot May 29, 2025
502addb
Merge remote-tracking branch 'upstream/master' into setup-minify-erro…
Janpot Jun 4, 2025
b3a7f30
update
Janpot Jun 4, 2025
1b6dc12
[infra] Migrate error code extraction to code-infra
brijeshb42 Sep 5, 2025
ce5a7f8
Merge remote-tracking branch 'upstream/master' into setup-minify-erro…
Janpot Sep 5, 2025
42f2ca4
Merge branch 'error-code-extraction' into setup-minify-error-messages
Janpot Sep 5, 2025
6512821
Update pnpm-lock.yaml
Janpot Sep 5, 2025
b0c76d2
Update pnpm-lock.yaml
Janpot Sep 5, 2025
eca3e92
update
Janpot Sep 5, 2025
9f78051
Update docs/README.md
Janpot Sep 30, 2025
b3c0876
Update docs/README.md
Janpot Sep 30, 2025
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
10 changes: 0 additions & 10 deletions .markdownlint-cli2.cjs

This file was deleted.

11 changes: 11 additions & 0 deletions .markdownlint-cli2.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createBaseConfig } from '@mui/internal-code-infra/markdownlint';

const baseConfig = createBaseConfig();

export default {
...baseConfig,
config: {
...baseConfig.config,
MD038: false, // false poisitives in MDX
},
};
14 changes: 6 additions & 8 deletions babel.config.mjs
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import getBaseConfig from '@mui/internal-code-infra/babel-config';

const errorCodesPath = new URL(import.meta.resolve('./docs/public/static/error-codes.json'))
.pathname;
const missingError = process.env.MUI_EXTRACT_ERROR_CODES === 'true' ? 'write' : 'annotate';
const errorCodesPath = new URL(import.meta.resolve('docs/src/error-codes.json')).pathname;

export default function getBabelConfig(api) {
const baseConfig = getBaseConfig(api);

const plugins = [
[
'babel-plugin-macros',
'@mui/internal-babel-plugin-minify-errors',
{
muiError: {
errorCodesPath,
missingError,
},
missingError: 'annotate',
errorCodesPath,
runtimeModule: '#formatErrorMessage',
detection: 'opt-out',
},
],
];
Expand Down
17 changes: 17 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,20 @@ Package managers other than pnpm (like npm or Yarn) are not supported and will n

[You can follow this guide](https://github.com/mui/base-ui/blob/HEAD/CONTRIBUTING.md)
on how to get started contributing to Base UI.

## Error code extraction

Errors in production are minified. They are extracted out of the source code by running the command

```bash
pnpm extract-error-codes
```

This updates the `./src/error-codes.json` file with the newly extracted errors.

Important: If you just altered the text of an error, you are allowed to update the existing error code with the new text in `./src/error-codes.json`, but only under the following conditions:

1. There hasn't been an update to the semantic meaning of the error message. Error codes need to outlive Base UI versions, so the same code must mean the same thing across versions.
2. There hasn't been a change in parameters, no added and no removed.

In both of those cases, always create a new error code lline in `./src/error-codes.json`.
1 change: 0 additions & 1 deletion docs/public/static/error-codes.json

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use client';
import * as React from 'react';
import { useSearchParams } from 'next/navigation';

export interface ErrorDisplayProps {
msg: string;
}

function ErrorMessageWithArgs({ msg }: ErrorDisplayProps) {
const searchParams = useSearchParams();
return React.useMemo(() => {
const args = searchParams.getAll('args[]');
let index = 0;
return msg.replace(/%s/g, () => {
const replacement = args[index];
index += 1;
return replacement === undefined ? '[missing argument]' : replacement;
});
}, [msg, searchParams]);
}

/**
* Client component that interpolates arguments in an error message. Must be
* a client component because it reads the search params.
*/
export default function ErrorDisplay({ msg }: ErrorDisplayProps) {
const fallbackMsg = React.useMemo(() => msg.replace(/%s/g, '…'), [msg]);

return (
<code>
<React.Suspense fallback={fallbackMsg}>
<ErrorMessageWithArgs msg={msg} />
</React.Suspense>
</code>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Production error

{/**
@typedef Props
@property {string} msg The raw error message.
**/}

<Subtitle>Explanation for minified production error message.</Subtitle>
<Meta
name="description"
content="In the production build, error messages are minified to keep your application lightweight."
/>

A minified Base UI error occurred in the production build of React.

The full error message:

<ErrorDisplay msg={props.msg} />
28 changes: 28 additions & 0 deletions docs/src/app/(public)/(content)/production-error/[code]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from 'react';
import { notFound } from 'next/navigation';
import codes from 'docs/src/error-codes.json';
import PageContent from './PageContent.mdx';
import ErrorDisplay from './ErrorDisplay';

export const dynamicParams = false;

export async function generateStaticParams() {
return Object.keys(codes).map((code) => ({ code }));
}

export default async function ProductionError(props: { params: Promise<{ code: string }> }) {
const params = await props.params;
const code = Number(params.code);

if (Number.isNaN(code)) {
notFound();
}

const msg = (codes as Partial<Record<string, string>>)[code];

if (!msg) {
notFound();
}

return <PageContent components={{ ErrorDisplay }} msg={msg} />;
}
75 changes: 75 additions & 0 deletions docs/src/error-codes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"1": "Base UI: Cannot call an event handler while rendering.",
"2": "Unsupported number of selectors",
"3": "Unsupported number of arguments",
"4": "Base UI: CheckboxGroupContext is missing. CheckboxGroup parts must be placed within <CheckboxGroup>.",
"5": "Base UI: DirectionContext is missing.",
"6": "Base UI: MenubarContext is missing. Menubar parts must be placed within <Menubar>.",
"7": "Base UI: useToast must be used within <Toast.Provider>.",
"8": "Base UI: ToggleGroupContext is missing. ToggleGroup parts must be placed within <ToggleGroup>.",
"9": "Base UI: Render element or function are not defined.",
"10": "Base UI: AccordionItemContext is missing. Accordion parts must be placed within <Accordion.Item>.",
"11": "Base UI: AccordionRootContext is missing. Accordion parts must be placed within <Accordion.Root>.",
"12": "Base UI: <AlertDialog.Portal> is missing.",
"13": "Base UI: AlertDialogRootContext is missing. AlertDialog parts must be placed within <AlertDialog.Root>.",
"14": "Base UI: AvatarRootContext is missing. Avatar parts must be placed within <Avatar.Root>.",
"15": "Base UI: CheckboxRootContext is missing. Checkbox parts must be placed within <Checkbox.Root>.",
"16": "Base UI: CollapsibleRootContext is missing. Collapsible parts must be placed within <Collapsible.Root>.",
"17": "Base UI: CompositeRootContext is missing. Composite parts must be placed within <Composite.Root>.",
"18": "useComboboxChipContext must be used within a ComboboxChip",
"19": "Base UI: ComboboxGroupContext is missing. ComboboxGroup parts must be placed within <Combobox.Group>.",
"20": "Base UI: ComboboxItemContext is missing. ComboboxItem parts must be placed within <Combobox.Item>.",
"21": "Base UI: <Combobox.Portal> is missing.",
"22": "Base UI: <Combobox.Popup> and <Combobox.Arrow> must be used within the <Combobox.Positioner> component",
"23": "Base UI: ComboboxRootContext is missing. Combobox parts must be placed within <Combobox.Root>.",
"24": "Base UI: ComboboxFloatingContext is missing. Combobox parts must be placed within <Combobox.Root>.",
"25": "Base UI: ComboboxItemsContext is missing. Combobox parts must be placed within <Combobox.Root>.",
"26": "Base UI: ContextMenuRootContext is missing. ContextMenu parts must be placed within <ContextMenu.Root>.",
"27": "Base UI: <Dialog.Portal> is missing.",
"28": "Base UI: DialogRootContext is missing. Dialog parts must be placed within <Dialog.Root>.",
"29": "Base UI: FieldRootContext is missing. Field parts must be placed within <Field.Root>.",
"30": "[Floating UI]: Invalid grid - item width at index %s is greater than grid columns",
"31": "Base UI: MenuCheckboxItemContext is missing. MenuCheckboxItem parts must be placed within <Menu.CheckboxItem>.",
"32": "Base UI: MenuGroupRootContext is missing. Menu group parts must be used within <Menu.Group>.",
"33": "Base UI: <Menu.Portal> is missing.",
"34": "Base UI: MenuPositionerContext is missing. MenuPositioner parts must be placed within <Menu.Positioner>.",
"35": "Base UI: MenuRadioGroupContext is missing. MenuRadioGroup parts must be placed within <Menu.RadioGroup>.",
"36": "Base UI: MenuRadioItemContext is missing. MenuRadioItem parts must be placed within <Menu.RadioItem>.",
"37": "Base UI: MenuRootContext is missing. Menu parts must be placed within <Menu.Root>.",
"38": "Base UI: <Menu.SubmenuTrigger> must be placed in <Menu.SubmenuRoot>.",
"39": "Base UI: MeterRootContext is missing. Meter parts must be placed within <Meter.Root>.",
"40": "Base UI: NavigationMenuItem parts must be used within a <NavigationMenu.Item>.",
"41": "Base UI: <NavigationMenu.Portal> is missing.",
"42": "Base UI: NavigationMenuPositionerContext is missing. NavigationMenuPositioner parts must be placed within <NavigationMenu.Positioner>.",
"43": "Base UI: NavigationMenuRootContext is missing. Navigation Menu parts must be placed within <NavigationMenu.Root>.",
"44": "Base UI: NumberFieldRootContext is missing. NumberField parts must be placed within <NumberField.Root>.",
"45": "Base UI: NumberFieldScrubAreaContext is missing. NumberFieldScrubArea parts must be placed within <NumberField.ScrubArea>.",
"46": "Base UI: <Popover.Portal> is missing.",
"47": "Base UI: PopoverPositionerContext is missing. PopoverPositioner parts must be placed within <Popover.Positioner>.",
"48": "Base UI: PopoverRootContext is missing. Popover parts must be placed within <Popover.Root>.",
"49": "Base UI: <PreviewCard.Portal> is missing.",
"50": "Base UI: <PreviewCard.Popup> and <PreviewCard.Arrow> must be used within the <PreviewCard.Positioner> component",
"51": "Base UI: PreviewCardRootContext is missing. PreviewCard parts must be placed within <PreviewCard.Root>.",
"52": "Base UI: ProgressRootContext is missing. Progress parts must be placed within <Progress.Root>.",
"53": "Base UI: RadioRootContext is missing. Radio parts must be placed within <Radio.Root>.",
"54": "Base UI: ScrollAreaRootContext is missing. ScrollArea parts must be placed within <ScrollArea.Root>.",
"55": "Base UI: ScrollAreaScrollbarContext is missing. ScrollAreaScrollbar parts must be placed within <ScrollArea.Scrollbar>.",
"56": "Base UI: ScrollAreaViewportContext missing. ScrollAreaViewport parts must be placed within <ScrollArea.Viewport>.",
"57": "Base UI: SelectGroupContext is missing. SelectGroup parts must be placed within <Select.Group>.",
"58": "Base UI: SelectItemContext is missing. SelectItem parts must be placed within <Select.Item>.",
"59": "Base UI: <Select.Portal> is missing.",
"60": "Base UI: SelectPositionerContext is missing. SelectPositioner parts must be placed within <Select.Positioner>.",
"61": "Base UI: SelectRootContext is missing. Select parts must be placed within <Select.Root>.",
"62": "Base UI: SelectFloatingContext is missing. Select parts must be placed within <Select.Root>.",
"63": "Base UI: SliderRootContext is missing. Slider parts must be placed within <Slider.Root>.",
"64": "Base UI: SwitchRootContext is missing. Switch parts must be placed within <Switch.Root>.",
"65": "Base UI: TabsListContext is missing. TabsList parts must be placed within <Tabs.List>.",
"66": "Base UI: TabsRootContext is missing. Tabs parts must be placed within <Tabs.Root>.",
"67": "Base UI: ToastRootContext is missing. Toast parts must be used within <Toast.Root>.",
"68": "Base UI: ToastViewportContext is missing. Toast parts must be placed within <Toast.Viewport>.",
"69": "Base UI: ToolbarGroupContext is missing. ToolbarGroup parts must be placed within <Toolbar.Group>.",
"70": "Base UI: ToolbarRootContext is missing. Toolbar parts must be placed within <Toolbar.Root>.",
"71": "Base UI: <Tooltip.Portal> is missing.",
"72": "Base UI: TooltipPositionerContext is missing. TooltipPositioner parts must be placed within <Tooltip.Positioner>.",
"73": "Base UI: TooltipRootContext is missing. Tooltip parts must be placed within <Tooltip.Root>."
}
5 changes: 5 additions & 0 deletions docs/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@

declare module 'gtag.js';
declare module '@mui/monorepo/docs/nextConfigDocsInfra.js';

declare module '*.mdx' {
const MDXComponent: (props) => JSX.Element;
export default MDXComponent;
}
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"docs:start": "pnpm --filter docs serve",
"docs:link-check": "pnpm --filter docs link-check",
"docs:generate-llms": "pnpm --filter docs run generate-llms",
"extract-error-codes": "cross-env MUI_EXTRACT_ERROR_CODES=true lerna run --concurrency 1 build:stable",
"extract-error-codes": "code-infra extract-error-codes --errorCodesPath docs/src/error-codes.json --detection opt-out",
"jsonlint": "code-infra jsonlint",
"eslint": "eslint . --cache --report-unused-disable-directives --ext .js,.ts,.tsx,.mts --max-warnings 0 && pnpm -r lint",
"eslint:ci": "eslint . --report-unused-disable-directives --ext .js,.ts,.tsx,.mts --max-warnings 0 && pnpm -r lint",
Expand Down Expand Up @@ -65,7 +65,8 @@
"@babel/node": "^7.28.0",
"@babel/plugin-transform-react-constant-elements": "^7.27.1",
"@base-ui-components/monorepo-tests": "workspace:*",
"@mui/internal-code-infra": "^0.0.3-canary.3",
"@mui/internal-babel-plugin-minify-errors": "https://pkg.pr.new/mui/mui-public/@mui/internal-babel-plugin-minify-errors@405bc3a",
"@mui/internal-code-infra": "https://pkg.pr.new/mui/mui-public/@mui/internal-code-infra@405bc3a",
"@mui/internal-docs-utils": "^2.0.1",
"@mui/internal-markdown": "^2.0.7",
"@mui/internal-scripts": "^2.0.10",
Expand All @@ -85,7 +86,6 @@
"@vitest/ui": "3.2.4",
"@vvago/vale": "^3.12.0",
"babel-loader": "^10.0.0",
"babel-plugin-macros": "^3.1.0",
"babel-plugin-module-resolver": "^5.0.2",
"babel-plugin-react-remove-properties": "^0.3.0",
"chai-dom": "^1.12.1",
Expand Down
3 changes: 2 additions & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@
"./use-render": "./src/use-render/index.ts"
},
"imports": {
"#test-utils": "./test/index.ts"
"#test-utils": "./test/index.ts",
"#formatErrorMessage": "@base-ui-components/utils/formatErrorMessage"
},
"type": "commonjs",
"scripts": {
Expand Down
3 changes: 3 additions & 0 deletions packages/utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
"./store": "./src/store/index.ts",
"./*": "./src/*.ts"
},
"imports": {
"#formatErrorMessage": "./src/formatErrorMessage.ts"
},
"type": "commonjs",
"scripts": {
"prebuild": "rimraf --glob build build-tests \"*.tsbuildinfo\"",
Expand Down
14 changes: 14 additions & 0 deletions packages/utils/src/formatErrorMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* WARNING: Don't import this directly. It's imported by the code generated by
* `@mui/interal-babel-plugin-minify-errors`. Make sure to always use string literals in `Error`
* constructors to ensure the plugin works as expected. Supported patterns include:
* throw new Error('My message');
* throw new Error(`My message: ${foo}`);
* throw new Error(`My message: ${foo}` + 'another string');
* ...
*/
export default function formatErrorMessage(code: number, ...args: string[]): string {
const url = new URL(`https://base-ui.com/production-error/${code}`);
args.forEach((arg) => url.searchParams.append('args[]', arg));
return `Base UI error #${code}; visit ${url} for the full message.`;
}
4 changes: 2 additions & 2 deletions packages/utils/src/store/createSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export const createSelector = ((
} else if (a) {
selector = a;
} else {
throw new Error('Missing arguments');
throw /* minify-error-disabled */ new Error('Missing arguments');
}

return selector;
Expand Down Expand Up @@ -206,7 +206,7 @@ export const createSelectorMemoized: CreateSelectorFunction = (...selectors: any
case 2: return fn(state, a1, a2);
case 3: return fn(state, a1, a2, a3);
default:
throw new Error('unreachable');
throw /* minify-error-disabled */ new Error('unreachable');
}
};

Expand Down
Loading