feat(react): replace deprecated babel classProperties.loose option in .babelrc files (#30912)

This PR updates all `.babelrc` file that still uses the deprecated
`classProperties.loose` (deprecated since v18). The top-level `loose`
option does the same thing -- previously the two options handled
different cases because `@babel/preset-env` did not include class
properties.
This commit is contained in:
Jack Hsu 2025-04-29 09:57:16 -04:00 committed by GitHub
parent 9234fb30a6
commit e21c1a6010
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 172 additions and 0 deletions

View File

@ -3805,6 +3805,16 @@
}
},
"migrations": {
"/nx-api/react/migrations/update-21-0-0-update-babel-loose": {
"description": "Replaces `classProperties.loose` option with `loose`.",
"file": "generated/packages/react/migrations/update-21-0-0-update-babel-loose.json",
"hidden": false,
"name": "update-21-0-0-update-babel-loose",
"version": "21.0.0-beta.11",
"originalFilePath": "/packages/react",
"path": "/nx-api/react/migrations/update-21-0-0-update-babel-loose",
"type": "migration"
},
"/nx-api/react/migrations/add-mf-env-var-to-target-defaults": {
"description": "Add NX_MF_DEV_REMOTES to inputs for task hashing when '@nx/webpack:webpack' or '@nx/rspack:rspack' is used for Module Federation.",
"file": "generated/packages/react/migrations/add-mf-env-var-to-target-defaults.json",

View File

@ -3778,6 +3778,16 @@
}
],
"migrations": [
{
"description": "Replaces `classProperties.loose` option with `loose`.",
"file": "generated/packages/react/migrations/update-21-0-0-update-babel-loose.json",
"hidden": false,
"name": "update-21-0-0-update-babel-loose",
"version": "21.0.0-beta.11",
"originalFilePath": "/packages/react",
"path": "react/migrations/update-21-0-0-update-babel-loose",
"type": "migration"
},
{
"description": "Add NX_MF_DEV_REMOTES to inputs for task hashing when '@nx/webpack:webpack' or '@nx/rspack:rspack' is used for Module Federation.",
"file": "generated/packages/react/migrations/add-mf-env-var-to-target-defaults.json",

View File

@ -0,0 +1,14 @@
{
"name": "update-21-0-0-update-babel-loose",
"cli": "nx",
"version": "21.0.0-beta.11",
"description": "Replaces `classProperties.loose` option with `loose`.",
"factory": "./src/migrations/update-21-0-0/update-babel-loose",
"implementation": "/packages/react/src/migrations/update-21-0-0/update-babel-loose.ts",
"aliases": [],
"hidden": false,
"path": "/packages/react",
"schema": null,
"type": "migration",
"examplesFile": "#### Replace `classProperties.loose` option in `.babelrc`\n\nThe `classProperties.loose` option is replaced by `loose` in `.babelrc` files.\n\n#### Sample Code Changes\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```json {% fileName=\".babelrc\" %}\n{\n \"presets\": [\n [\n \"@nx/react/babel\",\n {\n \"runtime\": \"automatic\",\n \"classProperties\": {\n \"loose\": true\n },\n \"useBuiltIns\": \"usage\"\n }\n ]\n ],\n \"plugins\": []\n}\n```\n\n{% /tab %}\n{% tab label=\"After\" %}\n\n```json {% highlightLines=[7] fileName=\".babelrc\" %}\n{\n \"presets\": [\n [\n \"@nx/react/babel\",\n {\n \"runtime\": \"automatic\",\n \"loose\": true,\n \"useBuiltIns\": \"usage\"\n }\n ]\n ],\n \"plugins\": []\n}\n```\n\n{% /tab %}\n{% /tabs %}\n"
}

View File

@ -41,6 +41,12 @@
"version": "20.4.0-beta.0",
"description": "Add NX_MF_DEV_REMOTES to inputs for task hashing when '@nx/webpack:webpack' or '@nx/rspack:rspack' is used for Module Federation.",
"factory": "./src/migrations/update-18-0-0/add-mf-env-var-to-target-defaults"
},
"update-21-0-0-update-babel-loose": {
"cli": "nx",
"version": "21.0.0-beta.11",
"description": "Replaces `classProperties.loose` option with `loose`.",
"factory": "./src/migrations/update-21-0-0/update-babel-loose"
}
},
"packageJsonUpdates": {

View File

@ -0,0 +1,48 @@
#### Replace `classProperties.loose` option in `.babelrc`
The `classProperties.loose` option is replaced by `loose` in `.babelrc` files.
#### Sample Code Changes
{% tabs %}
{% tab label="Before" %}
```json {% fileName=".babelrc" %}
{
"presets": [
[
"@nx/react/babel",
{
"runtime": "automatic",
"classProperties": {
"loose": true
},
"useBuiltIns": "usage"
}
]
],
"plugins": []
}
```
{% /tab %}
{% tab label="After" %}
```json {% highlightLines=[7] fileName=".babelrc" %}
{
"presets": [
[
"@nx/react/babel",
{
"runtime": "automatic",
"loose": true,
"useBuiltIns": "usage"
}
]
],
"plugins": []
}
```
{% /tab %}
{% /tabs %}

View File

@ -0,0 +1,53 @@
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { Tree } from '@nx/devkit';
import updateBabelLoose from './update-babel-loose';
describe('update-babel-loose migration', () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
});
it('should update classProperties.loose to loose in .babelrc', async () => {
const filePath = '.babelrc';
tree.write(
filePath,
JSON.stringify({
presets: [
[
'@nx/react/babel',
{
runtime: 'automatic',
classProperties: {
loose: true,
},
useBuiltIns: 'usage',
},
],
],
plugins: [],
})
);
await updateBabelLoose(tree);
const content = tree.read(filePath, 'utf-8');
const updatedConfig = JSON.parse(content);
expect(updatedConfig.presets[0][1]).toEqual({
runtime: 'automatic',
loose: true,
useBuiltIns: 'usage',
});
});
it('should skip invalid JSON files', async () => {
const filePath = '.babelrc';
tree.write(filePath, 'invalid json content');
await updateBabelLoose(tree);
const content = tree.read(filePath, 'utf-8');
expect(content).toBe('invalid json content');
});
});

View File

@ -0,0 +1,31 @@
import {
formatFiles,
type Tree,
updateJson,
visitNotIgnoredFiles,
} from '@nx/devkit';
export default async function updateBabelLoose(tree: Tree) {
visitNotIgnoredFiles(tree, '', (path) => {
if (!path.endsWith('.babelrc')) return;
try {
updateJson(tree, path, (babelConfig) => {
if (!Array.isArray(babelConfig.presets)) return;
const ourPreset = babelConfig.presets.find(
(p) => Array.isArray(p) && p[0] === '@nx/react/babel'
);
if (!ourPreset || !ourPreset[1]) return;
const options = ourPreset[1];
if (options['classProperties']?.loose !== undefined) {
options.loose = options['classProperties'].loose;
delete options['classProperties'];
}
return babelConfig;
});
} catch {
// Skip if JSON does not parse for whatever reason
return;
}
});
await formatFiles(tree);
}