@mui/x-codemod 9.0.0-rc.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,117 @@
1
1
  # Changelog
2
2
 
3
+ ## 9.0.0
4
+
5
+ <!-- generated comparing v9.0.0-rc.0..master -->
6
+
7
+ _Apr 8, 2026_
8
+
9
+ 🥳 We're excited to announce the stable release of MUI X v9!
10
+ This major release includes many new features and improvements. Here are some highlights ✨:
11
+
12
+ - Data Grid – [Charts integration](https://mui.com/x/react-data-grid/charts-integration/) [Premium]
13
+ - Data Grid – [AI Assistant](https://mui.com/x/react-data-grid/ai-assistant/) [Premium]
14
+ - Data Grid – [Undo and redo](https://mui.com/x/react-data-grid/undo-redo/) [Premium]
15
+ - Data Grid – [Drag fill](https://mui.com/x/react-data-grid/clipboard/#drag-to-fill) [Premium]
16
+ - Data Grid – [longText column type](https://mui.com/x/react-data-grid/column-definition/#column-types)
17
+ - Charts – [Interaction and accessibility](https://mui.com/x/react-charts/accessibility/)
18
+ - Charts – [Candlestick](https://mui.com/x/react-charts/candlestick/) [Premium]
19
+ - Charts – [Range bar charts](https://mui.com/x/react-charts/range-bar/) [Premium]
20
+ - Charts – [WebGL Heatmap renderer](https://mui.com/x/react-charts/heatmap/#webgl-renderer) [Premium]
21
+ - Tree View – [Virtualization](https://mui.com/x/react-tree-view/rich-tree-view/virtualization/) [Pro]
22
+ - New [Scheduler](https://mui.com/x/react-scheduler/) packages [Alpha]
23
+
24
+ We'd like to extend a big thank you to the 5 contributors who made this release possible.
25
+ The following team members contributed to this release:
26
+ @DanailH, @LukasTy, @MBilalShafi, @oliviertassinari, @siriwatknp
27
+
28
+ ### Data Grid
29
+
30
+ #### `@mui/x-data-grid@9.0.0`
31
+
32
+ Internal changes.
33
+
34
+ #### `@mui/x-data-grid-pro@9.0.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
35
+
36
+ Same changes as in `@mui/x-data-grid@9.0.0`, plus:
37
+
38
+ - [DataGridPro] Preserve parent selection for non-selectable children (#21132) @MBilalShafi
39
+
40
+ #### `@mui/x-data-grid-premium@9.0.0` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
41
+
42
+ Same changes as in `@mui/x-data-grid-pro@9.0.0`, plus:
43
+
44
+ - [DataGridPremium] Drag fill (#21717) @MBilalShafi
45
+
46
+ ### Date and Time Pickers
47
+
48
+ #### Breaking changes
49
+
50
+ - Removed the legacy Pickers and Field TextField props (for example: `InputProps`) in favor of the nested `slotProps`. [Read more](https://mui.com/x/migration/migration-pickers-v8/#drop-deprecated-pickerstextfield-props)
51
+ - The `utils` field in `PickersAdapterContextValue` has been removed in favor of the `adapter` field.
52
+ This should no longer affect you, as the context export has also been removed.
53
+ - `MuiPickersAdapterContext` export has been removed.
54
+ Prefer using the `usePickerAdapter` hook. [Read more](https://mui.com/x/migration/migration-pickers-v8/#localizationprovider-breaking-changes).
55
+
56
+ #### `@mui/x-date-pickers@9.0.0`
57
+
58
+ - [pickers] Refactor `PickersTextField` to use `slotProps` approach (#22002) @LukasTy
59
+ - [pickers] Remove deprecated LocalizationProvider legacy API (#22010) @LukasTy
60
+
61
+ #### `@mui/x-date-pickers-pro@9.0.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
62
+
63
+ Same changes as in `@mui/x-date-pickers@9.0.0`.
64
+
65
+ ### Charts
66
+
67
+ #### `@mui/x-charts@9.0.0`
68
+
69
+ Internal changes.
70
+
71
+ #### `@mui/x-charts-pro@9.0.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
72
+
73
+ Same changes as in `@mui/x-charts@9.0.0`.
74
+
75
+ #### `@mui/x-charts-premium@9.0.0` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
76
+
77
+ Same changes as in `@mui/x-charts-pro@9.0.0`.
78
+
79
+ ### Tree View
80
+
81
+ #### `@mui/x-tree-view@9.0.0`
82
+
83
+ Internal changes.
84
+
85
+ #### `@mui/x-tree-view-pro@9.0.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
86
+
87
+ Same changes as in `@mui/x-tree-view@9.0.0`.
88
+
89
+ ### Scheduler
90
+
91
+ #### `@mui/x-scheduler@9.0.0-alpha.0`
92
+
93
+ Internal changes.
94
+
95
+ #### `@mui/x-scheduler-premium@9.0.0-alpha.0` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
96
+
97
+ Same changes as in `@mui/x-scheduler-pro@9.0.0-alpha.0`.
98
+
99
+ ### Codemod
100
+
101
+ #### `@mui/x-codemod@9.0.0`
102
+
103
+ Internal changes.
104
+
105
+ ### Docs
106
+
107
+ - [docs] Add explanation for v8 -> v9 license migration (#22004) @DanailH
108
+
109
+ ### Core
110
+
111
+ - [code-infra] Optimize dependency definition (#22006) @LukasTy
112
+ - [internal] Prepare v9 stable (#22018) @siriwatknp
113
+ - [internal] Remove 'conf' from codebase (#21989) @oliviertassinari
114
+
3
115
  ## 9.0.0-rc.0
4
116
 
5
117
  <!-- generated comparing v9.0.0-beta.0..master -->
@@ -40,8 +152,8 @@ Same changes as in `@mui/x-data-grid-pro@9.0.0-rc.0`, plus:
40
152
 
41
153
  #### Breaking changes
42
154
 
43
- - Accessible DOM structure is now the only default. [Read more](https://next.mui.com/x/migration/migration-pickers-v8/#accessible-dom-structure-is-now-the-default)
44
- - The `PickerDay2` and `DateRangePickerDay2` components were propagated to stable while removing the previous defaults. [Read more](https://next.mui.com/x/migration/migration-pickers-v8/#day-slot)
155
+ - Accessible DOM structure is now the only default. [Read more](https://mui.com/x/migration/migration-pickers-v8/#accessible-dom-structure-is-now-the-default)
156
+ - The `PickerDay2` and `DateRangePickerDay2` components were propagated to stable while removing the previous defaults. [Read more](https://mui.com/x/migration/migration-pickers-v8/#day-slot)
45
157
 
46
158
  #### `@mui/x-date-pickers@9.0.0-rc.0`
47
159
 
package/README.md CHANGED
@@ -69,7 +69,7 @@ To run codemods for a specific package, refer to the respective section.
69
69
  <!-- #npm-tag-reference -->
70
70
 
71
71
  ```bash
72
- npx @mui/x-codemod@next v9.0.0/preset-safe <path|folder>
72
+ npx @mui/x-codemod@latest v9.0.0/preset-safe <path|folder>
73
73
  ```
74
74
 
75
75
  The corresponding sub-sections are listed below
@@ -90,7 +90,7 @@ If `charts` is the only property, the entire `experimentalFeatures` prop is remo
90
90
  <!-- #npm-tag-reference -->
91
91
 
92
92
  ```bash
93
- npx @mui/x-codemod@next v9.0.0/data-grid/remove-stabilized-experimentalFeatures <path|folder>
93
+ npx @mui/x-codemod@latest v9.0.0/data-grid/remove-stabilized-experimentalFeatures <path|folder>
94
94
  ```
95
95
 
96
96
  ```diff
@@ -109,7 +109,7 @@ The `preset-safe` codemods for Charts.
109
109
  <!-- #npm-tag-reference -->
110
110
 
111
111
  ```bash
112
- npx @mui/x-codemod@next v9.0.0/charts/preset-safe <path|folder>
112
+ npx @mui/x-codemod@latest v9.0.0/charts/preset-safe <path|folder>
113
113
  ```
114
114
 
115
115
  The list includes these transformers
@@ -320,7 +320,7 @@ The `preset-safe` codemods for Pickers.
320
320
  <!-- #npm-tag-reference -->
321
321
 
322
322
  ```bash
323
- npx @mui/x-codemod@next v9.0.0/pickers/preset-safe <path|folder>
323
+ npx @mui/x-codemod@latest v9.0.0/pickers/preset-safe <path|folder>
324
324
  ```
325
325
 
326
326
  The list includes these transformers
@@ -332,6 +332,7 @@ The list includes these transformers
332
332
  - [`rename-pickers-day`](#rename-pickers-day)
333
333
  - [`rename-picker-classes`](#rename-picker-classes)
334
334
  - [`remove-disable-margin`](#remove-disable-margin)
335
+ - [`migrate-text-field-props`](#migrate-text-field-props)
335
336
 
336
337
  #### `rename-field-ref`
337
338
 
@@ -351,7 +352,7 @@ Renames the `unstableFieldRef` prop to `fieldRef` on all Picker and Field compon
351
352
  <!-- #npm-tag-reference -->
352
353
 
353
354
  ```bash
354
- npx @mui/x-codemod@next v9.0.0/pickers/rename-field-ref <path|folder>
355
+ npx @mui/x-codemod@latest v9.0.0/pickers/rename-field-ref <path|folder>
355
356
  ```
356
357
 
357
358
  #### `remove-enable-accessible-field-dom-structure`
@@ -373,7 +374,7 @@ The accessible DOM structure is now the only supported option and this prop has
373
374
  <!-- #npm-tag-reference -->
374
375
 
375
376
  ```bash
376
- npx @mui/x-codemod@next v9.0.0/pickers/remove-enable-accessible-field-dom-structure <path|folder>
377
+ npx @mui/x-codemod@latest v9.0.0/pickers/remove-enable-accessible-field-dom-structure <path|folder>
377
378
  ```
378
379
 
379
380
  #### `remove-picker-day-2`
@@ -392,7 +393,7 @@ Also handles objects passed through variables (for example `const slots = { day:
392
393
  <!-- #npm-tag-reference -->
393
394
 
394
395
  ```bash
395
- npx @mui/x-codemod@next v9.0.0/pickers/remove-picker-day-2 <path>
396
+ npx @mui/x-codemod@latest v9.0.0/pickers/remove-picker-day-2 <path>
396
397
  ```
397
398
 
398
399
  #### `rename-picker-day-2`
@@ -419,7 +420,7 @@ Renames `PickerDay2` and `DateRangePickerDay2` components and their related type
419
420
  <!-- #npm-tag-reference -->
420
421
 
421
422
  ```bash
422
- npx @mui/x-codemod@next v9.0.0/pickers/rename-picker-day-2 <path>
423
+ npx @mui/x-codemod@latest v9.0.0/pickers/rename-picker-day-2 <path>
423
424
  ```
424
425
 
425
426
  #### `rename-pickers-day`
@@ -443,7 +444,7 @@ Renames `PickersDay` to `PickerDay` and all related types, classes, and theme co
443
444
  <!-- #npm-tag-reference -->
444
445
 
445
446
  ```bash
446
- npx @mui/x-codemod@next v9.0.0/pickers/rename-pickers-day <path>
447
+ npx @mui/x-codemod@latest v9.0.0/pickers/rename-pickers-day <path>
447
448
  ```
448
449
 
449
450
  #### `rename-picker-classes`
@@ -464,7 +465,7 @@ Renames `PickerDay` and `DateRangePickerDay` CSS class keys to their new equival
464
465
  <!-- #npm-tag-reference -->
465
466
 
466
467
  ```bash
467
- npx @mui/x-codemod@next v9.0.0/pickers/rename-picker-classes <path>
468
+ npx @mui/x-codemod@latest v9.0.0/pickers/rename-picker-classes <path>
468
469
  ```
469
470
 
470
471
  #### `remove-disable-margin`
@@ -484,7 +485,37 @@ When `disableMargin={false}`, the prop is simply removed without adding the CSS
484
485
  <!-- #npm-tag-reference -->
485
486
 
486
487
  ```bash
487
- npx @mui/x-codemod@next v9.0.0/pickers/remove-disable-margin <path>
488
+ npx @mui/x-codemod@latest v9.0.0/pickers/remove-disable-margin <path>
489
+ ```
490
+
491
+ #### `migrate-text-field-props`
492
+
493
+ Rewrites the legacy `InputProps`, `inputProps`, `InputLabelProps` and `FormHelperTextProps` props on Picker, Field and `PickersTextField` components into the new `slotProps.{input,htmlInput,inputLabel,formHelperText}` shape. On Picker and Field components the new keys are nested inside `slotProps.textField.slotProps`; on `PickersTextField` they live directly under `slotProps`.
494
+
495
+ ```diff
496
+ -<DateField
497
+ - InputProps={{ name: 'birthday' }}
498
+ - inputProps={{ 'data-testid': 'html-input' }}
499
+ -/>
500
+ +<DateField
501
+ + slotProps={{
502
+ + textField: {
503
+ + slotProps: {
504
+ + input: { name: 'birthday' },
505
+ + htmlInput: { 'data-testid': 'html-input' },
506
+ + },
507
+ + },
508
+ + }}
509
+ +/>
510
+
511
+ -<DatePicker slotProps={{ textField: { InputProps: { name: 'date' } } }} />
512
+ +<DatePicker slotProps={{ textField: { slotProps: { input: { name: 'date' } } } }} />
513
+ ```
514
+
515
+ <!-- #npm-tag-reference -->
516
+
517
+ ```bash
518
+ npx @mui/x-codemod@next v9.0.0/pickers/migrate-text-field-props <path>
488
519
  ```
489
520
 
490
521
  ## v8.0.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/x-codemod",
3
- "version": "9.0.0-rc.0",
3
+ "version": "9.0.0",
4
4
  "author": "MUI Team",
5
5
  "description": "Codemod scripts for MUI X.",
6
6
  "license": "MIT",
@@ -28,7 +28,7 @@
28
28
  "@babel/traverse": "^7.29.0",
29
29
  "jscodeshift": "17.3.0",
30
30
  "yargs": "^18.0.0",
31
- "@mui/x-internals": "9.0.0-rc.0"
31
+ "@mui/x-internals": "^9.0.0"
32
32
  },
33
33
  "sideEffects": false,
34
34
  "publishConfig": {
@@ -15,11 +15,27 @@ const addItemToObject = (path, value, object, j) => {
15
15
 
16
16
  // Final case where we have to add the property to the object.
17
17
  if (splittedPath.length === 1) {
18
- const propertyToAdd = j.objectProperty(j.identifier(path), value);
19
18
  if (object === null) {
19
+ const propertyToAdd = j.objectProperty(j.identifier(path), value);
20
20
  return j.objectExpression([propertyToAdd]);
21
21
  }
22
- return j.objectExpression([...(object.properties ?? []).filter(property => property.key.name !== path), propertyToAdd]);
22
+
23
+ // When both the existing and new values are ObjectExpressions, merge their properties
24
+ // (new properties win on key conflicts) instead of replacing the entire object.
25
+ const existingProperty = (object.properties ?? []).find(property => property.key?.name === path || property.key?.value === path);
26
+ if (existingProperty && existingProperty.value.type === 'ObjectExpression' && value.type === 'ObjectExpression') {
27
+ // Spread elements (e.g. `{ ...rest }`) have no `key`, so guard against `undefined`
28
+ // sneaking into the dedup set — otherwise existing spreads would be filtered out.
29
+ const newKeys = new Set(value.properties.map(p => p.key?.name ?? p.key?.value).filter(key => key !== undefined));
30
+ const mergedValue = j.objectExpression([...existingProperty.value.properties.filter(p => {
31
+ const key = p.key?.name ?? p.key?.value;
32
+ return key === undefined || !newKeys.has(key);
33
+ }), ...value.properties]);
34
+ const mergedProperty = j.objectProperty(j.identifier(path), mergedValue);
35
+ return j.objectExpression([...(object.properties ?? []).filter(property => (property.key?.name ?? property.key?.value) !== path), mergedProperty]);
36
+ }
37
+ const propertyToAdd = j.objectProperty(j.identifier(path), value);
38
+ return j.objectExpression([...(object.properties ?? []).filter(property => (property.key?.name ?? property.key?.value) !== path), propertyToAdd]);
23
39
  }
24
40
  const remainingPath = splittedPath.slice(1).join('.');
25
41
  const targetKey = splittedPath[0];
@@ -30,11 +46,12 @@ const addItemToObject = (path, value, object, j) => {
30
46
  }
31
47
 
32
48
  // Look if the object we got already contains the property we have to use.
33
- const correspondingObject = (object.properties ?? []).find(property => property.key.name === targetKey);
49
+ // `property.key` is missing on spread / rest elements, so guard the access.
50
+ const correspondingObject = (object.properties ?? []).find(property => (property.key?.name ?? property.key?.value) === targetKey);
34
51
  const propertyToAdd = j.objectProperty(j.identifier(targetKey),
35
52
  // Here we use recursion to mix the new value with the current one
36
53
  addItemToObject(remainingPath, value, correspondingObject?.value ?? null, j));
37
- return j.objectExpression([...(object.properties ?? []).filter(property => property.key.name !== targetKey), propertyToAdd]);
54
+ return j.objectExpression([...(object.properties ?? []).filter(property => (property.key?.name ?? property.key?.value) !== targetKey), propertyToAdd]);
38
55
  };
39
56
 
40
57
  /**
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = transformer;
8
+ exports.testConfig = void 0;
9
+ var _path = _interopRequireDefault(require("path"));
10
+ var _readFile = _interopRequireDefault(require("../../../util/readFile"));
11
+ var _addComponentsSlots = require("../../../util/addComponentsSlots");
12
+ var _removeProps = _interopRequireDefault(require("../../../util/removeProps"));
13
+ // @ts-ignore - JS file without types
14
+
15
+ /**
16
+ * Maps a legacy text-field prop name to the corresponding key inside `slotProps`.
17
+ */
18
+ const PROP_TO_SLOT = {
19
+ InputProps: 'input',
20
+ inputProps: 'htmlInput',
21
+ InputLabelProps: 'inputLabel',
22
+ FormHelperTextProps: 'formHelperText'
23
+ };
24
+ const LEGACY_PROP_NAMES = Object.keys(PROP_TO_SLOT);
25
+ const FIELD_AND_PICKER_NAMES = [
26
+ // Fields
27
+ 'DateField', 'DateTimeField', 'TimeField', 'DateRangeField', 'DateTimeRangeField', 'TimeRangeField', 'MultiInputDateRangeField', 'MultiInputDateTimeRangeField', 'MultiInputTimeRangeField', 'SingleInputDateRangeField', 'SingleInputDateTimeRangeField', 'SingleInputTimeRangeField',
28
+ // Pickers
29
+ 'DatePicker', 'DesktopDatePicker', 'MobileDatePicker', 'StaticDatePicker', 'DateTimePicker', 'DesktopDateTimePicker', 'MobileDateTimePicker', 'StaticDateTimePicker', 'TimePicker', 'DesktopTimePicker', 'MobileTimePicker', 'StaticTimePicker', 'DateRangePicker', 'DesktopDateRangePicker', 'MobileDateRangePicker', 'StaticDateRangePicker', 'DateTimeRangePicker', 'DesktopDateTimeRangePicker', 'MobileDateTimeRangePicker', 'TimeRangePicker', 'DesktopTimeRangePicker', 'MobileTimeRangePicker'];
30
+ const ALL_TARGET_NAMES = [...FIELD_AND_PICKER_NAMES, 'PickersTextField'];
31
+ const getKeyName = key => {
32
+ if (!key) {
33
+ return undefined;
34
+ }
35
+ if (key.type === 'Identifier') {
36
+ return key.name;
37
+ }
38
+ if (key.type === 'Literal' || key.type === 'StringLiteral') {
39
+ return String(key.value);
40
+ }
41
+ return undefined;
42
+ };
43
+ function transformer(file, api, options) {
44
+ const j = api.jscodeshift;
45
+ const root = j(file.source);
46
+ const printOptions = options.printOptions || {
47
+ quote: 'single',
48
+ trailingComma: true
49
+ };
50
+
51
+ // 1. Rewrite legacy props passed directly as JSX attributes on field / picker components.
52
+ root.find(j.JSXElement).filter(elementPath => {
53
+ const nameNode = elementPath.value.openingElement.name;
54
+ return nameNode && nameNode.type === 'JSXIdentifier' && ALL_TARGET_NAMES.includes(nameNode.name);
55
+ }).forEach(elementPath => {
56
+ const nameNode = elementPath.value.openingElement.name;
57
+ // For PickersTextField the new keys live directly on `slotProps`.
58
+ // For every other component they live on a nested `textField.slotProps` object.
59
+ const pathPrefix = nameNode.name === 'PickersTextField' ? '' : 'textField.slotProps.';
60
+ const attributesToTransform = j(elementPath).find(j.JSXAttribute).filter(attribute => {
61
+ const attributeParent = attribute.parentPath.parentPath;
62
+ if (attributeParent.value.type !== 'JSXOpeningElement' || attributeParent.value.name.name !== nameNode.name) {
63
+ return false;
64
+ }
65
+ return LEGACY_PROP_NAMES.includes(attribute.value.name.name);
66
+ });
67
+ attributesToTransform.forEach(attribute => {
68
+ const attributeName = attribute.value.name.name;
69
+ const value = attribute.value.value?.type === 'JSXExpressionContainer' ? attribute.value.value.expression : attribute.value.value || j.booleanLiteral(true);
70
+ (0, _addComponentsSlots.transformNestedProp)(elementPath, 'slotProps', `${pathPrefix}${PROP_TO_SLOT[attributeName]}`, value, j);
71
+ });
72
+ });
73
+
74
+ // Drop the now-orphaned legacy attributes from the targeted components.
75
+ (0, _removeProps.default)({
76
+ root,
77
+ componentNames: ALL_TARGET_NAMES,
78
+ props: LEGACY_PROP_NAMES,
79
+ j
80
+ });
81
+
82
+ // 2. Rewrite legacy props found inside `slotProps={{ field: { ... } }}` and
83
+ // `slotProps={{ textField: { ... } }}` regardless of which component they appear on.
84
+ root.find(j.JSXAttribute, {
85
+ name: {
86
+ name: 'slotProps'
87
+ }
88
+ }).forEach(attrPath => {
89
+ const openingElement = attrPath.parentPath?.parentPath?.value;
90
+ if (!openingElement || openingElement.type !== 'JSXOpeningElement' || openingElement.name?.type !== 'JSXIdentifier' || !FIELD_AND_PICKER_NAMES.includes(openingElement.name.name)) {
91
+ return;
92
+ }
93
+ const attrValue = attrPath.value.value;
94
+ if (!attrValue || attrValue.type !== 'JSXExpressionContainer') {
95
+ return;
96
+ }
97
+ const expression = attrValue.expression;
98
+ if (expression.type !== 'ObjectExpression') {
99
+ return;
100
+ }
101
+ // Collect legacy props found inside `field` / `textField` slot objects.
102
+ // - `textField` legacy props are migrated in-place under `textField.slotProps.<newKey>`.
103
+ // - `field` legacy props cannot be nested under `field.slotProps.textField.slotProps.<newKey>`
104
+ // because the `field` slotProps type does not allow it. Hoist them to the sibling
105
+ // `textField.slotProps.<newKey>` instead.
106
+ const fieldCollected = [];
107
+ expression.properties.forEach(prop => {
108
+ if (prop.type === 'SpreadElement' || prop.type === 'ExperimentalSpreadProperty') {
109
+ console.warn(`[migrate-text-field-props] ${file.path}: encountered a spread inside slotProps; ` + `cannot inspect for legacy text field props. Migrate manually if needed.`);
110
+ return;
111
+ }
112
+ if (prop.type !== 'Property' && prop.type !== 'ObjectProperty') {
113
+ return;
114
+ }
115
+ const keyName = getKeyName(prop.key);
116
+ if (keyName !== 'field' && keyName !== 'textField') {
117
+ return;
118
+ }
119
+ if (prop.value.type !== 'ObjectExpression') {
120
+ console.warn(`[migrate-text-field-props] ${file.path}: \`slotProps.${keyName}\` is set to a ` + `non-literal value (e.g. a variable or a function); cannot migrate legacy ` + `text field props automatically. Migrate manually if needed.`);
121
+ return;
122
+ }
123
+ let target = prop.value;
124
+ const remaining = [];
125
+ const collected = [];
126
+ target.properties.forEach(innerProp => {
127
+ if (innerProp.type !== 'Property' && innerProp.type !== 'ObjectProperty') {
128
+ remaining.push(innerProp);
129
+ return;
130
+ }
131
+ const innerKey = getKeyName(innerProp.key);
132
+ if (innerKey && LEGACY_PROP_NAMES.includes(innerKey)) {
133
+ collected.push({
134
+ newKey: PROP_TO_SLOT[innerKey],
135
+ value: innerProp.value
136
+ });
137
+ return;
138
+ }
139
+ remaining.push(innerProp);
140
+ });
141
+ if (collected.length === 0) {
142
+ return;
143
+ }
144
+ if (keyName === 'field') {
145
+ // Defer: hoist these to the sibling `textField` slot below.
146
+ target.properties = remaining;
147
+ fieldCollected.push(...collected);
148
+ return;
149
+ }
150
+ target.properties = remaining;
151
+ // Use the same recursive merge helper used by `transformNestedProp`.
152
+ collected.forEach(({
153
+ newKey,
154
+ value
155
+ }) => {
156
+ target = (0, _addComponentsSlots.addItemToObject)(`slotProps.${newKey}`, value, target, j);
157
+ });
158
+ prop.value = target;
159
+ });
160
+ if (fieldCollected.length > 0) {
161
+ // Drop `field` if it became empty.
162
+ expression.properties = expression.properties.filter(prop => {
163
+ if (prop.type !== 'Property' && prop.type !== 'ObjectProperty') {
164
+ return true;
165
+ }
166
+ if (getKeyName(prop.key) !== 'field') {
167
+ return true;
168
+ }
169
+ return prop.value.type !== 'ObjectExpression' || prop.value.properties.length > 0;
170
+ });
171
+ // Hoist the collected legacy props to `textField.slotProps.<newKey>`.
172
+ let merged = expression;
173
+ fieldCollected.forEach(({
174
+ newKey,
175
+ value
176
+ }) => {
177
+ merged = (0, _addComponentsSlots.addItemToObject)(`textField.slotProps.${newKey}`, value, merged, j);
178
+ });
179
+ expression.properties = merged.properties;
180
+ }
181
+ });
182
+ return root.toSource(printOptions);
183
+ }
184
+ const testConfig = () => ({
185
+ name: 'migrate-text-field-props',
186
+ specFiles: [{
187
+ name: 'migrate legacy text field props to slotProps',
188
+ actual: (0, _readFile.default)(_path.default.join(__dirname, 'actual.spec.tsx')),
189
+ expected: (0, _readFile.default)(_path.default.join(__dirname, 'expected.spec.tsx'))
190
+ }]
191
+ });
192
+ exports.testConfig = testConfig;
@@ -13,11 +13,12 @@ var _renamePickersDay = _interopRequireDefault(require("../rename-pickers-day"))
13
13
  var _renamePickerClasses = _interopRequireDefault(require("../rename-picker-classes"));
14
14
  var _removeDisableMargin = _interopRequireDefault(require("../remove-disable-margin"));
15
15
  var _removeEnableAccessibleFieldDomStructure = _interopRequireDefault(require("../remove-enable-accessible-field-dom-structure"));
16
+ var _migrateTextFieldProps = _interopRequireDefault(require("../migrate-text-field-props"));
16
17
  // Order matters: removePickerDay2 must run before renamePickerDay2
17
18
  // because it looks for `PickerDay2` identifiers in slot objects.
18
19
  // If renamePickerDay2 ran first, those identifiers would already be
19
20
  // renamed to `PickerDay` and removePickerDay2 would not find them.
20
- const allModules = [_renameFieldRef.default, _removePickerDay.default, _renamePickerDay.default, _renamePickersDay.default, _renamePickerClasses.default, _removeDisableMargin.default, _removeEnableAccessibleFieldDomStructure.default];
21
+ const allModules = [_renameFieldRef.default, _removePickerDay.default, _renamePickerDay.default, _renamePickersDay.default, _renamePickerClasses.default, _removeDisableMargin.default, _removeEnableAccessibleFieldDomStructure.default, _migrateTextFieldProps.default];
21
22
  function transformer(file, api, options) {
22
23
  allModules.forEach(transform => {
23
24
  file.source = transform(file, api, options);