From 509c7a112d99b418dee14ac1347a8bdd27e53775 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Jun 2025 09:00:56 +0000 Subject: [PATCH 1/4] Initial plan for issue From b03c55079e7aff3211b52f45e12cb4e46ab59f39 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Jun 2025 09:08:35 +0000 Subject: [PATCH 2/4] Remove deprecated autolink functionality to copy useWinUI3 flags from react-native.config.js Co-authored-by: anupriya13 <54227869+anupriya13@users.noreply.github.com> --- .../autolinkWindows/autolinkWindows.ts | 23 +----- .../cli/src/e2etest/autolink.test.ts | 74 ------------------- .../cli/src/e2etest/projectConfig.test.ts | 45 ----------- 3 files changed, 2 insertions(+), 140 deletions(-) diff --git a/packages/@react-native-windows/cli/src/commands/autolinkWindows/autolinkWindows.ts b/packages/@react-native-windows/cli/src/commands/autolinkWindows/autolinkWindows.ts index 075ab1901dc..7592278db43 100644 --- a/packages/@react-native-windows/cli/src/commands/autolinkWindows/autolinkWindows.ts +++ b/packages/@react-native-windows/cli/src/commands/autolinkWindows/autolinkWindows.ts @@ -712,7 +712,6 @@ export class AutoLinkWindows { public async ensureXAMLDialect() { let changesNeeded = false; - const useWinUI3FromConfig = this.getWindowsConfig().useWinUI3; const experimentalFeatures = this.getExperimentalFeaturesPropsXml(); if (experimentalFeatures) { const useWinUI3FromExperimentalFeatures = @@ -729,30 +728,12 @@ export class AutoLinkWindows { experimentalFeatures.content, 'WinUI3Version', ); - // Use the UseWinUI3 value in react-native.config.js, or if not present, the value from ExperimentalFeatures.props + // Use the UseWinUI3 value from ExperimentalFeatures.props changesNeeded = await this.updatePackagesConfigXAMLDialect( - useWinUI3FromConfig !== undefined - ? useWinUI3FromConfig - : useWinUI3FromExperimentalFeatures, + useWinUI3FromExperimentalFeatures, targetWinUI2xVersion, targetWinUI3xVersion, ); - if (useWinUI3FromConfig !== undefined) { - // Make sure ExperimentalFeatures.props matches the value that comes from react-native.config.js - const node = - experimentalFeatures.content.getElementsByTagName('UseWinUI3'); - const newValue = useWinUI3FromConfig ? 'true' : 'false'; - changesNeeded = node.item(0)?.textContent !== newValue || changesNeeded; - if (!this.options.check && changesNeeded) { - node.item(0)!.textContent = newValue; - const experimentalFeaturesOutput = - new XMLSerializer().serializeToString(experimentalFeatures.content); - await this.updateFile( - experimentalFeatures.path, - experimentalFeaturesOutput, - ); - } - } } return changesNeeded; } diff --git a/packages/@react-native-windows/cli/src/e2etest/autolink.test.ts b/packages/@react-native-windows/cli/src/e2etest/autolink.test.ts index ac78d004fa4..25bb0a1e575 100644 --- a/packages/@react-native-windows/cli/src/e2etest/autolink.test.ts +++ b/packages/@react-native-windows/cli/src/e2etest/autolink.test.ts @@ -249,81 +249,7 @@ test('one valid cs autolink dependency', () => { ); }); -test('ensureXAMLDialect - useWinUI3=true in react-native.config.js, useWinUI3=false in ExperimentalFeatures.props', async () => { - const folder = path.resolve('src/e2etest/projects/WithWinUI3'); - - // Create project with UseWinUI3 == false in ExperimentalFeatures.props - await ensureCppAppProject(folder, 'WithWinUI3', false, false, false); - - const rnc = require(path.join(folder, 'react-native.config.js')); - - const config = projectConfigWindows(folder, rnc.project.windows)!; - // Set useWinUI3=true in react-native.config.js - config.useWinUI3 = true; - - const al = new AutoLinkTest( - {windows: config}, - {}, - { - check: false, - logging: false, - }, - ); - al.experimentalFeaturesProps = `false`; - al.packagesConfig = ``; - - const exd = await al.ensureXAMLDialect(); - expect(exd).toBeTruthy(); - - const expectedExperimentalFeatures = - 'true'; - expect(al.experimentalFeaturesProps).toEqual(expectedExperimentalFeatures); - - // example packages.config: - // - // - // - // - // - expect(al.packagesConfig).toContain('Microsoft.WindowsAppSDK'); - expect(al.packagesConfig).toContain(''); - expect(al.packagesConfig).not.toContain('Microsoft.UI.Xaml'); -}); - -test('ensureXAMLDialect - useWinUI3=false in react-native.config.js, useWinUI3=true in ExperimentalFeatures.props', async () => { - const folder = path.resolve('src/e2etest/projects/WithWinUI3'); - const rnc = require(path.join(folder, 'react-native.config.js')); - - const config = projectConfigWindows(folder, rnc.project.windows)!; - config.useWinUI3 = false; - const al = new AutoLinkTest( - {windows: config}, - {}, - { - check: false, - logging: false, - }, - ); - al.experimentalFeaturesProps = `true`; - al.packagesConfig = ``; - const exd = await al.ensureXAMLDialect(); - expect(exd).toBeTruthy(); - - const expectedExperimentalFeatures = - 'false'; - expect(al.experimentalFeaturesProps).toEqual(expectedExperimentalFeatures); - - // example packages.config: - // - // - // - // - // - expect(al.packagesConfig).not.toContain('Microsoft.WindowsAppSDK'); - expect(al.packagesConfig).toContain(''); - expect(al.packagesConfig).toContain('Microsoft.UI.Xaml'); -}); test('ensureXAMLDialect - useWinUI3 not in react-native.config.js, useWinUI3=true in ExperimentalFeatures.props', async () => { const folder = path.resolve('src/e2etest/projects/WithWinUI3'); diff --git a/packages/@react-native-windows/cli/src/e2etest/projectConfig.test.ts b/packages/@react-native-windows/cli/src/e2etest/projectConfig.test.ts index c04bdd4ee66..b6d9bba84fc 100644 --- a/packages/@react-native-windows/cli/src/e2etest/projectConfig.test.ts +++ b/packages/@react-native-windows/cli/src/e2etest/projectConfig.test.ts @@ -119,48 +119,3 @@ test.each(projects)( } }, ); - -// Skipping this test as the feature is broken and needs to be removed, see https://github.com/microsoft/react-native-windows/issues/14601 -test.skip('useWinUI3=true in react-native.config.js, UseWinUI3=false in ExperimentalFeatures.props', async () => { - const folder = path.resolve('src/e2etest/projects/WithWinUI3'); - - // Create project with UseWinUI3 == false in ExperimentalFeatures.props - await ensureCppAppProject(folder, 'WithWinUI3', false, false, false); - - const rnc = require(path.join(folder, 'react-native.config.js')); - - const config = projectConfigWindows(folder, rnc.project.windows)!; - // Set useWinUI3=true in react-native.config.js - config.useWinUI3 = true; - - const experimentalFeaturesPropsFile = path.join( - folder, - 'windows/ExperimentalFeatures.props', - ); - - // Verify starting props file - const startingExperimentalFeatures = ( - await fs.readFile(experimentalFeaturesPropsFile) - ).toString(); - expect(startingExperimentalFeatures.replace(/\r/g, '')).toMatchSnapshot(); - - // Run Autolink to sync the files - const al = new AutoLinkWindows( - {windows: config}, - {}, - { - check: false, - logging: false, - }, - ); - - const exd = await al.ensureXAMLDialect(); - expect(exd).toBeTruthy(); - - // Verify ending props file - const finalExperimentalFeatures = ( - await fs.readFile(experimentalFeaturesPropsFile) - ).toString(); - - expect(finalExperimentalFeatures.replace(/\r/g, '')).toMatchSnapshot(); -}); From 5af8740e297f4321d045821de14dbc152b0eee84 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Jun 2025 09:35:20 +0000 Subject: [PATCH 3/4] Add change file for deprecating autolink useWinUI3 functionality --- ...e-windows-cli-d7a7f24d-f06e-4a41-b87a-1def406d65f2.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@react-native-windows-cli-d7a7f24d-f06e-4a41-b87a-1def406d65f2.json diff --git a/change/@react-native-windows-cli-d7a7f24d-f06e-4a41-b87a-1def406d65f2.json b/change/@react-native-windows-cli-d7a7f24d-f06e-4a41-b87a-1def406d65f2.json new file mode 100644 index 00000000000..3a49fdad25c --- /dev/null +++ b/change/@react-native-windows-cli-d7a7f24d-f06e-4a41-b87a-1def406d65f2.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Remove deprecated autolink functionality to copy useWinUI3 flags from react-native.config.js into ExperimentalFeatures.props", + "packageName": "@react-native-windows/cli", + "email": "copilot@github.com", + "dependentChangeType": "prerelease" +} \ No newline at end of file From 7ce01eefcffad7fd30ad777b3adeb16b9e498078 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 1 Jul 2025 21:03:23 +0000 Subject: [PATCH 4/4] Completely remove ensureXAMLDialect() function and its functionality Co-authored-by: jonthysell <10852185+jonthysell@users.noreply.github.com> --- .../autolinkWindows/autolinkWindows.ts | 162 +----------------- .../cli/src/e2etest/autolink.test.ts | 101 +---------- 2 files changed, 3 insertions(+), 260 deletions(-) diff --git a/packages/@react-native-windows/cli/src/commands/autolinkWindows/autolinkWindows.ts b/packages/@react-native-windows/cli/src/commands/autolinkWindows/autolinkWindows.ts index 7592278db43..91835a3b132 100644 --- a/packages/@react-native-windows/cli/src/commands/autolinkWindows/autolinkWindows.ts +++ b/packages/@react-native-windows/cli/src/commands/autolinkWindows/autolinkWindows.ts @@ -13,9 +13,9 @@ import fs from '@react-native-windows/fs'; import path from 'path'; import chalk from 'chalk'; import {performance} from 'perf_hooks'; -import {XMLSerializer} from '@xmldom/xmldom'; + import {Ora} from 'ora'; -const formatter = require('xml-formatter'); + import type { Command, @@ -114,9 +114,6 @@ export class AutoLinkWindows { verboseMessage('Parsing dependencies...', verbose); - this.changesNecessary = - (await this.ensureXAMLDialect()) || this.changesNecessary; - // Generating cs/cpp files for app code consumption if (projectLang === 'cs') { this.changesNecessary = @@ -693,161 +690,6 @@ export class AutoLinkWindows { return changesNecessary; } - protected getExperimentalFeaturesPropsXml() { - const experimentalFeaturesProps = path.join( - path.dirname(this.getSolutionFile()), - 'ExperimentalFeatures.props', - ); - if (fs.existsSync(experimentalFeaturesProps)) { - const experimentalFeaturesContents = configUtils.readProjectFile( - experimentalFeaturesProps, - ); - return { - path: experimentalFeaturesProps, - content: experimentalFeaturesContents, - }; - } - return undefined; - } - - public async ensureXAMLDialect() { - let changesNeeded = false; - const experimentalFeatures = this.getExperimentalFeaturesPropsXml(); - if (experimentalFeatures) { - const useWinUI3FromExperimentalFeatures = - configUtils - .tryFindPropertyValue(experimentalFeatures.content, 'UseWinUI3') - ?.toLowerCase() === 'true'; - // Check if WinUI2xVersion is specified in experimental features - const targetWinUI2xVersion = configUtils.tryFindPropertyValue( - experimentalFeatures.content, - 'WinUI2xVersion', - ); - // Check if WinUI3Version is specified in experimental features - const targetWinUI3xVersion = configUtils.tryFindPropertyValue( - experimentalFeatures.content, - 'WinUI3Version', - ); - // Use the UseWinUI3 value from ExperimentalFeatures.props - changesNeeded = await this.updatePackagesConfigXAMLDialect( - useWinUI3FromExperimentalFeatures, - targetWinUI2xVersion, - targetWinUI3xVersion, - ); - } - return changesNeeded; - } - - protected getPackagesConfigXml() { - const projectFile = this.getProjectFile(); - const packagesConfig = path.join( - path.dirname(projectFile), - 'packages.config', - ); - - if (fs.existsSync(packagesConfig)) { - return { - path: packagesConfig, - content: configUtils.readProjectFile(packagesConfig), - }; - } - return undefined; - } - - private async updatePackagesConfigXAMLDialect( - useWinUI3: boolean, - targetWinUI2xVersion: string | null, - targetWinUI3xVersion: string | null, - ) { - let changed = false; - const packagesConfig = this.getPackagesConfigXml(); - if (packagesConfig) { - // if we don't have a packages.config, then this is a C# project, in which case we use and dynamically pick the right XAML package. - const project = this.getWindowsConfig(); - - const winUIPropsPath = path.join( - resolveRnwRoot(project), - 'PropertySheets/WinUI.props', - ); - const winuiPropsContents = configUtils.readProjectFile(winUIPropsPath); - - // Use the given WinUI2xVersion, otherwise fallback to WinUI.props - const winui2xVersion = - targetWinUI2xVersion ?? - configUtils.tryFindPropertyValue(winuiPropsContents, 'WinUI2xVersion'); - - // Use the given WinUI3Version, otherwise fallback to WinUI.props - const winui3Version = - targetWinUI3xVersion ?? - configUtils.tryFindPropertyValue(winuiPropsContents, 'WinUI3Version'); - - const dialects = [ - {id: 'Microsoft.WindowsAppSDK', version: winui3Version!}, - {id: 'Microsoft.UI.Xaml', version: winui2xVersion!}, - ]; - const keepPkg = useWinUI3 ? dialects[0] : dialects[1]; - const removePkg = useWinUI3 ? dialects[1] : dialects[0]; - - changed = this.updatePackagesConfig( - packagesConfig, - [removePkg], - [keepPkg], - ); - - if (!this.options.check && changed) { - const serializer = new XMLSerializer(); - const output = serializer.serializeToString(packagesConfig.content); - const formattedXml = formatter(output, {indentation: ' '}); - await this.updateFile(packagesConfig.path, formattedXml); - } - } - return changed; - } - - private updatePackagesConfig( - packagesConfig: {path: string; content: Document}, - removePkgs: {id: string; version: string}[], - keepPkgs: {id: string; version: string}[], - ) { - let changed = false; - const packageElements = - packagesConfig.content.documentElement.getElementsByTagName('package'); - - const nodesToRemove: Element[] = []; - - for (let i = 0; i < packageElements.length; i++) { - const packageElement = packageElements.item(i)!; - const idAttr = packageElement!.getAttributeNode('id'); - const id = idAttr!.value; - const keepPkg = keepPkgs.find(pkg => pkg.id === id); - if (removePkgs.find(pkg => pkg.id === id)) { - nodesToRemove.push(packageElement); - changed = true; - } else if (keepPkg) { - changed = - changed || keepPkg.version !== packageElement.getAttribute('version'); - packageElement.setAttribute('version', keepPkg.version!); - keepPkgs = keepPkgs.filter(pkg => pkg.id !== keepPkg.id); - } - } - - nodesToRemove.forEach(pkg => - packagesConfig.content.documentElement.removeChild(pkg), - ); - - keepPkgs.forEach(keepPkg => { - const newPkg = packagesConfig.content.createElement('package'); - - Object.entries(keepPkg).forEach(([attr, value]) => { - newPkg.setAttribute(attr, value as string); - }); - newPkg.setAttribute('targetFramework', 'native'); - packagesConfig.content.documentElement.appendChild(newPkg); - changed = true; - }); - return changed; - } - /** @return The CLI command to invoke autolink-windows independently */ public getAutolinkWindowsCommand() { const folder = this.windowsAppConfig.folder; diff --git a/packages/@react-native-windows/cli/src/e2etest/autolink.test.ts b/packages/@react-native-windows/cli/src/e2etest/autolink.test.ts index 25bb0a1e575..1c92146161e 100644 --- a/packages/@react-native-windows/cli/src/e2etest/autolink.test.ts +++ b/packages/@react-native-windows/cli/src/e2etest/autolink.test.ts @@ -6,14 +6,12 @@ import path from 'path'; import {commanderNameToOptionName} from '@react-native-windows/telemetry'; -import {projectConfigWindows} from '../commands/config/projectConfig'; import {AutoLinkWindows} from '../commands/autolinkWindows/autolinkWindows'; import { AutoLinkOptions, autolinkOptions, } from '../commands/autolinkWindows/autolinkWindowsOptions'; -import {DOMParser} from '@xmldom/xmldom'; -import {ensureCppAppProject, ensureWinUI3Project} from './projectConfig.utils'; +import {ensureWinUI3Project} from './projectConfig.utils'; test('autolink with no windows project', () => { expect(() => { @@ -37,34 +35,7 @@ class AutoLinkTest extends AutoLinkWindows { public getWindowsProjectConfig() { return this.windowsAppConfig; } - public packagesConfig = ''; - public experimentalFeaturesProps = ''; - protected getPackagesConfigXml() { - return { - path: 'packages.config', - content: new DOMParser().parseFromString( - this.packagesConfig, - 'application/xml', - ), - }; - } - protected getExperimentalFeaturesPropsXml() { - return { - path: 'ExperimentalFeatures.props', - content: new DOMParser().parseFromString( - this.experimentalFeaturesProps, - 'application/xml', - ), - }; - } protected async updateFile(filepath: string, content: string) { - if (filepath === 'packages.config') { - this.packagesConfig = content; - } else if (filepath === 'ExperimentalFeatures.props') { - this.experimentalFeaturesProps = content; - } else { - throw new Error(`Unknown path: ${filepath}`); - } return true; } } @@ -250,76 +221,6 @@ test('one valid cs autolink dependency', () => { }); - -test('ensureXAMLDialect - useWinUI3 not in react-native.config.js, useWinUI3=true in ExperimentalFeatures.props', async () => { - const folder = path.resolve('src/e2etest/projects/WithWinUI3'); - const rnc = require(path.join(folder, 'react-native.config.js')); - - const config = projectConfigWindows(folder, rnc.project.windows)!; - delete config.useWinUI3; - const al = new AutoLinkTest( - {windows: config}, - {}, - { - check: false, - logging: false, - }, - ); - al.experimentalFeaturesProps = `true`; - al.packagesConfig = ``; - - const exd = await al.ensureXAMLDialect(); - expect(exd).toBeTruthy(); - - const expectedExperimentalFeatures = - 'true'; - expect(al.experimentalFeaturesProps).toEqual(expectedExperimentalFeatures); - - // example packages.config: - // - // - // - // - // - expect(al.packagesConfig).toContain('Microsoft.WindowsAppSDK'); - expect(al.packagesConfig).toContain(''); - expect(al.packagesConfig).not.toContain('Microsoft.UI.Xaml'); -}); - -test('ensureXAMLDialect - useWinUI3 not in react-native.config.js, useWinUI3=false in ExperimentalFeatures.props', async () => { - const folder = path.resolve('src/e2etest/projects/WithWinUI3'); - const rnc = require(path.join(folder, 'react-native.config.js')); - - const config = projectConfigWindows(folder, rnc.project.windows)!; - delete config.useWinUI3; - const al = new AutoLinkTest( - {windows: config}, - {}, - { - check: false, - logging: false, - }, - ); - al.experimentalFeaturesProps = `false`; - al.packagesConfig = ``; - - const exd = await al.ensureXAMLDialect(); - expect(exd).toBeTruthy(); - - const expectedExperimentalFeatures = `false`; - expect(al.experimentalFeaturesProps).toEqual(expectedExperimentalFeatures); - - // example packages.config: - // - // - // - // - // - expect(al.packagesConfig).not.toContain('Microsoft.WindowsAppSDK'); - expect(al.packagesConfig).toContain(''); - expect(al.packagesConfig).toContain('Microsoft.UI.Xaml'); -}); - test('Indirect autolink dependency', () => { const autolink = new AutoLinkTest( {windows: {folder: __dirname, sourceDir: '.', solutionFile: 'foo.sln'}},