Skip to content

[codemod] Add package name option #45977

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
wants to merge 60 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
283cc7a
support custom package for accordion
siriwatknp Apr 22, 2025
467d5f8
finish alert-props
siriwatknp Apr 22, 2025
b9b924b
finish alert-classes
siriwatknp Apr 22, 2025
553ca67
finish autocomplete-props
siriwatknp Apr 22, 2025
bf79724
finish avatar-group-props
siriwatknp Apr 22, 2025
518b3f0
finish avatar-props
siriwatknp Apr 22, 2025
9dde422
finish backdrop-props
siriwatknp Apr 22, 2025
18f36d2
finish badge-props
siriwatknp Apr 22, 2025
888770a
finish button-props
siriwatknp Apr 22, 2025
4e21700
finish button-group-classes
siriwatknp Apr 22, 2025
6602190
finish card-header-props
siriwatknp Apr 22, 2025
9dacf0c
finish chip-classes
siriwatknp Apr 22, 2025
8393e55
finish circular-progress-classes
siriwatknp Apr 22, 2025
c441a3b
finish dialog-classes
siriwatknp Apr 22, 2025
79a3a81
finish divider-props
siriwatknp Apr 22, 2025
aca9fe7
finish drawer-classes
siriwatknp Apr 22, 2025
8c832f6
finish drawer-props
siriwatknp Apr 22, 2025
5fc3778
fix backdrop-props
siriwatknp Apr 22, 2025
9bcce35
fix card-header-props
siriwatknp Apr 22, 2025
03c43b2
finish filled-input-props
siriwatknp Apr 22, 2025
5887bd6
finish form-control-label-props
siriwatknp Apr 22, 2025
8842416
finish image-list-item-bar
siriwatknp Apr 22, 2025
1857576
finish input-base-classes
siriwatknp Apr 22, 2025
6569c41
finish input-base-props
siriwatknp Apr 22, 2025
437c130
finish input-props
siriwatknp Apr 22, 2025
66615ed
finish linear-progress-classes
siriwatknp Apr 22, 2025
f71d5b0
finish list-item-props
siriwatknp Apr 22, 2025
34160c3
finish list-item-text-props
siriwatknp Apr 22, 2025
9a9b245
finish menu-props
siriwatknp Apr 22, 2025
13c981d
finish mobile-stepper-props
siriwatknp Apr 22, 2025
5f81a51
finish modal-props
siriwatknp Apr 22, 2025
0c17f7a
finish outlined-input-props
siriwatknp Apr 22, 2025
311a965
finish pagination-item-classes
siriwatknp Apr 22, 2025
68c88e9
finish pagination-item-props
siriwatknp Apr 22, 2025
8d30087
finish popover-props
siriwatknp Apr 22, 2025
d17d57d
finish popper-props
siriwatknp Apr 22, 2025
fecd38c
finish rating-props
siriwatknp Apr 22, 2025
3804807
finish select-classes
siriwatknp Apr 22, 2025
2a0cda0
finish slider-classes
siriwatknp Apr 22, 2025
ae6713f
finish slider-props
siriwatknp Apr 22, 2025
1cc023f
finish snackbar-props
siriwatknp Apr 22, 2025
2a1bf7d
finish speed-dial-action-props
siriwatknp Apr 22, 2025
04c4466
finish speed-dial-props
siriwatknp Apr 22, 2025
4cf76e2
finish step-connector-classes
siriwatknp Apr 22, 2025
9f74f41
finish step-content-props
siriwatknp Apr 22, 2025
59d785f
finish step-label-props
siriwatknp Apr 22, 2025
290f8c5
finish tab-classes
siriwatknp Apr 22, 2025
de45136
finish table-pagination-props
siriwatknp Apr 22, 2025
a35108a
finish table-sort-label-classes
siriwatknp Apr 22, 2025
7813a00
finish tabs-classes
siriwatknp Apr 22, 2025
183c29c
finish tabs-props
siriwatknp Apr 22, 2025
b0a428e
finish text-field-props
siriwatknp Apr 22, 2025
380883e
finish toggle-button-group-classes
siriwatknp Apr 22, 2025
75acb51
finish tooltip-props
siriwatknp Apr 22, 2025
63842c6
finish typography-props
siriwatknp Apr 22, 2025
bb1911d
add packageName option
siriwatknp Apr 22, 2025
6ad76cc
finish list-item-button-prop
siriwatknp Apr 22, 2025
1259721
finish grid-v2-props
siriwatknp Apr 22, 2025
2c339d3
finish grid-props
siriwatknp Apr 22, 2025
7b76fda
fix non-breaking space
siriwatknp Apr 23, 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: 10 additions & 0 deletions packages/mui-codemod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ Examples:
npx @mui/codemod@latest v5.0.0/preset-safe src --parser=flow
```

### package name

Use this flag if you have a custom package name that reexports Material UI components. For example:

```bash
npx @mui/codemod@latest --packageName="@org/ui"
```

The snippet above will look for `@org/ui` instead of `@mui/material` in the codemod.

### jscodeshift options

To pass more options directly to jscodeshift, use `--jscodeshift="..."`. For example:
Expand Down
8 changes: 8 additions & 0 deletions packages/mui-codemod/codemod.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ async function runJscodeshiftTransform(transform, files, flags, codemodFlags) {
if (flags.jscodeshift) {
args.push(flags.jscodeshift);
}
if (flags.packageName) {
args.push(`--packageName=${flags.packageName}`);
}

args.push(...files);

Expand Down Expand Up @@ -196,6 +199,11 @@ yargs
description: '(Advanced) Pass options directly to jscodeshift',
default: false,
type: 'string',
})
.option('packageName', {
description: 'The package name to look for in the import statements',
default: '@mui/material',
type: 'string',
});
},
handler: run,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ export default function transformer(file, api, options) {

movePropIntoSlots(j, {
root,
packageName: options.packageName,
componentName: 'Accordion',
propName: 'TransitionComponent',
slotName: 'transition',
});

movePropIntoSlotProps(j, {
root,
packageName: options.packageName,
componentName: 'Accordion',
propName: 'TransitionProps',
slotName: 'transition',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,29 @@ describe('@mui/codemod', () => {
expect(actual).to.equal(expected, 'The transformed version should be correct');
});
});

describe('[custom package] accordion-props', () => {
it('transforms props as needed', () => {
const actual = transform(
{ source: read('./test-cases/package.actual.js') },
{ jscodeshift },
{ packageName: '@org/ui/material' },
);

const expected = read('./test-cases/package.expected.js');
expect(actual).to.equal(expected, 'The transformed version should be correct');
});

it('should be idempotent', () => {
const actual = transform(
{ source: read('./test-cases/package.expected.js') },
{ jscodeshift },
{ packageName: '@org/ui/material' },
);

const expected = read('./test-cases/package.expected.js');
expect(actual).to.equal(expected, 'The transformed version should be correct');
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Accordion from '@org/ui/material/Accordion';
import { Accordion as MyAccordion } from '@org/ui/material';

<Accordion TransitionProps={{ unmountOnExit: true }} slots={{
transition: CustomTransition
}} />;
<MyAccordion TransitionProps={transitionVars} slots={{
transition: CustomTransition
}} />;
<Accordion
TransitionProps={{ unmountOnExit: true }}
slots={{
root: 'div',
transition: CustomTransition
}}
slotProps={{
root: { className: 'foo' },
}} />;
<MyAccordion
TransitionProps={{ unmountOnExit: true }}
slots={{
...outerSlots,
transition: CustomTransition
}}
slotProps={{
...outerSlotProps,
}} />;
<Accordion slots={{ transition: SlotTransition }} />;
<Accordion TransitionProps={{ unmountOnExit: true }} slotProps={{ transition: { id: 'test' } }} />;
// should skip non MUI components
<NonMuiAccordion
TransitionComponent={CustomTransition}
TransitionProps={{ unmountOnExit: true }}
/>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import Accordion from '@org/ui/material/Accordion';
import { Accordion as MyAccordion } from '@org/ui/material';

<Accordion slots={{
transition: CustomTransition
}} slotProps={{
transition: { unmountOnExit: true }
}} />;
<MyAccordion slots={{
transition: CustomTransition
}} slotProps={{
transition: transitionVars
}} />;
<Accordion
slots={{
root: 'div',
transition: CustomTransition
}}
slotProps={{
root: { className: 'foo' },
transition: { unmountOnExit: true }
}} />;
<MyAccordion
slots={{
...outerSlots,
transition: CustomTransition
}}
slotProps={{
...outerSlotProps,
transition: { unmountOnExit: true }
}} />;
<Accordion slots={{ transition: SlotTransition }} />;
<Accordion
slotProps={{ transition: {
...{ unmountOnExit: true },
...{ id: 'test' }
} }} />;
// should skip non MUI components
<NonMuiAccordion
TransitionComponent={CustomTransition}
TransitionProps={{ unmountOnExit: true }}
/>;
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ export default function transformer(file, api, options) {

root
.find(j.ImportDeclaration)
.filter((path) => path.node.source.value.match(/^@mui\/material\/AccordionSummary$/))
.filter((path) =>
path.node.source.value.match(
new RegExp(`^${options.packageName || '@mui/material'}(/AccordionSummary)?$`),
),
)
.forEach((path) => {
path.node.specifiers.forEach((specifier) => {
if (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,33 @@ describe('@mui/codemod', () => {
});
});

describe('[package] js-transform', () => {
it('transforms props as needed', () => {
const actual = jsTransform(
{ source: read('./test-cases/package.actual.js') },
{ jscodeshift },
{
printOptions: { quote: 'single', trailingComma: true },
packageName: '@org/ui/material',
},
);

const expected = read('./test-cases/package.expected.js');
expect(actual).to.equal(expected, 'The transformed version should be correct');
});

it('should be idempotent', () => {
const actual = jsTransform(
{ source: read('./test-cases/package.expected.js') },
{ jscodeshift },
{ packageName: '@org/ui/material' },
);

const expected = read('./test-cases/package.expected.js');
expect(actual).to.equal(expected, 'The transformed version should be correct');
});
});

describe('css-transform', () => {
it('transforms classes as needed', async () => {
const actual = await postcssProcessor.process(read('./test-cases/actual.css'), {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { accordionSummaryClasses } from '@org/ui/material/AccordionSummary';

fn({
MuiAccordionSummary: {
styleOverrides: {
root: {
'& .MuiAccordionSummary-contentGutters': {
color: 'red',
},
},
},
},
});

fn({
MuiAccordionSummary: {
styleOverrides: {
root: {
[`& .${accordionSummaryClasses.contentGutters}`]: {
color: 'red',
},
},
},
},
});

styled(Component)(() => {
return {
'& .MuiAccordionSummary-contentGutters': {
color: 'red',
},
};
});

styled(Component)(() => {
return {
[`& .${accordionSummaryClasses.contentGutters}`]: {
color: 'red',
},
};
});

<AccordionSummary
sx={{
'& .MuiAccordionSummary-contentGutters': {
color: 'red',
},
}}
/>;

<AccordionSummary
sx={{
[`& .${accordionSummaryClasses.contentGutters}`]: {
color: 'red',
},
}}
/>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { accordionSummaryClasses } from '@org/ui/material/AccordionSummary';

fn({
MuiAccordionSummary: {
styleOverrides: {
root: {
'&.MuiAccordionSummary-gutters .MuiAccordionSummary-content': {
color: 'red',
},
},
},
},
});

fn({
MuiAccordionSummary: {
styleOverrides: {
root: {
[`&.${accordionSummaryClasses.gutters} .${accordionSummaryClasses.content}`]: {
color: 'red',
},
},
},
},
});

styled(Component)(() => {
return {
'&.MuiAccordionSummary-gutters .MuiAccordionSummary-content': {
color: 'red',
},
};
});

styled(Component)(() => {
return {
[`&.${accordionSummaryClasses.gutters} .${accordionSummaryClasses.content}`]: {
color: 'red',
},
};
});

<AccordionSummary
sx={{
'&.MuiAccordionSummary-gutters .MuiAccordionSummary-content': {
color: 'red',
},
}}
/>;

<AccordionSummary
sx={{
[`&.${accordionSummaryClasses.gutters} .${accordionSummaryClasses.content}`]: {
color: 'red',
},
}}
/>;
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ export default function transformer(file, api, options) {
classes.forEach(({ deprecatedClass, replacementSelector }) => {
root
.find(j.ImportDeclaration)
.filter((path) => path.node.source.value.match(/^@mui\/material\/Alert$/))
.filter((path) =>
path.node.source.value.match(
new RegExp(`^${options.packageName || '@mui/material'}(/Alert)?$`),
),
)
.forEach((path) => {
path.node.specifiers.forEach((specifier) => {
if (specifier.type === 'ImportSpecifier' && specifier.imported.name === 'alertClasses') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,33 @@ describe('@mui/codemod', () => {
});
});

describe('[package] js-transform', () => {
it('transforms props as needed', () => {
const actual = jsTransform(
{ source: read('./test-cases/package.actual.js') },
{ jscodeshift },
{
printOptions: { quote: 'single', trailingComma: true },
packageName: '@org/ui/material',
},
);

const expected = read('./test-cases/package.expected.js');
expect(actual).to.equal(expected, 'The transformed version should be correct');
});

it('should be idempotent', () => {
const actual = jsTransform(
{ source: read('./test-cases/package.expected.js') },
{ jscodeshift },
{ packageName: '@org/ui/material' },
);

const expected = read('./test-cases/package.expected.js');
expect(actual).to.equal(expected, 'The transformed version should be correct');
});
});

describe('css-transform', () => {
it('transforms classes as needed', async () => {
const actual = await postcssProcessor.process(read('./test-cases/actual.css'), {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { alertClasses } from '@org/ui/material/Alert';

('&.MuiAlert-standardSuccess');
('&.MuiAlert-standardInfo');
('&.MuiAlert-standardWarning');
('&.MuiAlert-standardError');
('&.MuiAlert-outlinedSuccess');
('&.MuiAlert-outlinedInfo');
('&.MuiAlert-outlinedWarning');
('&.MuiAlert-outlinedError');
('&.MuiAlert-filledSuccess');
('&.MuiAlert-filledInfo');
('&.MuiAlert-filledWarning');
('&.MuiAlert-filledError');
`&.${alertClasses.standardSuccess}`;
`&.${alertClasses.standardInfo}`;
`&.${alertClasses.standardWarning}`;
`&.${alertClasses.standardError}`;
`&.${alertClasses.outlinedSuccess}`;
`&.${alertClasses.outlinedInfo}`;
`&.${alertClasses.outlinedWarning}`;
`&.${alertClasses.outlinedError}`;
`&.${alertClasses.filledSuccess}`;
`&.${alertClasses.filledInfo}`;
`&.${alertClasses.filledWarning}`;
`&.${alertClasses.filledError}`;
Loading
Loading