Skip to content

Commit

Permalink
Merge branch 'master' into release/v6.4.1
Browse files Browse the repository at this point in the history
  • Loading branch information
mnajdova committed Jan 21, 2025
2 parents 56e25cd + 4f872d6 commit b59f577
Show file tree
Hide file tree
Showing 22 changed files with 98 additions and 2 deletions.
15 changes: 15 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,21 @@ module.exports = /** @type {Config} */ ({
'react/prop-types': 'off',
},
},
{
files: ['packages/*/src/*/*.?(c|m)[jt]s?(x)'],
excludedFiles: [
'*.spec.*',
'*.test.*',
// deprecated library
'**/mui-base/**/*',
'**/mui-joy/**/*',
// used internally, not used on app router yet
'**/mui-docs/**/*',
],
rules: {
'material-ui/disallow-react-api-in-server-components': 'error',
},
},
{
files: ['packages/*/src/**/*.?(c|m)[jt]s?(x)'],
excludedFiles: '*.spec.*',
Expand Down
1 change: 1 addition & 0 deletions packages/eslint-plugin-material-ui/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ module.exports.rules = {
'no-empty-box': require('./rules/no-empty-box'),
'no-styled-box': require('./rules/no-styled-box'),
'straight-quotes': require('./rules/straight-quotes'),
'disallow-react-api-in-server-components': require('./rules/disallow-react-api-in-server-components'),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
module.exports = {
create(context) {
let hasUseClientDirective = false;
const apis = new Set([
'useState',
'useEffect',
'useLayoutEffect',
'useReducer',
'useTransition',
'createContext',
]);
return {
Program(node) {
hasUseClientDirective = node.body.some(
(statement) =>
statement.type === 'ExpressionStatement' &&
statement.expression.type === 'Literal' &&
statement.expression.value === 'use client',
);
},
CallExpression(node) {
if (
!hasUseClientDirective &&
node.callee.type === 'MemberExpression' &&
node.callee.object.name === 'React' &&
apis.has(node.callee.property.name)
) {
context.report({
node,
message: `Using 'React.${node.callee.property.name}' is forbidden if the file doesn't have a 'use client' directive.`,
fix(fixer) {
const sourceCode = context.getSourceCode();
if (
sourceCode.text.includes('"use server"') ||
sourceCode.text.includes("'use server'")
) {
return null;
}

const firstToken = sourceCode.ast.body[0];
return fixer.insertTextBefore(firstToken, "'use client';\n");
},
});
}
},
};
},
meta: {
fixable: 'code',
},
};
1 change: 1 addition & 0 deletions packages/mui-lab/src/Timeline/TimelineContext.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';

type ButtonPositionClassName = string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';
import type { ButtonGroupProps } from './ButtonGroup';

Expand Down
1 change: 1 addition & 0 deletions packages/mui-material/src/Dialog/DialogContext.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';

interface DialogContextValue {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';
import type { FormControlProps } from './FormControl';

Expand Down
1 change: 1 addition & 0 deletions packages/mui-material/src/Hidden/withWidth.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import getDisplayName from '@mui/utils/getDisplayName';
Expand Down
1 change: 1 addition & 0 deletions packages/mui-material/src/RadioGroup/RadioGroupContext.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';

export interface RadioGroupContextValue {
Expand Down
1 change: 1 addition & 0 deletions packages/mui-material/src/Step/StepContext.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';

export interface StepContextType {
Expand Down
1 change: 1 addition & 0 deletions packages/mui-material/src/Stepper/StepperContext.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';

export interface StepperContextType {
Expand Down
1 change: 1 addition & 0 deletions packages/mui-material/src/Table/Tablelvl2Context.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';

type ToggleButtonPositionClassName = string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';
import type { ToggleButtonGroupProps } from './ToggleButtonGroup';

Expand Down
1 change: 1 addition & 0 deletions packages/mui-private-theming/src/useTheme/ThemeContext.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';

const ThemeContext = React.createContext(null);
Expand Down
1 change: 1 addition & 0 deletions packages/mui-styles/src/StylesProvider/StylesProvider.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import { exactProp } from '@mui/utils';
Expand Down
1 change: 1 addition & 0 deletions packages/mui-styles/src/makeStyles/makeStyles.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';
import { getDynamicStyles } from 'jss';
import mergeClasses from '../mergeClasses';
Expand Down
1 change: 1 addition & 0 deletions packages/mui-system/src/RtlProvider/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';

Expand Down
1 change: 1 addition & 0 deletions packages/mui-system/src/cssVars/createCssVarsProvider.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import { GlobalStyles } from '@mui/styled-engine';
Expand Down
11 changes: 11 additions & 0 deletions packages/mui-utils/src/deepmerge/deepmerge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,17 @@ describe('deepmerge', () => {
expect(result.element).to.equal(element2);
});

it('should not deep clone React component', () => {
// most 3rd-party components use `forwardRef`
const Link = React.forwardRef((props, ref) => React.createElement('a', { ref, ...props }));
const result = deepmerge(
{ defaultProps: { component: 'a' } },
{ defaultProps: { component: Link } },
);

expect(result.defaultProps.component).to.equal(Link);
});

it('should deep clone example correctly', () => {
const result = deepmerge({ a: { b: 1 }, d: 2 }, { a: { c: 2 }, d: 4 });

Expand Down
5 changes: 3 additions & 2 deletions packages/mui-utils/src/deepmerge/deepmerge.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import { isValidElementType } from 'react-is';

// https://github.com/sindresorhus/is-plain-obj/blob/main/index.js
export function isPlainObject(item: unknown): item is Record<keyof any, unknown> {
Expand All @@ -21,7 +22,7 @@ export interface DeepmergeOptions {
}

function deepClone<T>(source: T): T | Record<keyof any, unknown> {
if (React.isValidElement(source) || !isPlainObject(source)) {
if (React.isValidElement(source) || isValidElementType(source) || !isPlainObject(source)) {
return source;
}

Expand Down Expand Up @@ -61,7 +62,7 @@ export default function deepmerge<T>(

if (isPlainObject(target) && isPlainObject(source)) {
Object.keys(source).forEach((key) => {
if (React.isValidElement(source[key])) {
if (React.isValidElement(source[key]) || isValidElementType(source[key])) {
(output as Record<keyof any, unknown>)[key] = source[key];
} else if (
isPlainObject(source[key]) &&
Expand Down

0 comments on commit b59f577

Please sign in to comment.