From e561239a199b64d5edeb98e59c13c170da584f51 Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Fri, 10 Apr 2026 15:59:03 -0700 Subject: [PATCH 1/8] Fix project compilation --- packages/playground/windows/playground-composition.sln | 3 +++ vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/playground/windows/playground-composition.sln b/packages/playground/windows/playground-composition.sln index 76256ed5464..7948bdf2265 100644 --- a/packages/playground/windows/playground-composition.sln +++ b/packages/playground/windows/playground-composition.sln @@ -29,6 +29,9 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Include", "..\..\..\vnext\include\Include.vcxitems", "{EF074BA1-2D54-4D49-A28E-5E040B47CD2E}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleCustomComponent", "..\..\sample-custom-component\windows\SampleCustomComponent\SampleCustomComponent.vcxproj", "{A8DA218C-4CB5-48CB-A9EE-9E6337165D07}" + ProjectSection(ProjectDependencies) = postProject + {F7D32BD0-2749-483E-9A0D-1635EF7E3136} = {F7D32BD0-2749-483E-9A0D-1635EF7E3136} + EndProjectSection EndProject GlobalSection(SharedMSBuildProjectFiles) = preSolution ..\..\..\vnext\Shared\Shared.vcxitems*{2049dbe9-8d13-42c9-ae4b-413ae38fffd0}*SharedItemsImports = 9 diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj index f30c1e60de7..ea2daf49df4 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj @@ -411,9 +411,6 @@ - - - {fca38f3c-7c73-4c47-be4e-32f77fa8538d} From dae3d474c6e2e5a8e2f22101f750ae153b95e0d4 Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Fri, 10 Apr 2026 16:05:32 -0700 Subject: [PATCH 2/8] Fix "yarn start" --- packages/playground/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/playground/package.json b/packages/playground/package.json index 49656a45caa..7d2daee1d1b 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -3,10 +3,10 @@ "version": "0.0.54", "private": true, "scripts": { - "start": "npx @react-native-community/cli rnx-start", + "start": "react-native rnx-start", "lint:fix": "rnw-scripts lint:fix", "lint": "rnw-scripts lint", - "windows": "npx @react-native-community/cli run-windows" + "windows": "react-native run-windows" }, "dependencies": { "@react-native-picker/picker": "2.11.0", From d711a3bb372798e71d7845a25f9c63b5cfab419f Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Fri, 10 Apr 2026 16:28:09 -0700 Subject: [PATCH 3/8] Change files --- ...ative-windows-9d93df46-79f8-4116-b0c8-7985a9c67d91.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/react-native-windows-9d93df46-79f8-4116-b0c8-7985a9c67d91.json diff --git a/change/react-native-windows-9d93df46-79f8-4116-b0c8-7985a9c67d91.json b/change/react-native-windows-9d93df46-79f8-4116-b0c8-7985a9c67d91.json new file mode 100644 index 00000000000..3442cc7a9b0 --- /dev/null +++ b/change/react-native-windows-9d93df46-79f8-4116-b0c8-7985a9c67d91.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Fix project compilation", + "packageName": "react-native-windows", + "email": "vmorozov@microsoft.com", + "dependentChangeType": "patch" +} From 3665521776ec667f32359696a99408dd007dd546 Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Mon, 20 Apr 2026 11:35:14 -0700 Subject: [PATCH 4/8] Fix JS issues in Node.js 24.x --- packages/playground/metro.config.js | 37 +++++++++++++++++++++++++---- vnext/PropertySheets/Codegen.props | 2 +- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/packages/playground/metro.config.js b/packages/playground/metro.config.js index 9c43518fdb7..3de2fbeea06 100644 --- a/packages/playground/metro.config.js +++ b/packages/playground/metro.config.js @@ -9,13 +9,23 @@ const {makeMetroConfig} = require('@rnw-scripts/metro-dev-config'); const fs = require('fs'); const path = require('path'); -const rnwPath = fs.realpathSync( +// On Windows, require.resolve through symlinks (e.g. yarn workspace links) can +// return paths with a different drive letter case than process.cwd(). Metro's +// file system lookup is case-sensitive, so we must normalize to match. +function normalizePathDrive(p) { + if (process.platform === 'win32' && p.length >= 2 && p[1] === ':') { + return p[0].toUpperCase() + p.slice(1); + } + return p; +} + +const rnwPath = normalizePathDrive(fs.realpathSync( path.dirname(require.resolve('react-native-windows/package.json')), -); +)); -const rnwTesterPath = fs.realpathSync( +const rnwTesterPath = normalizePathDrive(fs.realpathSync( path.dirname(require.resolve('@react-native-windows/tester/package.json')), -); +)); const devPackages = { 'react-native': path.normalize(rnwPath), @@ -144,8 +154,25 @@ function tryResolveDevRelativeImport( return null; } -module.exports = makeMetroConfig({ +const baseConfig = makeMetroConfig({ resolver: { resolveRequest: devResolveRequest, }, }); + +// The getModulesRunBeforeMainModule paths (from @rnx-kit/metro-config) may have +// wrong drive letter case on Windows due to require.resolve through symlinks. +// Metro compares these paths via strict equality against module paths in the +// bundle graph, so the case must match exactly. +const originalGetModulesRunBeforeMainModule = + baseConfig.serializer.getModulesRunBeforeMainModule; +baseConfig.serializer = { + ...baseConfig.serializer, + getModulesRunBeforeMainModule: (...args) => { + return originalGetModulesRunBeforeMainModule(...args).map(p => + normalizePathDrive(fs.realpathSync(p)), + ); + }, +}; + +module.exports = baseConfig; diff --git a/vnext/PropertySheets/Codegen.props b/vnext/PropertySheets/Codegen.props index 78d3ad39967..5669bc9ea3e 100644 --- a/vnext/PropertySheets/Codegen.props +++ b/vnext/PropertySheets/Codegen.props @@ -7,7 +7,7 @@ true - npx --yes @react-native-community/cli codegen-windows + npx --yes --no-workspaces @react-native-community/cli codegen-windows $([MSBuild]::GetDirectoryNameOfFileAbove($(ProjectDir), 'package.json')) --logging From 760a2fcc9c31ad7ff2ac6fadc1ff95539d511888 Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Mon, 20 Apr 2026 11:54:33 -0700 Subject: [PATCH 5/8] Fix other related code --- vnext/PropertySheets/Autolink.props | 2 +- vnext/PropertySheets/Bundle.props | 2 +- vnext/template/metro.config.js | 14 ++++++++++++-- vnext/templates/cpp-app/metro.config.js | 14 ++++++++++++-- vnext/templates/cpp-lib/example/metro.config.js | 14 ++++++++++++-- 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/vnext/PropertySheets/Autolink.props b/vnext/PropertySheets/Autolink.props index a58a0721d8d..27438bd96ae 100644 --- a/vnext/PropertySheets/Autolink.props +++ b/vnext/PropertySheets/Autolink.props @@ -7,7 +7,7 @@ true - npx --yes @react-native-community/cli autolink-windows + npx --yes --no-workspaces @react-native-community/cli autolink-windows $([MSBuild]::GetDirectoryNameOfFileAbove($(ProjectDir), 'package.json')) --check --sln "$([MSBuild]::MakeRelative($(AutolinkCommandWorkingDir), $(SolutionPath)))" --proj "$([MSBuild]::MakeRelative($(AutolinkCommandWorkingDir), $(ProjectPath)))" --check diff --git a/vnext/PropertySheets/Bundle.props b/vnext/PropertySheets/Bundle.props index 4b08d67e031..7340439d5a2 100644 --- a/vnext/PropertySheets/Bundle.props +++ b/vnext/PropertySheets/Bundle.props @@ -39,7 +39,7 @@ --minify false - npx @react-native-community/cli bundle + npx --no-workspaces @react-native-community/cli bundle $([MSBuild]::GetDirectoryNameOfFileAbove($(ProjectDir), 'package.json')) diff --git a/vnext/template/metro.config.js b/vnext/template/metro.config.js index fa46717d0b5..a77a64c978a 100644 --- a/vnext/template/metro.config.js +++ b/vnext/template/metro.config.js @@ -3,9 +3,19 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config'); const fs = require('fs'); const path = require('path'); -const rnwPath = fs.realpathSync( +// On Windows, require.resolve through yarn workspace junctions can return paths +// with a different drive letter case than process.cwd(). Metro's internal file +// system lookup is case-sensitive, so we normalize to match. +function normalizePathDrive(p) { + if (process.platform === 'win32' && p.length >= 2 && p[1] === ':') { + return p[0].toUpperCase() + p.slice(1); + } + return p; +} + +const rnwPath = normalizePathDrive(fs.realpathSync( path.resolve(require.resolve('react-native-windows/package.json'), '..'), -); +)); //{{#devMode}} [devMode const rnwRootNodeModules = path.resolve(rnwPath, '..', 'node_modules'); diff --git a/vnext/templates/cpp-app/metro.config.js b/vnext/templates/cpp-app/metro.config.js index e33b62b8e9c..077d503fea1 100644 --- a/vnext/templates/cpp-app/metro.config.js +++ b/vnext/templates/cpp-app/metro.config.js @@ -3,9 +3,19 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config'); const fs = require('fs'); const path = require('node:path'); -const rnwPath = fs.realpathSync( +// On Windows, require.resolve through yarn workspace junctions can return paths +// with a different drive letter case than process.cwd(). Metro's internal file +// system lookup is case-sensitive, so we normalize to match. +function normalizePathDrive(p) { + if (process.platform === 'win32' && p.length >= 2 && p[1] === ':') { + return p[0].toUpperCase() + p.slice(1); + } + return p; +} + +const rnwPath = normalizePathDrive(fs.realpathSync( path.resolve(require.resolve('react-native-windows/package.json'), '..'), -); +)); //{{#devMode}} [devMode const rnwRootNodeModules = path.resolve(rnwPath, '..', 'node_modules'); diff --git a/vnext/templates/cpp-lib/example/metro.config.js b/vnext/templates/cpp-lib/example/metro.config.js index 126ff52201c..efe460f4c9d 100644 --- a/vnext/templates/cpp-lib/example/metro.config.js +++ b/vnext/templates/cpp-lib/example/metro.config.js @@ -7,9 +7,19 @@ const pack = require('../package.json'); const root = path.resolve(__dirname, '..'); const modules = Object.keys({ ...pack.peerDependencies }); -const rnwPath = fs.realpathSync( +// On Windows, require.resolve through yarn workspace junctions can return paths +// with a different drive letter case than process.cwd(). Metro's internal file +// system lookup is case-sensitive, so we normalize to match. +function normalizePathDrive(p) { + if (process.platform === 'win32' && p.length >= 2 && p[1] === ':') { + return p[0].toUpperCase() + p.slice(1); + } + return p; +} + +const rnwPath = normalizePathDrive(fs.realpathSync( path.resolve(require.resolve('react-native-windows/package.json'), '..'), -); +)); //{{#devMode}} [devMode const rnwRootNodeModules = path.resolve(rnwPath, '..', 'node_modules'); From 915dbbb442f13f6ff42bae0b252e83ba55ab1d0d Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Mon, 20 Apr 2026 12:04:57 -0700 Subject: [PATCH 6/8] Use process.cwd drive letter instead --- packages/playground/metro.config.js | 4 ++-- vnext/template/metro.config.js | 4 ++-- vnext/templates/cpp-app/metro.config.js | 4 ++-- vnext/templates/cpp-lib/example/metro.config.js | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/playground/metro.config.js b/packages/playground/metro.config.js index 3de2fbeea06..943efd6016e 100644 --- a/packages/playground/metro.config.js +++ b/packages/playground/metro.config.js @@ -11,10 +11,10 @@ const path = require('path'); // On Windows, require.resolve through symlinks (e.g. yarn workspace links) can // return paths with a different drive letter case than process.cwd(). Metro's -// file system lookup is case-sensitive, so we must normalize to match. +// file system lookup is case-sensitive, so we normalize to match cwd. function normalizePathDrive(p) { if (process.platform === 'win32' && p.length >= 2 && p[1] === ':') { - return p[0].toUpperCase() + p.slice(1); + return process.cwd()[0] + p.slice(1); } return p; } diff --git a/vnext/template/metro.config.js b/vnext/template/metro.config.js index a77a64c978a..682fb941e77 100644 --- a/vnext/template/metro.config.js +++ b/vnext/template/metro.config.js @@ -5,10 +5,10 @@ const path = require('path'); // On Windows, require.resolve through yarn workspace junctions can return paths // with a different drive letter case than process.cwd(). Metro's internal file -// system lookup is case-sensitive, so we normalize to match. +// system lookup is case-sensitive, so we normalize to match cwd. function normalizePathDrive(p) { if (process.platform === 'win32' && p.length >= 2 && p[1] === ':') { - return p[0].toUpperCase() + p.slice(1); + return process.cwd()[0] + p.slice(1); } return p; } diff --git a/vnext/templates/cpp-app/metro.config.js b/vnext/templates/cpp-app/metro.config.js index 077d503fea1..ea6a25d7d12 100644 --- a/vnext/templates/cpp-app/metro.config.js +++ b/vnext/templates/cpp-app/metro.config.js @@ -5,10 +5,10 @@ const path = require('node:path'); // On Windows, require.resolve through yarn workspace junctions can return paths // with a different drive letter case than process.cwd(). Metro's internal file -// system lookup is case-sensitive, so we normalize to match. +// system lookup is case-sensitive, so we normalize to match cwd. function normalizePathDrive(p) { if (process.platform === 'win32' && p.length >= 2 && p[1] === ':') { - return p[0].toUpperCase() + p.slice(1); + return process.cwd()[0] + p.slice(1); } return p; } diff --git a/vnext/templates/cpp-lib/example/metro.config.js b/vnext/templates/cpp-lib/example/metro.config.js index efe460f4c9d..24f4d29b489 100644 --- a/vnext/templates/cpp-lib/example/metro.config.js +++ b/vnext/templates/cpp-lib/example/metro.config.js @@ -9,10 +9,10 @@ const modules = Object.keys({ ...pack.peerDependencies }); // On Windows, require.resolve through yarn workspace junctions can return paths // with a different drive letter case than process.cwd(). Metro's internal file -// system lookup is case-sensitive, so we normalize to match. +// system lookup is case-sensitive, so we normalize to match cwd. function normalizePathDrive(p) { if (process.platform === 'win32' && p.length >= 2 && p[1] === ':') { - return p[0].toUpperCase() + p.slice(1); + return process.cwd()[0] + p.slice(1); } return p; } From 1fbf70c77eda28d535ec56f98883d1d4d1b6cdbf Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Mon, 20 Apr 2026 14:04:05 -0700 Subject: [PATCH 7/8] Add missing "--yes" flag --- vnext/PropertySheets/Bundle.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnext/PropertySheets/Bundle.props b/vnext/PropertySheets/Bundle.props index 7340439d5a2..156492da23b 100644 --- a/vnext/PropertySheets/Bundle.props +++ b/vnext/PropertySheets/Bundle.props @@ -39,7 +39,7 @@ --minify false - npx --no-workspaces @react-native-community/cli bundle + npx --yes --no-workspaces @react-native-community/cli bundle $([MSBuild]::GetDirectoryNameOfFileAbove($(ProjectDir), 'package.json')) From 0c985388650dcc741d7b0edef3afa18c7456bebd Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Mon, 20 Apr 2026 18:30:30 -0700 Subject: [PATCH 8/8] Fix Universal solution compilation in Visual Studio --- vnext/Directory.Build.targets | 16 ++++++++++++---- vnext/PropertySheets/NuGet.LockFile.props | 8 ++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/vnext/Directory.Build.targets b/vnext/Directory.Build.targets index a2ab4fd389c..f844708fea0 100644 --- a/vnext/Directory.Build.targets +++ b/vnext/Directory.Build.targets @@ -4,21 +4,29 @@ - - + + - + diff --git a/vnext/PropertySheets/NuGet.LockFile.props b/vnext/PropertySheets/NuGet.LockFile.props index 0cfb6b016ed..842fc28a3d7 100644 --- a/vnext/PropertySheets/NuGet.LockFile.props +++ b/vnext/PropertySheets/NuGet.LockFile.props @@ -9,6 +9,14 @@ true + + false packages $(NuGetLockFileName).experimentalwinui3