From edc3a071f9f47fa718e832fe21e76e747153453a Mon Sep 17 00:00:00 2001 From: "Jon Thysell (JAUNTY)" Date: Fri, 1 Dec 2023 12:12:38 -0800 Subject: [PATCH 01/10] Create new arch module template: cpp-lib ## Description Adds a new `cpp-lib` template designed to be applied on top of the new projects created by the (now recommended) `create-react-native-library` script. ### Type of Change - New feature (non-breaking change which adds functionality) - This change requires a documentation update ### Why Devs need to add windows support to native modules in a way which can be consumed by the new arch RNW apps (i.e. the `cpp-app` template). Resolves #12480 ### What Create a new `cpp-lib` template which can be applied on top of the template created by `npx create-react-native-library`, adding Windows support for a turbo module. It will also automatically apply the `cpp-app` template to the example project if it exists. ## Screenshots image ## Testing Verified the new template could be applied to a fresh library created by `create-react-native-library` and that example app autolinks, builds, and runs correctly. ## Changelog Should this change be included in the release notes: yes Created a new arch module template: cpp-lib --- .ado/jobs/cli-init-windows.yml | 30 +++ .ado/templates/react-native-init-windows.yml | 86 ++++--- ...-f2a1a001-7c49-48af-8a21-a5947fbec507.json | 7 + ...osoft.ReactNative.Composition.CppLib.props | 24 ++ ...oft.ReactNative.Composition.CppLib.targets | 20 ++ vnext/templates/cpp-app/template.config.js | 7 +- .../MyApp/AutolinkedNativeModules.g.cpp | 13 + .../windows/MyApp/AutolinkedNativeModules.g.h | 10 + .../templates/cpp-app/windows/MyApp/MyApp.cpp | 8 +- .../cpp-app/windows/MyApp/MyApp.vcxproj | 2 + .../windows/MyApp/MyApp.vcxproj.filters | 6 + .../templates/cpp-lib/example/metro.config.js | 77 ++++++ vnext/templates/cpp-lib/template.config.js | 243 ++++++++++++++++++ .../windows/ExperimentalFeatures.props | 11 + vnext/templates/cpp-lib/windows/MyLib.sln | 156 +++++++++++ .../templates/cpp-lib/windows/MyLib/MyLib.cpp | 18 ++ .../templates/cpp-lib/windows/MyLib/MyLib.def | 3 + vnext/templates/cpp-lib/windows/MyLib/MyLib.h | 31 +++ .../templates/cpp-lib/windows/MyLib/MyLib.rc | Bin 0 -> 160 bytes .../cpp-lib/windows/MyLib/MyLib.vcxproj | 140 ++++++++++ .../windows/MyLib/MyLib.vcxproj.filters | 44 ++++ .../windows/MyLib/ReactPackageProvider.cpp | 20 ++ .../windows/MyLib/ReactPackageProvider.h | 24 ++ .../windows/MyLib/ReactPackageProvider.idl | 9 + .../cpp-lib/windows/MyLib/_gitignore | 1 + vnext/templates/cpp-lib/windows/MyLib/pch.cpp | 1 + vnext/templates/cpp-lib/windows/MyLib/pch.h | 35 +++ .../cpp-lib/windows/MyLib/resource.h | 5 + .../cpp-lib/windows/MyLib/targetver.h | 8 + vnext/templates/cpp-lib/windows/_gitignore | 41 +++ vnext/templates/templateUtils.js | 69 ++++- 31 files changed, 1108 insertions(+), 41 deletions(-) create mode 100644 change/react-native-windows-f2a1a001-7c49-48af-8a21-a5947fbec507.json create mode 100644 vnext/PropertySheets/External/Microsoft.ReactNative.Composition.CppLib.props create mode 100644 vnext/PropertySheets/External/Microsoft.ReactNative.Composition.CppLib.targets create mode 100644 vnext/templates/cpp-app/windows/MyApp/AutolinkedNativeModules.g.cpp create mode 100644 vnext/templates/cpp-app/windows/MyApp/AutolinkedNativeModules.g.h create mode 100644 vnext/templates/cpp-lib/example/metro.config.js create mode 100644 vnext/templates/cpp-lib/template.config.js create mode 100644 vnext/templates/cpp-lib/windows/ExperimentalFeatures.props create mode 100644 vnext/templates/cpp-lib/windows/MyLib.sln create mode 100644 vnext/templates/cpp-lib/windows/MyLib/MyLib.cpp create mode 100644 vnext/templates/cpp-lib/windows/MyLib/MyLib.def create mode 100644 vnext/templates/cpp-lib/windows/MyLib/MyLib.h create mode 100644 vnext/templates/cpp-lib/windows/MyLib/MyLib.rc create mode 100644 vnext/templates/cpp-lib/windows/MyLib/MyLib.vcxproj create mode 100644 vnext/templates/cpp-lib/windows/MyLib/MyLib.vcxproj.filters create mode 100644 vnext/templates/cpp-lib/windows/MyLib/ReactPackageProvider.cpp create mode 100644 vnext/templates/cpp-lib/windows/MyLib/ReactPackageProvider.h create mode 100644 vnext/templates/cpp-lib/windows/MyLib/ReactPackageProvider.idl create mode 100644 vnext/templates/cpp-lib/windows/MyLib/_gitignore create mode 100644 vnext/templates/cpp-lib/windows/MyLib/pch.cpp create mode 100644 vnext/templates/cpp-lib/windows/MyLib/pch.h create mode 100644 vnext/templates/cpp-lib/windows/MyLib/resource.h create mode 100644 vnext/templates/cpp-lib/windows/MyLib/targetver.h create mode 100644 vnext/templates/cpp-lib/windows/_gitignore diff --git a/.ado/jobs/cli-init-windows.yml b/.ado/jobs/cli-init-windows.yml index 35e9736c8bb..cacf9e74b01 100644 --- a/.ado/jobs/cli-init-windows.yml +++ b/.ado/jobs/cli-init-windows.yml @@ -22,6 +22,16 @@ parameters: configuration: Debug platform: x86 additionalRunArguments: --no-autolink + - Name: FabricLibX64Release + template: cpp-lib + configuration: Release + platform: x64 + additionalRunArguments: + - Name: FabricLibX86Debug + template: cpp-lib + configuration: Debug + platform: x86 + additionalRunArguments: - BuildEnvironment: Continuous Matrix: - Name: FabricX64Debug @@ -44,6 +54,26 @@ parameters: configuration: Release platform: x86 additionalRunArguments: --no-autolink + - Name: FabricLibX64Debug + template: cpp-lib + configuration: Debug + platform: x64 + additionalRunArguments: + - Name: FabricLibX64Release + template: cpp-lib + configuration: ReleaseLib + platform: x64 + additionalRunArguments: + - Name: FabricLibX86Debug + template: cpp-lib + configuration: Debug + platform: x86 + additionalRunArguments: + - Name: FabricLibX86Release + template: cpp-lib + configuration: Release + platform: x86 + additionalRunArguments: jobs: - ${{ each config in parameters.buildMatrix }}: - ${{ if eq(config.BuildEnvironment, parameters.buildEnvironment) }}: diff --git a/.ado/templates/react-native-init-windows.yml b/.ado/templates/react-native-init-windows.yml index 7d977a95082..3fdfabad539 100644 --- a/.ado/templates/react-native-init-windows.yml +++ b/.ado/templates/react-native-init-windows.yml @@ -40,49 +40,71 @@ steps: - ${{ if endsWith(parameters.template, '-app') }}: - script: | npx --yes react-native@$(reactNativeDevDependency) init testcli --template react-native@$(reactNativeDevDependency) - displayName: Init new app project + displayName: Init new app project with react-native init workingDirectory: $(Agent.BuildDirectory) - ${{ if endsWith(parameters.template, '-lib') }}: - - script: | - npx --yes create-react-native-module@0.20.2 --package-name "testcli" testcli - displayName: Init new lib project + - pwsh: | + npx --yes create-react-native-library@latest --slug testcli --description testcli --author-name "React-Native-Windows Bot" --author-email 53619745+rnbot@users.noreply.github.com --author-url http://example.com --repo-url http://example.com --languages java-objc --type module-new --react-native-version $(reactNativeDevDependency) testcli + ((Get-Content -Path testcli\.yarnrc.yml -Raw) -replace " - path: scripts/pod-install.cjs") | Set-Content -Encoding UTF8 testcli\.yarnrc.yml + displayName: Init new lib project with create-react-native-library workingDirectory: $(Agent.BuildDirectory) - - script: | - rmdir /s /q android - displayName: Remove broken android folder # See issue https://github.com/microsoft/react-native-windows/issues/12209 - workingDirectory: $(Agent.BuildDirectory)\testcli - - script: | call yarn install - call yarn upgrade react@$(reactDevDependency) --dev - call yarn upgrade react-native@$(reactNativeDevDependency) --dev - displayName: Update project react and react-native dev versions - workingDirectory: $(Agent.BuildDirectory)\testcli - - - script: | - call yarn add react-native-windows@$(npmVersion) - displayName: yarn add react-native-windows@$(npmVersion) + displayName: yarn install workingDirectory: $(Agent.BuildDirectory)\testcli env: - npm_config_registry: http://localhost:4873 + YARN_ENABLE_IMMUTABLE_INSTALLS: false + + - ${{ if endsWith(parameters.template, '-app') }}: + - script: | + call yarn upgrade react@$(reactDevDependency) --dev + call yarn upgrade react-native@$(reactNativeDevDependency) --dev + displayName: Update project react and react-native dev versions + workingDirectory: $(Agent.BuildDirectory)\testcli + + - script: | + call yarn add react-native-windows@$(npmVersion) + displayName: yarn add react-native-windows@$(npmVersion) + workingDirectory: $(Agent.BuildDirectory)\testcli + env: + npm_config_registry: http://localhost:4873 + + - ${{ if endsWith(parameters.template, '-lib') }}: + - script: | + call yarn config set npmRegistryServer http://localhost:4873 + call yarn config set unsafeHttpWhitelist --json "[\"localhost\"]" + call yarn add react-native-windows@$(npmVersion) --dev + call yarn add react-native-windows@* --peer + displayName: yarn add react-native-windows@$(npmVersion) + workingDirectory: $(Agent.BuildDirectory)\testcli + env: + YARN_ENABLE_IMMUTABLE_INSTALLS: false - script: | call yarn react-native init-windows --template ${{ parameters.template }} --overwrite --logging ${{ parameters.additionalInitArguments }} displayName: Call react-native init-windows workingDirectory: $(Agent.BuildDirectory)\testcli - env: - npm_config_registry: http://localhost:4873 - - ${{ if endsWith(parameters.template, '-app') }}: - - powershell: | - $path = (Get-ChildItem -Filter "Package.appxmanifest" -File -Recurse).FullName; - [xml] $manifest = Get-Content $path - $manifest.Package.Identity.Name = 'ReactNative.InitTest' - $manifest.Save("$path") - displayName: Set AppX package name to "ReactNative.InitTest" + - ${{ if endsWith(parameters.template, '-lib') }}: + - script: | + call yarn install + displayName: yarn install again + workingDirectory: $(Agent.BuildDirectory)\testcli + env: + YARN_ENABLE_IMMUTABLE_INSTALLS: false + + - powershell: | + $path = (Get-ChildItem -Filter "Package.appxmanifest" -File -Recurse).FullName; + [xml] $manifest = Get-Content $path + $manifest.Package.Identity.Name = 'ReactNative.InitTest' + $manifest.Save("$path") + displayName: Set AppX package name to "ReactNative.InitTest" + ${{ if endsWith(parameters.template, '-app') }}: workingDirectory: $(Agent.BuildDirectory)\testcli\windows + ${{ if endsWith(parameters.template, '-lib') }}: + workingDirectory: $(Agent.BuildDirectory)\testcli\example\windows # End npm test server - template: verdaccio-stop.yml @@ -95,7 +117,10 @@ steps: - template: react-native-debug-info.yml parameters: - workingDirectory: $(Agent.BuildDirectory)\testcli + ${{ if endsWith(parameters.template, '-app') }}: + workingDirectory: $(Agent.BuildDirectory)\testcli + ${{ if endsWith(parameters.template, '-lib') }}: + workingDirectory: $(Agent.BuildDirectory)\testcli\example - template: ../templates/run-windows-with-certificates.yml parameters: @@ -105,7 +130,10 @@ steps: buildPlatform: ${{ parameters.platform }} deployOption: ${{ parameters.additionalRunArguments }} buildLogDirectory: $(Build.BinariesDirectory)\${{ parameters.platform }}\${{ parameters.configuration }}\BuildLogs - workingDirectory: $(Agent.BuildDirectory)\testcli + ${{ if endsWith(parameters.template, '-app') }}: + workingDirectory: $(Agent.BuildDirectory)\testcli + ${{ if endsWith(parameters.template, '-lib') }}: + workingDirectory: $(Agent.BuildDirectory)\testcli\example restoreLockedMode: false # Allow new lockfile to be created - template: upload-build-logs.yml diff --git a/change/react-native-windows-f2a1a001-7c49-48af-8a21-a5947fbec507.json b/change/react-native-windows-f2a1a001-7c49-48af-8a21-a5947fbec507.json new file mode 100644 index 00000000000..b15137fc642 --- /dev/null +++ b/change/react-native-windows-f2a1a001-7c49-48af-8a21-a5947fbec507.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Create new arch module template: cpp-lib", + "packageName": "react-native-windows", + "email": "jthysell@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/vnext/PropertySheets/External/Microsoft.ReactNative.Composition.CppLib.props b/vnext/PropertySheets/External/Microsoft.ReactNative.Composition.CppLib.props new file mode 100644 index 00000000000..f9f20164222 --- /dev/null +++ b/vnext/PropertySheets/External/Microsoft.ReactNative.Composition.CppLib.props @@ -0,0 +1,24 @@ + + + + + + true + false + + + + + + + diff --git a/vnext/PropertySheets/External/Microsoft.ReactNative.Composition.CppLib.targets b/vnext/PropertySheets/External/Microsoft.ReactNative.Composition.CppLib.targets new file mode 100644 index 00000000000..aa824160f5f --- /dev/null +++ b/vnext/PropertySheets/External/Microsoft.ReactNative.Composition.CppLib.targets @@ -0,0 +1,20 @@ + + + + + + + + + + + + + diff --git a/vnext/templates/cpp-app/template.config.js b/vnext/templates/cpp-app/template.config.js index 4627b7c6488..a8466eb8b03 100644 --- a/vnext/templates/cpp-app/template.config.js +++ b/vnext/templates/cpp-app/template.config.js @@ -20,12 +20,7 @@ const templateUtils = require('../templateUtils'); async function preInstall(config = {}, options = {}) {} async function getFileMappings(config = {}, options = {}) { - const rnwPath = path.dirname( - require.resolve('react-native-windows', [config.root]), - ); - const rnwVersion = require(path.join(rnwPath, 'package.json')).version; - - const devMode = existsSync(path.join(rnwPath, 'src')); + const {rnwVersion, devMode} = templateUtils.getRnwInfo(config, options); const projectName = config?.project?.windows?.project?.projectName ?? options?.name ?? 'MyApp'; diff --git a/vnext/templates/cpp-app/windows/MyApp/AutolinkedNativeModules.g.cpp b/vnext/templates/cpp-app/windows/MyApp/AutolinkedNativeModules.g.cpp new file mode 100644 index 00000000000..aa6e5db813d --- /dev/null +++ b/vnext/templates/cpp-app/windows/MyApp/AutolinkedNativeModules.g.cpp @@ -0,0 +1,13 @@ +// AutolinkedNativeModules.g.cpp contents generated by "react-native autolink-windows" +// clang-format off +#include "pch.h" +#include "AutolinkedNativeModules.g.h"{{ &autolinkCppIncludes }} + +namespace winrt::Microsoft::ReactNative +{ + +void RegisterAutolinkedNativeModulePackages(winrt::Windows::Foundation::Collections::IVector const& packageProviders) +{ {{ &autolinkCppPackageProviders }} +} + +} diff --git a/vnext/templates/cpp-app/windows/MyApp/AutolinkedNativeModules.g.h b/vnext/templates/cpp-app/windows/MyApp/AutolinkedNativeModules.g.h new file mode 100644 index 00000000000..f28bb8be361 --- /dev/null +++ b/vnext/templates/cpp-app/windows/MyApp/AutolinkedNativeModules.g.h @@ -0,0 +1,10 @@ +// AutolinkedNativeModules.g.h contents generated by "react-native autolink-windows" +// clang-format off +#pragma once + +namespace winrt::Microsoft::ReactNative +{ + +void RegisterAutolinkedNativeModulePackages(winrt::Windows::Foundation::Collections::IVector const& packageProviders); + +} diff --git a/vnext/templates/cpp-app/windows/MyApp/MyApp.cpp b/vnext/templates/cpp-app/windows/MyApp/MyApp.cpp index ce0317ee108..bf5b49d538d 100644 --- a/vnext/templates/cpp-app/windows/MyApp/MyApp.cpp +++ b/vnext/templates/cpp-app/windows/MyApp/MyApp.cpp @@ -4,6 +4,8 @@ #include "pch.h" #include "{{ name }}.h" +#include "AutolinkedNativeModules.g.h" + #include "../../node_modules/react-native-windows/codegen/NativeDeviceInfoSpec.g.h" #include @@ -127,9 +129,9 @@ struct WindowData { GetCurrentDirectory(MAX_PATH, workingDir); auto host = Host(); - // Disable until we have a 3rd party story for custom components - // RegisterAutolinkedNativeModulePackages(host.PackageProviders()); // Includes any - // autolinked modules + + // Include any autolinked modules + RegisterAutolinkedNativeModulePackages(host.PackageProviders()); host.InstanceSettings().JavaScriptBundleFile(m_bundleFile); diff --git a/vnext/templates/cpp-app/windows/MyApp/MyApp.vcxproj b/vnext/templates/cpp-app/windows/MyApp/MyApp.vcxproj index 1a019b4e4dc..276e3305cd0 100644 --- a/vnext/templates/cpp-app/windows/MyApp/MyApp.vcxproj +++ b/vnext/templates/cpp-app/windows/MyApp/MyApp.vcxproj @@ -101,11 +101,13 @@ + + Create Create diff --git a/vnext/templates/cpp-app/windows/MyApp/MyApp.vcxproj.filters b/vnext/templates/cpp-app/windows/MyApp/MyApp.vcxproj.filters index bd8163ead6e..a1d9ae1a198 100644 --- a/vnext/templates/cpp-app/windows/MyApp/MyApp.vcxproj.filters +++ b/vnext/templates/cpp-app/windows/MyApp/MyApp.vcxproj.filters @@ -24,6 +24,9 @@ Header Files + + Header Files + Header Files @@ -32,6 +35,9 @@ Source Files + + Source Files + Source Files diff --git a/vnext/templates/cpp-lib/example/metro.config.js b/vnext/templates/cpp-lib/example/metro.config.js new file mode 100644 index 00000000000..408bdd6bdcd --- /dev/null +++ b/vnext/templates/cpp-lib/example/metro.config.js @@ -0,0 +1,77 @@ +const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); +const fs = require('fs'); +const path = require('path'); +const escape = require('escape-string-regexp'); +const exclusionList = require('metro-config/src/defaults/exclusionList'); +const pak = require('../package.json'); + +const root = path.resolve(__dirname, '..'); +const modules = Object.keys({ ...pak.peerDependencies }); + +const rnwPath = fs.realpathSync( + path.resolve(require.resolve('react-native-windows/package.json'), '..'), +); + +//{{#devMode}} [devMode +const rnwRootNodeModules = path.resolve(rnwPath, '..', 'node_modules'); +const rnwPackages = path.resolve(rnwPath, '..', 'packages'); +// devMode]{{/devMode}} + +/** + * Metro configuration + * https://facebook.github.io/metro/docs/configuration + * + * @type {import('metro-config').MetroConfig} + */ +const config = { + watchFolders: [root, + //{{#devMode}} [devMode + rnwPath, rnwRootNodeModules, rnwPackages + // devMode]{{/devMode}} + ], + + // We need to make sure that only one version is loaded for peerDependencies + // So we block them at the root, and alias them to the versions in example's node_modules + resolver: { + blacklistRE: exclusionList( + modules.map( + (m) => + new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) + ).concat([ + // This stops "react-native run-windows" from causing the metro server to crash if its already running + new RegExp( + `${path.resolve(__dirname, 'windows').replace(/[/\\]/g, '/')}.*`, + ), + // This prevents "react-native run-windows" from hitting: EBUSY: resource busy or locked, open msbuild.ProjectImports.zip or other files produced by msbuild + new RegExp(`${rnwPath}/build/.*`), + new RegExp(`${rnwPath}/target/.*`), + /.*\.ProjectImports\.zip/, + ]) + ), + + extraNodeModules: modules.reduce((acc, name) => { + acc[name] = path.join(__dirname, 'node_modules', name); + return acc; + }, + { + //{{#devMode}} [devMode + 'react-native-windows': rnwPath, + // devMode]{{/devMode}} + } + ), + }, + + transformer: { + getTransformOptions: async () => ({ + transform: { + experimentalImportSupport: false, + inlineRequires: true, + }, + }), + + // This fixes the 'missing-asset-registry-path` error (see https://github.com/microsoft/react-native-windows/issues/11437) + assetRegistryPath: 'react-native/Libraries/Image/AssetRegistry', + }, +}; + +module.exports = mergeConfig(getDefaultConfig(__dirname), config); diff --git a/vnext/templates/cpp-lib/template.config.js b/vnext/templates/cpp-lib/template.config.js new file mode 100644 index 00000000000..13caca5d42d --- /dev/null +++ b/vnext/templates/cpp-lib/template.config.js @@ -0,0 +1,243 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * + * @ts check + * @format + */ + +const existsSync = require('fs').existsSync; +const path = require('path'); +const username = require('username'); +const uuid = require('uuid'); +const util = require('util'); + +const glob = util.promisify(require('glob')); + +const templateUtils = require('../templateUtils'); + +function getExampleInfo(config = {}, options = {}) { + const projectPath = config?.root ?? process.cwd(); + const exampleProjectPath = path.join(projectPath, 'example'); + + const exists = existsSync(exampleProjectPath); + const exProjectConfig = exists + ? templateUtils.getWindowsProjectConfig(exampleProjectPath) + : null; + const exOptions = exists + ? { + ...options, + template: 'cpp-app', + name: `${options?.name ?? 'MyLib'}Example`, + namespace: `${options?.namespace ?? 'MyLib'}Example`, + } + : null; + + return { + exists, + config: { + root: exampleProjectPath, + project: { + windows: exProjectConfig, + }, + }, + options: exOptions, + }; +} + +const exampleTemplateConfig = require('../cpp-app/template.config'); + +async function preInstall(config = {}, options = {}) { + const exampleInfo = getExampleInfo(config, options); + + if (exampleInfo.exists) { + if (options?.logging) { + console.log('Running cpp-app template preInstall() for example...'); + } + await exampleTemplateConfig.preInstall( + exampleInfo.config, + exampleInfo.options, + ); + } +} + +async function getFileMappings(config = {}, options = {}) { + const {rnwVersion, devMode} = templateUtils.getRnwInfo(config, options); + + const projectName = + config?.project?.windows?.project?.projectName ?? options?.name ?? 'MyLib'; + const namespace = options?.namespace ?? projectName; + const namespaceCpp = namespace.replace(/\./g, '::'); + const projectGuid = + config?.project?.windows?.project?.projectGuid + ?.replace('{', '') + .replace('}', '') ?? uuid.v4(); + const currentUser = username.sync(); // Gets the current username depending on the platform. + + const cppNugetPackages = []; + + const replacements = { + useMustache: true, + regExpPatternsToRemove: [], + + name: projectName, + namespace: namespace, + namespaceCpp: namespaceCpp, + + rnwVersion: rnwVersion, + + // Visual Studio is very picky about the casing of the guids for projects, project references and the solution + // https://www.bing.com/search?q=visual+studio+project+guid+casing&cvid=311a5ad7f9fc41089507b24600d23ee7&FORM=ANAB01&PC=U531 + // we therefore have to precariously use the right casing in the right place or risk building in VS breaking. + projectGuidLower: `{${projectGuid.toLowerCase()}}`, + projectGuidUpper: `{${projectGuid.toUpperCase()}}`, + + currentUser: currentUser, + + devMode, + + cppNugetPackages, + }; + + let fileMappings = []; + + const templateFiles = await glob('**/*', { + cwd: __dirname, + ignore: 'template.config.js', + nodir: true, + }); + + const exampleInfo = getExampleInfo(config, options); + + for (const file of templateFiles) { + const fileMapping = { + from: path.resolve(__dirname, path.normalize(file)), + to: path.normalize(file), + replacements, + }; + + // Don't copy example files if there is no example in the destination + if (!exampleInfo.exists && fileMapping.to.startsWith('example')) { + continue; + } + + // Perform simple file renames + const fileName = path.basename(fileMapping.to); + switch (fileName) { + case '_gitignore': + fileMapping.to = path.join(path.dirname(fileMapping.to), '.gitignore'); + break; + case 'NuGet_Config': + fileMapping.to = path.join( + path.dirname(fileMapping.to), + 'NuGet.config', + ); + break; + } + + // Rename files with MyLib in the name + fileMapping.to = fileMapping.to.replace(/MyLib/g, projectName); + + fileMappings.push(fileMapping); + } + + // Add the file mappings from the cpp-app template for the example app + if (exampleInfo.exists) { + const exampleFileMappings = await exampleTemplateConfig.getFileMappings( + exampleInfo.config, + exampleInfo.options, + ); + + for (const exFileMap of exampleFileMappings) { + exFileMap.to = path.join('example', exFileMap.to); + + // Only add the file map if there isn't a mapping from this cpp-lib template + if (fileMappings.filter(fm => fm.to === exFileMap.to).length === 0) { + fileMappings.push(exFileMap); + } + } + } + + return fileMappings; +} + +async function postInstall(config = {}, options = {}) { + const projectName = + config?.project?.windows?.project?.projectName ?? options?.name ?? 'MyLib'; + const namespace = options?.namespace ?? projectName; + + // Update package.json codegen + await templateUtils.updateProjectPackageJson(config, options, { + codegenConfig: { + name: projectName, + type: 'modules', + jsSrcsDir: 'src', + windows: { + namespace: namespace + 'Codegen', + outputDirectory: `windows/${projectName}/codegen`, + separateDataTypes: true, + }, + }, + }); + + // Fix babel config's out of date preset + await templateUtils.replaceInFile( + config, + options, + 'babel.config.js', + 'module:metro-react-native-babel-preset', + 'module:@react-native/babel-preset', + ); + + const exampleInfo = getExampleInfo(config, options); + + if (exampleInfo.exists) { + const {rnwVersion} = templateUtils.getRnwInfo( + exampleInfo.config, + exampleInfo.options, + ); + + // Update example package.json with new scripts and dependencies + await templateUtils.updateProjectPackageJson( + exampleInfo.config, + exampleInfo.options, + { + scripts: { + windows: 'react-native run-windows', + 'test:windows': 'jest --config jest.config.windows.js', + }, + dependencies: { + 'react-native-windows': rnwVersion, + }, + devDependencies: { + '@rnx-kit/jest-preset': '^0.1.16', + }, + }, + ); + + // Fix babel config's out of date preset + await templateUtils.replaceInFile( + exampleInfo.config, + exampleInfo.options, + 'babel.config.js', + 'module:metro-react-native-babel-preset', + 'module:@react-native/babel-preset', + ); + + // Install recently added dependencies (doesn't work, uses wrong yarn) + //await templateUtils.runNpmInstall(exampleInfo.config, exampleInfo.options); + + console.log( + 'Run yarn to install new dependencies for the example project.', + ); + } +} + +module.exports = { + name: 'React Native Windows Turbo Module (New Arch, C++)', + description: + "[Experimental] A RNW turbo module targeting RN's new architecture.", + preInstall, + getFileMappings, + postInstall, +}; diff --git a/vnext/templates/cpp-lib/windows/ExperimentalFeatures.props b/vnext/templates/cpp-lib/windows/ExperimentalFeatures.props new file mode 100644 index 00000000000..6b4e69eb6c1 --- /dev/null +++ b/vnext/templates/cpp-lib/windows/ExperimentalFeatures.props @@ -0,0 +1,11 @@ + + + + + true + false + + true + + + diff --git a/vnext/templates/cpp-lib/windows/MyLib.sln b/vnext/templates/cpp-lib/windows/MyLib.sln new file mode 100644 index 00000000000..7d36d07392c --- /dev/null +++ b/vnext/templates/cpp-lib/windows/MyLib.sln @@ -0,0 +1,156 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32929.385 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "{{ name }}", "{{ name }}\{{ name }}.vcxproj", "{{ projectGuidUpper }}" + ProjectSection(ProjectDependencies) = postProject + {F7D32BD0-2749-483E-9A0D-1635EF7E3136} = {F7D32BD0-2749-483E-9A0D-1635EF7E3136} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Folly", "..\node_modules\react-native-windows\Folly\Folly.vcxproj", "{A990658C-CE31-4BCC-976F-0FC6B1AF693D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmt", "..\node_modules\react-native-windows\fmt\fmt.vcxproj", "{14B93DC8-FD93-4A6D-81CB-8BC96644501C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactCommon", "..\node_modules\react-native-windows\ReactCommon\ReactCommon.vcxproj", "{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}" + ProjectSection(ProjectDependencies) = postProject + {A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {A990658C-CE31-4BCC-976F-0FC6B1AF693D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Chakra", "..\node_modules\react-native-windows\Chakra\Chakra.vcxitems", "{C38970C0-5FBF-4D69-90D8-CBAC225AE895}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative", "..\node_modules\react-native-windows\Microsoft.ReactNative\Microsoft.ReactNative.vcxproj", "{F7D32BD0-2749-483E-9A0D-1635EF7E3136}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Cxx", "..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems", "{DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "..\node_modules\react-native-windows\Common\Common.vcxproj", "{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReactNative", "ReactNative", "{5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Shared", "..\node_modules\react-native-windows\Shared\Shared.vcxitems", "{2049DBE9-8D13-42C9-AE4B-413AE38FFFD0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mso", "..\node_modules\react-native-windows\Mso\Mso.vcxitems", "{84E05BFA-CBAF-4F0D-BFB6-4CE85742A57E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Include", "..\node_modules\react-native-windows\include\Include.vcxitems", "{EF074BA1-2D54-4D49-A28E-5E040B47CD2E}" +EndProject +Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\node_modules\react-native-windows\Shared\Shared.vcxitems*{2049dbe9-8d13-42c9-ae4b-413ae38fffd0}*SharedItemsImports = 9 + ..\node_modules\react-native-windows\Mso\Mso.vcxitems*{84e05bfa-cbaf-4f0d-bfb6-4ce85742a57e}*SharedItemsImports = 9 + ..\node_modules\react-native-windows\Chakra\Chakra.vcxitems*{c38970c0-5fbf-4d69-90d8-cbac225ae895}*SharedItemsImports = 9 + ..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{da8b35b3-da00-4b02-bde6-6a397b3fd46b}*SharedItemsImports = 9 + ..\node_modules\react-native-windows\include\Include.vcxitems*{ef074ba1-2d54-4d49-a28e-5e040b47cd2e}*SharedItemsImports = 9 + ..\node_modules\react-native-windows\Chakra\Chakra.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 + ..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 + ..\node_modules\react-native-windows\Mso\Mso.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 + ..\node_modules\react-native-windows\Shared\Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Debug|ARM64 = Debug|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + Release|ARM64 = Release|ARM64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {{ projectGuidUpper }}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {{ projectGuidUpper }}.Debug|ARM64.Build.0 = Debug|ARM64 + {{ projectGuidUpper }}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {{ projectGuidUpper }}.Debug|x64.ActiveCfg = Debug|x64 + {{ projectGuidUpper }}.Debug|x64.Build.0 = Debug|x64 + {{ projectGuidUpper }}.Debug|x64.Deploy.0 = Debug|x64 + {{ projectGuidUpper }}.Debug|x86.ActiveCfg = Debug|Win32 + {{ projectGuidUpper }}.Debug|x86.Build.0 = Debug|Win32 + {{ projectGuidUpper }}.Debug|x86.Deploy.0 = Debug|Win32 + {{ projectGuidUpper }}.Release|ARM64.ActiveCfg = Release|ARM64 + {{ projectGuidUpper }}.Release|ARM64.Build.0 = Release|ARM64 + {{ projectGuidUpper }}.Release|ARM64.Deploy.0 = Release|ARM64 + {{ projectGuidUpper }}.Release|x64.ActiveCfg = Release|x64 + {{ projectGuidUpper }}.Release|x64.Build.0 = Release|x64 + {{ projectGuidUpper }}.Release|x64.Deploy.0 = Release|x64 + {{ projectGuidUpper }}.Release|x86.ActiveCfg = Release|Win32 + {{ projectGuidUpper }}.Release|x86.Build.0 = Release|Win32 + {{ projectGuidUpper }}.Release|x86.Deploy.0 = Release|Win32 + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.Build.0 = Debug|ARM64 + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.ActiveCfg = Debug|x64 + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.Build.0 = Debug|x64 + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.ActiveCfg = Debug|Win32 + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x86.Build.0 = Debug|Win32 + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM64.ActiveCfg = Release|ARM64 + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|ARM64.Build.0 = Release|ARM64 + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.ActiveCfg = Release|x64 + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.Build.0 = Release|x64 + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.ActiveCfg = Release|Win32 + {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.Build.0 = Release|Win32 + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.Build.0 = Debug|ARM64 + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.ActiveCfg = Debug|x64 + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.Build.0 = Debug|x64 + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.ActiveCfg = Debug|Win32 + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x86.Build.0 = Debug|Win32 + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM64.ActiveCfg = Release|ARM64 + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|ARM64.Build.0 = Release|ARM64 + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.ActiveCfg = Release|x64 + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.Build.0 = Release|x64 + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.ActiveCfg = Release|Win32 + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.Build.0 = Release|Win32 + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM64.Build.0 = Debug|ARM64 + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.ActiveCfg = Debug|x64 + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x64.Build.0 = Debug|x64 + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.ActiveCfg = Debug|Win32 + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|x86.Build.0 = Debug|Win32 + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM64.ActiveCfg = Release|ARM64 + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|ARM64.Build.0 = Release|ARM64 + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.ActiveCfg = Release|x64 + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x64.Build.0 = Release|x64 + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.ActiveCfg = Release|Win32 + {F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Release|x86.Build.0 = Release|Win32 + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|ARM64.Build.0 = Debug|ARM64 + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.ActiveCfg = Debug|x64 + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x64.Build.0 = Debug|x64 + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.ActiveCfg = Debug|Win32 + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Debug|x86.Build.0 = Debug|Win32 + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM64.ActiveCfg = Release|ARM64 + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|ARM64.Build.0 = Release|ARM64 + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.ActiveCfg = Release|x64 + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.Build.0 = Release|x64 + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.ActiveCfg = Release|Win32 + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.Build.0 = Release|Win32 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|ARM64.Build.0 = Debug|ARM64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|x64.ActiveCfg = Debug|x64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|x64.Build.0 = Debug|x64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|x86.ActiveCfg = Debug|Win32 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|x86.Build.0 = Debug|Win32 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|x86.Deploy.0 = Debug|Win32 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|ARM64.ActiveCfg = Release|ARM64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|ARM64.Build.0 = Release|ARM64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x64.ActiveCfg = Release|x64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x64.Build.0 = Release|x64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x86.ActiveCfg = Release|Win32 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x86.Build.0 = Release|Win32 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x86.Deploy.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} + {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} + {C38970C0-5FBF-4D69-90D8-CBAC225AE895} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} + {F7D32BD0-2749-483E-9A0D-1635EF7E3136} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} + {DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} + {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} + {2049DBE9-8D13-42C9-AE4B-413AE38FFFD0} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} + {84E05BFA-CBAF-4F0D-BFB6-4CE85742A57E} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} + {EF074BA1-2D54-4D49-A28E-5E040B47CD2E} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} + {14B93DC8-FD93-4A6D-81CB-8BC96644501C} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D43FAD39-F619-437D-BB40-04A3982ACB6A} + EndGlobalSection +EndGlobal diff --git a/vnext/templates/cpp-lib/windows/MyLib/MyLib.cpp b/vnext/templates/cpp-lib/windows/MyLib/MyLib.cpp new file mode 100644 index 00000000000..1a67d656a64 --- /dev/null +++ b/vnext/templates/cpp-lib/windows/MyLib/MyLib.cpp @@ -0,0 +1,18 @@ +#include "pch.h" + +#include "{{ name }}.h" + +namespace winrt::{{ namespaceCpp }} +{ + +// See https://microsoft.github.io/react-native-windows/docs/native-modules for details on writing native modules + +void {{ name }}::Initialize(React::ReactContext const &reactContext) noexcept { + m_context = reactContext; +} + +double {{ name }}::multiply(double a, double b) noexcept { + return a * b; +} + +} // namespace winrt::{{ namespaceCpp }} \ No newline at end of file diff --git a/vnext/templates/cpp-lib/windows/MyLib/MyLib.def b/vnext/templates/cpp-lib/windows/MyLib/MyLib.def new file mode 100644 index 00000000000..24e7c1235c3 --- /dev/null +++ b/vnext/templates/cpp-lib/windows/MyLib/MyLib.def @@ -0,0 +1,3 @@ +EXPORTS +DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE +DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/vnext/templates/cpp-lib/windows/MyLib/MyLib.h b/vnext/templates/cpp-lib/windows/MyLib/MyLib.h new file mode 100644 index 00000000000..bf781a2b293 --- /dev/null +++ b/vnext/templates/cpp-lib/windows/MyLib/MyLib.h @@ -0,0 +1,31 @@ +#pragma once + +#include "pch.h" +#include "resource.h" + +#if __has_include("codegen\Native{{ name }}DataTypes.g.h") + #include "codegen\Native{{ name }}DataTypes.g.h" +#endif +#include "codegen\Native{{ name }}Spec.g.h" + +#include "NativeModules.h" + +namespace winrt::{{ namespaceCpp }} +{ + +REACT_MODULE({{ name }}) +struct {{ name }} +{ + using ModuleSpec = {{ namespaceCpp }}Codegen::{{ name }}Spec; + + REACT_INIT(Initialize) + void Initialize(React::ReactContext const &reactContext) noexcept; + + REACT_SYNC_METHOD(multiply) + double multiply(double a, double b) noexcept; + +private: + React::ReactContext m_context; +}; + +} // namespace winrt::{{ namespaceCpp }} \ No newline at end of file diff --git a/vnext/templates/cpp-lib/windows/MyLib/MyLib.rc b/vnext/templates/cpp-lib/windows/MyLib/MyLib.rc new file mode 100644 index 0000000000000000000000000000000000000000..3212c105fce3ed3e784a1f88d04cd4b26a0d7f4f GIT binary patch literal 160 zcmZXNK?;LF3`O6%&^rveEI5bgx?A_6wiU#d(Ye0ubG8z~PhNhK-yVqs9!&HkKCI@G zP1ed*G99zsotqJOF|wFRzNv#OsmETn>aBy_ac$zOr{T)QFR^GXU7XIkhvKbH`wefZ G;{F4P;Ty#O literal 0 HcmV?d00001 diff --git a/vnext/templates/cpp-lib/windows/MyLib/MyLib.vcxproj b/vnext/templates/cpp-lib/windows/MyLib/MyLib.vcxproj new file mode 100644 index 00000000000..89cb24908df --- /dev/null +++ b/vnext/templates/cpp-lib/windows/MyLib/MyLib.vcxproj @@ -0,0 +1,140 @@ + + + + + + true + true + {{ projectGuidUpper }} + {{ name }} + Win32Proj + {{ namespace }} + 10.0 + en-US + 17.0 + false + + + $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\ + false + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + DynamicLibrary + Unicode + v143 + false + + + true + + + false + true + + + + + + + + + + + + + Use + pch.h + $(IntDir)pch.pch + Level3 + true + %(AdditionalOptions) /bigobj + 4453;28204 + _WINRT_DLL;%(PreprocessorDefinitions) + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + stdcpp17 + + + shell32.lib;user32.lib;windowsapp.lib;%(AdditionalDependenices) + Console + true + {{ name }}.def + + + + + _DEBUG;%(PreprocessorDefinitions) + + + + + NDEBUG;%(PreprocessorDefinitions) + + + + + USE_FABRIC;%(PreprocessorDefinitions) + + + + + + + ReactPackageProvider.idl + + + + + + + + + Create + + + ReactPackageProvider.idl + + + + + + + + + + + + + + + {{#cppNugetPackages}} + + {{/cppNugetPackages}} + + + + This project references targets in your node_modules\react-native-windows folder. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/vnext/templates/cpp-lib/windows/MyLib/MyLib.vcxproj.filters b/vnext/templates/cpp-lib/windows/MyLib/MyLib.vcxproj.filters new file mode 100644 index 00000000000..0f7dbff1fb5 --- /dev/null +++ b/vnext/templates/cpp-lib/windows/MyLib/MyLib.vcxproj.filters @@ -0,0 +1,44 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/vnext/templates/cpp-lib/windows/MyLib/ReactPackageProvider.cpp b/vnext/templates/cpp-lib/windows/MyLib/ReactPackageProvider.cpp new file mode 100644 index 00000000000..c2386f60205 --- /dev/null +++ b/vnext/templates/cpp-lib/windows/MyLib/ReactPackageProvider.cpp @@ -0,0 +1,20 @@ +#include "pch.h" + +#include "ReactPackageProvider.h" +#if __has_include("ReactPackageProvider.g.cpp") +#include "ReactPackageProvider.g.cpp" +#endif + +#include "{{ name }}.h" + +using namespace winrt::Microsoft::ReactNative; + +namespace winrt::{{ namespaceCpp }}::implementation +{ + +void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept +{ + AddAttributedModules(packageBuilder, true); +} + +} // namespace winrt::{{ namespaceCpp }}::implementation diff --git a/vnext/templates/cpp-lib/windows/MyLib/ReactPackageProvider.h b/vnext/templates/cpp-lib/windows/MyLib/ReactPackageProvider.h new file mode 100644 index 00000000000..22532fd6449 --- /dev/null +++ b/vnext/templates/cpp-lib/windows/MyLib/ReactPackageProvider.h @@ -0,0 +1,24 @@ +#pragma once + +#include "ReactPackageProvider.g.h" + +using namespace winrt::Microsoft::ReactNative; + +namespace winrt::{{ namespaceCpp }}::implementation +{ + +struct ReactPackageProvider : ReactPackageProviderT +{ + ReactPackageProvider() = default; + + void CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept; +}; + +} // namespace winrt::{{ namespaceCpp }}::implementation + +namespace winrt::{{ namespaceCpp }}::factory_implementation +{ + +struct ReactPackageProvider : ReactPackageProviderT {}; + +} // namespace winrt::{{ namespaceCpp }}::factory_implementation diff --git a/vnext/templates/cpp-lib/windows/MyLib/ReactPackageProvider.idl b/vnext/templates/cpp-lib/windows/MyLib/ReactPackageProvider.idl new file mode 100644 index 00000000000..1465f2e7184 --- /dev/null +++ b/vnext/templates/cpp-lib/windows/MyLib/ReactPackageProvider.idl @@ -0,0 +1,9 @@ +namespace {{ namespace }} +{ + [webhosthidden] + [default_interface] + runtimeclass ReactPackageProvider : Microsoft.ReactNative.IReactPackageProvider + { + ReactPackageProvider(); + }; +} diff --git a/vnext/templates/cpp-lib/windows/MyLib/_gitignore b/vnext/templates/cpp-lib/windows/MyLib/_gitignore new file mode 100644 index 00000000000..82fabe9662a --- /dev/null +++ b/vnext/templates/cpp-lib/windows/MyLib/_gitignore @@ -0,0 +1 @@ +/Bundle \ No newline at end of file diff --git a/vnext/templates/cpp-lib/windows/MyLib/pch.cpp b/vnext/templates/cpp-lib/windows/MyLib/pch.cpp new file mode 100644 index 00000000000..1d9f38c57d6 --- /dev/null +++ b/vnext/templates/cpp-lib/windows/MyLib/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/vnext/templates/cpp-lib/windows/MyLib/pch.h b/vnext/templates/cpp-lib/windows/MyLib/pch.h new file mode 100644 index 00000000000..8512a671430 --- /dev/null +++ b/vnext/templates/cpp-lib/windows/MyLib/pch.h @@ -0,0 +1,35 @@ +// pch.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define NOMINMAX 1 +#define WIN32_LEAN_AND_MEAN 1 +#define WINRT_LEAN_AND_MEAN 1 + +// Windows Header Files +#include + +#pragma push_macro("GetCurrentTime") +#undef GetCurrentTime + +// Playground pch.h +#include +#include +#include +#include +#pragma pop_macro("GetCurrentTime") + +// C RunTime Header Files +#include +#include +#include +#include + +// reference additional headers your program requires here +#include +#include diff --git a/vnext/templates/cpp-lib/windows/MyLib/resource.h b/vnext/templates/cpp-lib/windows/MyLib/resource.h new file mode 100644 index 00000000000..964739b0b37 --- /dev/null +++ b/vnext/templates/cpp-lib/windows/MyLib/resource.h @@ -0,0 +1,5 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by {{ name }}.rc + +#pragma once diff --git a/vnext/templates/cpp-lib/windows/MyLib/targetver.h b/vnext/templates/cpp-lib/windows/MyLib/targetver.h new file mode 100644 index 00000000000..87c0086de75 --- /dev/null +++ b/vnext/templates/cpp-lib/windows/MyLib/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/vnext/templates/cpp-lib/windows/_gitignore b/vnext/templates/cpp-lib/windows/_gitignore new file mode 100644 index 00000000000..5bc72a4416d --- /dev/null +++ b/vnext/templates/cpp-lib/windows/_gitignore @@ -0,0 +1,41 @@ +*AppPackages* +*BundleArtifacts* + +#OS junk files +[Tt]humbs.db +*.DS_Store + +#Visual Studio files +*.[Oo]bj +*.user +*.aps +*.pch +*.vspscc +*.vssscc +*_i.c +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.[Cc]ache +*.ilk +*.log +*.lib +*.sbr +*.sdf +*.opensdf +*.opendb +*.unsuccessfulbuild +ipch/ +[Oo]bj/ +[Bb]in +[Dd]ebug*/ +[Rr]elease*/ +Ankh.NoLoad +.vs/ +# Visual C++ cache files + +#Files generated by the VS build +**/Generated Files/** diff --git a/vnext/templates/templateUtils.js b/vnext/templates/templateUtils.js index e9f2bb7174b..1e6f7fc5861 100644 --- a/vnext/templates/templateUtils.js +++ b/vnext/templates/templateUtils.js @@ -7,17 +7,74 @@ */ const existsSync = require('fs').existsSync; +const fs = require('fs').promises; const path = require('path'); const util = require('util'); const exec = util.promisify(require('child_process').exec); const pkgUtils = require('@react-native-windows/package-utils'); +const projectConfig = require('@react-native-windows/cli').projectConfig; + +function getRnwInfo(config = {}, options = {}) { + const projectPath = config?.root ?? process.cwd(); + + const rnwPath = path.dirname( + require.resolve('react-native-windows', [projectPath]), + ); + + const rnwVersion = require(path.join(rnwPath, 'package.json')).version; + + const devMode = existsSync(path.join(rnwPath, 'src')); + + if (options?.logging) { + console.log( + `Found react-native-windows@${rnwVersion} at ${rnwPath}${ + devMode ? ' (devMode enabled)' : '' + }...`, + ); + } + + return {rnwPath, rnwVersion, devMode}; +} + +function getWindowsProjectConfig(root) { + if (!existsSync(root)) { + return {}; + } + + const userConfigPath = path.join(root, 'react-native.config.js'); + + const userConfig = existsSync(userConfigPath) ? require(userConfigPath) : {}; + + return projectConfig(root, userConfig); +} + +async function replaceInFile( + config = {}, + options = {}, + relativePath = '', + searchValue = '', + replaceValue = '', +) { + const filePath = path.join(config?.root ?? process.cwd(), relativePath); + if (existsSync(filePath)) { + const contents = await fs.readFile(filePath, {encoding: 'utf-8'}); + const newContents = contents.replace(searchValue, replaceValue); + if (contents !== newContents) { + if (options?.logging) { + console.log(`Modifying ${filePath}...`); + } + await fs.writeFile(filePath, newContents, {encoding: 'utf-8'}); + } + } +} + async function runNpmInstall(config = {}, options = {}) { const projectPath = config?.root ?? process.cwd(); if (options?.logging) { - console.log('Installing dependencies...'); + console.log(`Installing dependencies for ${projectPath}...`); } const isYarn = existsSync(path.join(projectPath, 'yarn.lock')); await exec( @@ -39,9 +96,15 @@ async function updateProjectPackageJson(config = {}, options = {}, props = {}) { } if (options?.logging) { - console.log('Modifying project package.json...'); + console.log(`Modifying ${path.join(projectPath, 'package.json')}...`); } await projectPackage.mergeProps(props); } -module.exports = {runNpmInstall, updateProjectPackageJson}; +module.exports = { + getRnwInfo, + getWindowsProjectConfig, + replaceInFile, + runNpmInstall, + updateProjectPackageJson, +}; From c8dae5e7d11d8a3d85e38adc93821fb2133f2564 Mon Sep 17 00:00:00 2001 From: "Jon Thysell (JAUNTY)" Date: Tue, 5 Dec 2023 16:36:44 -0800 Subject: [PATCH 02/10] fix codegen naming issues --- vnext/templates/cpp-lib/template.config.js | 7 +++---- vnext/templates/cpp-lib/windows/MyLib/MyLib.h | 8 ++++---- vnext/templates/templateUtils.js | 8 ++++++++ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/vnext/templates/cpp-lib/template.config.js b/vnext/templates/cpp-lib/template.config.js index 13caca5d42d..156fc8dce77 100644 --- a/vnext/templates/cpp-lib/template.config.js +++ b/vnext/templates/cpp-lib/template.config.js @@ -81,6 +81,7 @@ async function getFileMappings(config = {}, options = {}) { regExpPatternsToRemove: [], name: projectName, + pascalName: templateUtils.pascalCase(projectName), namespace: namespace, namespaceCpp: namespaceCpp, @@ -165,15 +166,13 @@ async function postInstall(config = {}, options = {}) { const projectName = config?.project?.windows?.project?.projectName ?? options?.name ?? 'MyLib'; const namespace = options?.namespace ?? projectName; + const namespaceCpp = namespace.replace(/\./g, '::'); // Update package.json codegen await templateUtils.updateProjectPackageJson(config, options, { codegenConfig: { - name: projectName, - type: 'modules', - jsSrcsDir: 'src', windows: { - namespace: namespace + 'Codegen', + namespace: namespaceCpp + 'Codegen', outputDirectory: `windows/${projectName}/codegen`, separateDataTypes: true, }, diff --git a/vnext/templates/cpp-lib/windows/MyLib/MyLib.h b/vnext/templates/cpp-lib/windows/MyLib/MyLib.h index bf781a2b293..8ac21c72e56 100644 --- a/vnext/templates/cpp-lib/windows/MyLib/MyLib.h +++ b/vnext/templates/cpp-lib/windows/MyLib/MyLib.h @@ -3,10 +3,10 @@ #include "pch.h" #include "resource.h" -#if __has_include("codegen\Native{{ name }}DataTypes.g.h") - #include "codegen\Native{{ name }}DataTypes.g.h" +#if __has_include("codegen\Native{{ pascalName }}DataTypes.g.h") + #include "codegen\Native{{ pascalName }}DataTypes.g.h" #endif -#include "codegen\Native{{ name }}Spec.g.h" +#include "codegen\Native{{ pascalName }}Spec.g.h" #include "NativeModules.h" @@ -16,7 +16,7 @@ namespace winrt::{{ namespaceCpp }} REACT_MODULE({{ name }}) struct {{ name }} { - using ModuleSpec = {{ namespaceCpp }}Codegen::{{ name }}Spec; + using ModuleSpec = {{ namespaceCpp }}Codegen::{{ pascalName }}Spec; REACT_INIT(Initialize) void Initialize(React::ReactContext const &reactContext) noexcept; diff --git a/vnext/templates/templateUtils.js b/vnext/templates/templateUtils.js index 1e6f7fc5861..4f966e91e54 100644 --- a/vnext/templates/templateUtils.js +++ b/vnext/templates/templateUtils.js @@ -12,6 +12,8 @@ const path = require('path'); const util = require('util'); const exec = util.promisify(require('child_process').exec); +const _ = require('lodash'); + const pkgUtils = require('@react-native-windows/package-utils'); const projectConfig = require('@react-native-windows/cli').projectConfig; @@ -50,6 +52,11 @@ function getWindowsProjectConfig(root) { return projectConfig(root, userConfig); } +function pascalCase(str) { + const camelCase = _.camelCase(str); + return camelCase[0].toUpperCase() + camelCase.substr(1); +} + async function replaceInFile( config = {}, options = {}, @@ -104,6 +111,7 @@ async function updateProjectPackageJson(config = {}, options = {}, props = {}) { module.exports = { getRnwInfo, getWindowsProjectConfig, + pascalCase, replaceInFile, runNpmInstall, updateProjectPackageJson, From 5b7f044c34a7b81e5a110eaf5b6092ca451fce10 Mon Sep 17 00:00:00 2001 From: "Jon Thysell (JAUNTY)" Date: Tue, 9 Jan 2024 11:40:53 -0800 Subject: [PATCH 03/10] Clean up pch headers and add output of new project command to ci --- .ado/templates/react-native-init-windows.yml | 10 +++++++--- vnext/templates/cpp-app/windows/MyApp/pch.h | 11 ++++------- vnext/templates/cpp-lib/windows/MyLib/pch.h | 11 ++++------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/.ado/templates/react-native-init-windows.yml b/.ado/templates/react-native-init-windows.yml index 3fdfabad539..7ce5e6eaa3e 100644 --- a/.ado/templates/react-native-init-windows.yml +++ b/.ado/templates/react-native-init-windows.yml @@ -38,14 +38,18 @@ steps: buildEnvironment: ${{ parameters.buildEnvironment }} - ${{ if endsWith(parameters.template, '-app') }}: - - script: | - npx --yes react-native@$(reactNativeDevDependency) init testcli --template react-native@$(reactNativeDevDependency) + - pwsh: | + $cmd = "npx --yes react-native@$(reactNativeDevDependency) init testcli --template react-native@$(reactNativeDevDependency)" + Write-Host $cmd + iex $cmd displayName: Init new app project with react-native init workingDirectory: $(Agent.BuildDirectory) - ${{ if endsWith(parameters.template, '-lib') }}: - pwsh: | - npx --yes create-react-native-library@latest --slug testcli --description testcli --author-name "React-Native-Windows Bot" --author-email 53619745+rnbot@users.noreply.github.com --author-url http://example.com --repo-url http://example.com --languages java-objc --type module-new --react-native-version $(reactNativeDevDependency) testcli + $cmd = "npx --yes create-react-native-library@latest --slug testcli --description testcli --author-name `"React-Native-Windows Bot`" --author-email 53619745+rnbot@users.noreply.github.com --author-url http://example.com --repo-url http://example.com --languages java-objc --type module-new --react-native-version $(reactNativeDevDependency) testcli" + Write-Host $cmd + iex $cmd ((Get-Content -Path testcli\.yarnrc.yml -Raw) -replace " - path: scripts/pod-install.cjs") | Set-Content -Encoding UTF8 testcli\.yarnrc.yml displayName: Init new lib project with create-react-native-library workingDirectory: $(Agent.BuildDirectory) diff --git a/vnext/templates/cpp-app/windows/MyApp/pch.h b/vnext/templates/cpp-app/windows/MyApp/pch.h index 8512a671430..a97e6b01127 100644 --- a/vnext/templates/cpp-app/windows/MyApp/pch.h +++ b/vnext/templates/cpp-app/windows/MyApp/pch.h @@ -12,16 +12,15 @@ #define WINRT_LEAN_AND_MEAN 1 // Windows Header Files +#include #include +// WinRT Header Files +#include #pragma push_macro("GetCurrentTime") #undef GetCurrentTime - -// Playground pch.h #include #include -#include -#include #pragma pop_macro("GetCurrentTime") // C RunTime Header Files @@ -30,6 +29,4 @@ #include #include -// reference additional headers your program requires here -#include -#include +// Reference additional headers your project requires here diff --git a/vnext/templates/cpp-lib/windows/MyLib/pch.h b/vnext/templates/cpp-lib/windows/MyLib/pch.h index 8512a671430..a97e6b01127 100644 --- a/vnext/templates/cpp-lib/windows/MyLib/pch.h +++ b/vnext/templates/cpp-lib/windows/MyLib/pch.h @@ -12,16 +12,15 @@ #define WINRT_LEAN_AND_MEAN 1 // Windows Header Files +#include #include +// WinRT Header Files +#include #pragma push_macro("GetCurrentTime") #undef GetCurrentTime - -// Playground pch.h #include #include -#include -#include #pragma pop_macro("GetCurrentTime") // C RunTime Header Files @@ -30,6 +29,4 @@ #include #include -// reference additional headers your program requires here -#include -#include +// Reference additional headers your project requires here From b337236e3e425d7a3c3d6a98cb71bbf1177fc310 Mon Sep 17 00:00:00 2001 From: "Jon Thysell (JAUNTY)" Date: Tue, 9 Jan 2024 14:30:29 -0800 Subject: [PATCH 04/10] fix bad merge --- vnext/templates/cpp-app/windows/MyApp/MyApp.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/vnext/templates/cpp-app/windows/MyApp/MyApp.cpp b/vnext/templates/cpp-app/windows/MyApp/MyApp.cpp index ac382a52aa1..00f00bc7aca 100644 --- a/vnext/templates/cpp-app/windows/MyApp/MyApp.cpp +++ b/vnext/templates/cpp-app/windows/MyApp/MyApp.cpp @@ -6,8 +6,6 @@ #include "AutolinkedNativeModules.g.h" -#include "../../node_modules/react-native-windows/codegen/NativeDeviceInfoSpec.g.h" - #include #include From 08ada464cd709ae548063be21efe8064401a37ac Mon Sep 17 00:00:00 2001 From: "Jon Thysell (JAUNTY)" Date: Tue, 9 Jan 2024 14:45:32 -0800 Subject: [PATCH 05/10] remove yarnrc modification --- .ado/templates/react-native-init-windows.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.ado/templates/react-native-init-windows.yml b/.ado/templates/react-native-init-windows.yml index 7ce5e6eaa3e..f48cea9335f 100644 --- a/.ado/templates/react-native-init-windows.yml +++ b/.ado/templates/react-native-init-windows.yml @@ -38,19 +38,14 @@ steps: buildEnvironment: ${{ parameters.buildEnvironment }} - ${{ if endsWith(parameters.template, '-app') }}: - - pwsh: | - $cmd = "npx --yes react-native@$(reactNativeDevDependency) init testcli --template react-native@$(reactNativeDevDependency)" - Write-Host $cmd - iex $cmd + - script: | + npx --yes react-native@$(reactNativeDevDependency) init testcli --template react-native@$(reactNativeDevDependency) displayName: Init new app project with react-native init workingDirectory: $(Agent.BuildDirectory) - ${{ if endsWith(parameters.template, '-lib') }}: - - pwsh: | - $cmd = "npx --yes create-react-native-library@latest --slug testcli --description testcli --author-name `"React-Native-Windows Bot`" --author-email 53619745+rnbot@users.noreply.github.com --author-url http://example.com --repo-url http://example.com --languages java-objc --type module-new --react-native-version $(reactNativeDevDependency) testcli" - Write-Host $cmd - iex $cmd - ((Get-Content -Path testcli\.yarnrc.yml -Raw) -replace " - path: scripts/pod-install.cjs") | Set-Content -Encoding UTF8 testcli\.yarnrc.yml + - script: | + npx --yes create-react-native-library@latest --slug testcli --description testcli --author-name "React-Native-Windows Bot" --author-email 53619745+rnbot@users.noreply.github.com --author-url http://example.com --repo-url http://example.com --languages java-objc --type module-new --react-native-version $(reactNativeDevDependency) testcli displayName: Init new lib project with create-react-native-library workingDirectory: $(Agent.BuildDirectory) From 67001cdc26a9cce751e85b1316e37b48c554bf91 Mon Sep 17 00:00:00 2001 From: "Jon Thysell (JAUNTY)" Date: Thu, 11 Jan 2024 10:36:14 -0800 Subject: [PATCH 06/10] pch test --- vnext/templates/cpp-app/windows/MyApp/pch.h | 4 +--- vnext/templates/cpp-lib/windows/MyLib/pch.h | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/vnext/templates/cpp-app/windows/MyApp/pch.h b/vnext/templates/cpp-app/windows/MyApp/pch.h index a97e6b01127..b93cec13351 100644 --- a/vnext/templates/cpp-app/windows/MyApp/pch.h +++ b/vnext/templates/cpp-app/windows/MyApp/pch.h @@ -14,14 +14,12 @@ // Windows Header Files #include #include +#undef GetCurrentTime // WinRT Header Files #include -#pragma push_macro("GetCurrentTime") -#undef GetCurrentTime #include #include -#pragma pop_macro("GetCurrentTime") // C RunTime Header Files #include diff --git a/vnext/templates/cpp-lib/windows/MyLib/pch.h b/vnext/templates/cpp-lib/windows/MyLib/pch.h index a97e6b01127..b93cec13351 100644 --- a/vnext/templates/cpp-lib/windows/MyLib/pch.h +++ b/vnext/templates/cpp-lib/windows/MyLib/pch.h @@ -14,14 +14,12 @@ // Windows Header Files #include #include +#undef GetCurrentTime // WinRT Header Files #include -#pragma push_macro("GetCurrentTime") -#undef GetCurrentTime #include #include -#pragma pop_macro("GetCurrentTime") // C RunTime Header Files #include From 5f7fba25468a508aba6f641340aa068276c77f4b Mon Sep 17 00:00:00 2001 From: "Jon Thysell (JAUNTY)" Date: Fri, 12 Jan 2024 14:38:06 -0800 Subject: [PATCH 07/10] Fixed cpp-app and cpp-lib templates --- ...osoft.ReactNative.Composition.Common.props | 8 + vnext/templates/cpp-app/template.config.js | 8 +- .../windows/ExperimentalFeatures.props | 2 +- .../templates/cpp-app/windows/MyApp/MyApp.cpp | 349 +++++++----------- vnext/templates/cpp-app/windows/MyApp/pch.h | 6 + vnext/templates/cpp-lib/template.config.js | 141 ++++--- .../windows/ExperimentalFeatures.props | 2 +- .../templates/cpp-lib/windows/MyLib/MyLib.cpp | 4 +- vnext/templates/cpp-lib/windows/MyLib/MyLib.h | 4 +- vnext/templates/templateUtils.js | 32 +- 10 files changed, 243 insertions(+), 313 deletions(-) diff --git a/vnext/PropertySheets/External/Microsoft.ReactNative.Composition.Common.props b/vnext/PropertySheets/External/Microsoft.ReactNative.Composition.Common.props index db2f915b039..3cf960cbc93 100644 --- a/vnext/PropertySheets/External/Microsoft.ReactNative.Composition.Common.props +++ b/vnext/PropertySheets/External/Microsoft.ReactNative.Composition.Common.props @@ -16,5 +16,13 @@ true + + + + + + true + + diff --git a/vnext/templates/cpp-app/template.config.js b/vnext/templates/cpp-app/template.config.js index a8466eb8b03..2bf5cb78f35 100644 --- a/vnext/templates/cpp-app/template.config.js +++ b/vnext/templates/cpp-app/template.config.js @@ -33,6 +33,10 @@ async function getFileMappings(config = {}, options = {}) { const packageGuid = uuid.v4(); const currentUser = username.sync(); // Gets the current username depending on the platform. + const appJsonPath = path.join(config?.root ?? process.cwd(), 'app.json'); + const mainComponentName = + (existsSync(appJsonPath) ? require(appJsonPath).name : null) ?? projectName; + const cppNugetPackages = []; const replacements = { @@ -45,7 +49,7 @@ async function getFileMappings(config = {}, options = {}) { rnwVersion: rnwVersion, - mainComponentName: projectName, // TODO: replace with app.json name + mainComponentName, // Visual Studio is very picky about the casing of the guids for projects, project references and the solution // https://www.bing.com/search?q=visual+studio+project+guid+casing&cvid=311a5ad7f9fc41089507b24600d23ee7&FORM=ANAB01&PC=U531 @@ -56,7 +60,7 @@ async function getFileMappings(config = {}, options = {}) { // packaging and signing variables: packageGuidLower: `{${packageGuid.toLowerCase()}}`, packageGuidUpper: `{${packageGuid.toUpperCase()}}`, - currentUser: currentUser, + currentUser, devMode, diff --git a/vnext/templates/cpp-app/windows/ExperimentalFeatures.props b/vnext/templates/cpp-app/windows/ExperimentalFeatures.props index 6b4e69eb6c1..be73cb5575a 100644 --- a/vnext/templates/cpp-app/windows/ExperimentalFeatures.props +++ b/vnext/templates/cpp-app/windows/ExperimentalFeatures.props @@ -3,7 +3,7 @@ true - false + true true diff --git a/vnext/templates/cpp-app/windows/MyApp/MyApp.cpp b/vnext/templates/cpp-app/windows/MyApp/MyApp.cpp index 00f00bc7aca..15ac6aba18a 100644 --- a/vnext/templates/cpp-app/windows/MyApp/MyApp.cpp +++ b/vnext/templates/cpp-app/windows/MyApp/MyApp.cpp @@ -6,18 +6,8 @@ #include "AutolinkedNativeModules.g.h" -#include -#include - -#include -#include - #include "NativeModules.h" -#include "ReactPropertyBag.h" -constexpr size_t MAX_LOADSTRING = 100; - -// Have to use TurboModules to override built in modules.. so the standard attributed package provider doesn't work. struct CompReactPackageProvider : winrt::implements { public: // IReactPackageProvider @@ -27,229 +17,146 @@ struct CompReactPackageProvider }; // Global Variables: -WCHAR szTitle[MAX_LOADSTRING]; // The title bar text -WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name - -winrt::Windows::System::DispatcherQueueController g_dispatcherQueueController{nullptr}; -winrt::Windows::UI::Composition::Compositor g_compositor{nullptr}; - -constexpr auto WindowDataProperty = L"WindowData"; -constexpr PCWSTR c_windowClassName = L"MS_REACTNATIVE_RNTESTER_COMPOSITION"; -constexpr PCWSTR appName = L"{{ mainComponentName }}"; - -// Forward declarations of functions included in this code module: -LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); -int RunRNTester(int showCmd); - -struct WindowData { - static HINSTANCE s_instance; - static constexpr uint16_t defaultDebuggerPort{9229}; - - bool m_windowInited{false}; - winrt::Microsoft::ReactNative::CompositionHwndHost m_CompositionHwndHost{nullptr}; - winrt::Microsoft::ReactNative::ReactNativeHost m_host{nullptr}; - winrt::Microsoft::ReactNative::ReactInstanceSettings m_instanceSettings{nullptr}; - -#if BUNDLE - std::wstring m_bundleFile = L"index.windows"; - bool m_useWebDebugger{false}; - bool m_fastRefreshEnabled{false}; -#else - std::wstring m_bundleFile = L"index"; - bool m_useWebDebugger{false}; - bool m_fastRefreshEnabled{true}; -#endif +constexpr PCWSTR windowTitle = L"{{ mainComponentName }}"; +constexpr PCWSTR mainComponentName = L"{{ mainComponentName }}"; - bool m_useDirectDebugger{false}; - bool m_breakOnNextLine{false}; - uint16_t m_debuggerPort{defaultDebuggerPort}; - xaml::ElementTheme m_theme{xaml::ElementTheme::Default}; - - WindowData(const winrt::Microsoft::ReactNative::CompositionHwndHost &compHost) : m_CompositionHwndHost(compHost) { - winrt::Microsoft::ReactNative::Composition::CompositionUIService::SetCompositionContext( - InstanceSettings().Properties(), - winrt::Microsoft::ReactNative::Composition::WindowsCompositionContextHelper::CreateContext(g_compositor)); - } - - static WindowData *GetFromWindow(HWND hwnd) { - auto data = reinterpret_cast(GetProp(hwnd, WindowDataProperty)); - return data; - } +HWND global_hwnd; +winrt::Microsoft::ReactNative::CompositionRootView *global_rootView{nullptr}; - winrt::Microsoft::ReactNative::ReactNativeHost Host() noexcept { - if (!m_host) { - m_host = winrt::Microsoft::ReactNative::ReactNativeHost(); - m_host.InstanceSettings(InstanceSettings()); - } +float ScaleFactor(HWND hwnd) noexcept { + return GetDpiForWindow(hwnd) / static_cast(USER_DEFAULT_SCREEN_DPI); +} - return m_host; - } +void UpdateRootViewSizeToAppWindow( + winrt::Microsoft::ReactNative::CompositionRootView const &rootView, + winrt::Microsoft::UI::Windowing::AppWindow const &window) { + auto hwnd = winrt::Microsoft::UI::GetWindowFromWindowId(window.Id()); + auto scaleFactor = ScaleFactor(hwnd); + winrt::Windows::Foundation::Size size{ + window.ClientSize().Width / scaleFactor, window.ClientSize().Height / scaleFactor}; + rootView.Arrange(size); + rootView.Size(size); +} - winrt::Microsoft::ReactNative::ReactInstanceSettings InstanceSettings() noexcept { - if (!m_instanceSettings) { - m_instanceSettings = winrt::Microsoft::ReactNative::ReactInstanceSettings(); - } +// Create and configure the ReactNativeHost +winrt::Microsoft::ReactNative::ReactNativeHost CreateReactNativeHost( + HWND hwnd, + const winrt::Microsoft::UI::Composition::Compositor &compositor) { + WCHAR workingDir[MAX_PATH]; + GetCurrentDirectory(MAX_PATH, workingDir); - return m_instanceSettings; - } + auto host = winrt::Microsoft::ReactNative::ReactNativeHost(); - LRESULT RenderApp(HWND hwnd) { - WCHAR workingDir[MAX_PATH]; - GetCurrentDirectory(MAX_PATH, workingDir); - - auto host = Host(); - - // Include any autolinked modules - RegisterAutolinkedNativeModulePackages(host.PackageProviders()); - - host.InstanceSettings().JavaScriptBundleFile(m_bundleFile); - - host.InstanceSettings().UseWebDebugger(m_useWebDebugger); - host.InstanceSettings().UseDirectDebugger(m_useDirectDebugger); - host.InstanceSettings().BundleRootPath(std::wstring(L"file:").append(workingDir).append(L"\\Bundle\\").c_str()); - host.InstanceSettings().DebuggerBreakOnNextLine(m_breakOnNextLine); - host.InstanceSettings().UseFastRefresh(m_fastRefreshEnabled); - host.InstanceSettings().DebuggerPort(m_debuggerPort); - host.InstanceSettings().UseDeveloperSupport(true); - - host.PackageProviders().Append(winrt::make()); - winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId( - host.InstanceSettings().Properties(), reinterpret_cast(hwnd)); - - // Nudge the ReactNativeHost to create the instance and wrapping context - host.ReloadInstance(); - - winrt::Microsoft::ReactNative::ReactViewOptions viewOptions; - viewOptions.ComponentName(appName); - m_CompositionHwndHost.ReactViewHost( - winrt::Microsoft::ReactNative::ReactCoreInjection::MakeViewHost(host, viewOptions)); - - auto windowData = WindowData::GetFromWindow(hwnd); - if (!windowData->m_windowInited) { - m_CompositionHwndHost.Initialize((uint64_t)(hwnd)); - windowData->m_windowInited = true; - } - return 0; - } + // Include any autolinked modules + RegisterAutolinkedNativeModulePackages(host.PackageProviders()); - LRESULT TranslateMessage(UINT message, WPARAM wparam, LPARAM lparam) noexcept { - if (m_CompositionHwndHost) { - return static_cast(m_CompositionHwndHost.TranslateMessage(message, wparam, lparam)); - } - return 0; - } -}; + host.PackageProviders().Append(winrt::make()); -extern "C" IMAGE_DOS_HEADER __ImageBase; -HINSTANCE WindowData::s_instance = reinterpret_cast(&__ImageBase); + host.InstanceSettings().JavaScriptBundleFile(L"index.windows"); + host.InstanceSettings().DebugBundlePath(L"index"); -LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { - auto windowData = WindowData::GetFromWindow(hWnd); - if (windowData) { - auto result = WindowData::GetFromWindow(hWnd)->TranslateMessage(message, wParam, lParam); - if (result) - return result; - } + host.InstanceSettings().BundleRootPath(std::wstring(L"file:").append(workingDir).append(L"\\Bundle\\").c_str()); + host.InstanceSettings().DebuggerBreakOnNextLine(false); +#if _DEBUG + host.InstanceSettings().UseDirectDebugger(true); + host.InstanceSettings().UseFastRefresh(true); +#endif + host.InstanceSettings().UseDeveloperSupport(true); - switch (message) { - case WM_DESTROY: { - delete WindowData::GetFromWindow(hWnd); - SetProp(hWnd, WindowDataProperty, 0); - PostQuitMessage(0); - return 0; - } - case WM_NCCREATE: { - auto cs = reinterpret_cast(lParam); - auto windowData = static_cast(cs->lpCreateParams); - WINRT_ASSERT(windowData); - SetProp(hWnd, WindowDataProperty, reinterpret_cast(windowData)); - break; - } - case WM_GETOBJECT: { - { - auto windowData = WindowData::GetFromWindow(hWnd); - if (windowData == nullptr || !windowData->m_windowInited) - break; - - auto hwndHost = windowData->m_CompositionHwndHost; - winrt::com_ptr spReps; - if (!hwndHost.UiaProvider().try_as(spReps)) { - break; - } - LRESULT lResult = UiaReturnRawElementProvider(hWnd, wParam, lParam, spReps.get()); - return lResult; - } - } - } + winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId( + host.InstanceSettings().Properties(), reinterpret_cast(hwnd)); - return DefWindowProc(hWnd, message, wParam, lParam); -} + // By using the MicrosoftCompositionContextHelper here, React Native Windows will use Lifted Visuals for its + // tree. + winrt::Microsoft::ReactNative::Composition::CompositionUIService::SetCompositionContext( + host.InstanceSettings().Properties(), + winrt::Microsoft::ReactNative::Composition::MicrosoftCompositionContextHelper::CreateContext(compositor)); -int RunRNTester(int showCmd) { - auto windowData = std::make_unique(winrt::Microsoft::ReactNative::CompositionHwndHost()); - HWND hwnd = CreateWindow( - c_windowClassName, - appName, - WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, - nullptr, - nullptr, - WindowData::s_instance, - windowData.get()); - - WINRT_VERIFY(hwnd); - - windowData.release(); - - ShowWindow(hwnd, showCmd); - UpdateWindow(hwnd); - SetFocus(hwnd); - WindowData::GetFromWindow(hwnd)->RenderApp(hwnd); - - HACCEL hAccelTable = LoadAccelerators(WindowData::s_instance, MAKEINTRESOURCE(IDC_RNTESTER_COMPOSITION)); - - MSG msg = {}; - while (GetMessage(&msg, nullptr, 0, 0)) { - if (!TranslateAccelerator(hwnd, hAccelTable, &msg)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - return static_cast(msg.wParam); + return host; } _Use_decl_annotations_ int CALLBACK WinMain(HINSTANCE instance, HINSTANCE, PSTR /* commandLine */, int showCmd) { - WNDCLASSEXW wcex = {}; - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.style = CS_HREDRAW | CS_VREDRAW; - wcex.lpfnWndProc = &WndProc; - wcex.cbClsExtra = DLGWINDOWEXTRA; - wcex.cbWndExtra = sizeof(WindowData *); - wcex.hInstance = WindowData::s_instance; - wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_RNTESTER_COMPOSITION); - wcex.lpszClassName = c_windowClassName; - wcex.hIcon = LoadIconW(instance, MAKEINTRESOURCEW(IDI_ICON1)); - ATOM classId = RegisterClassEx(&wcex); - WINRT_VERIFY(classId); - winrt::check_win32(!classId); - - DispatcherQueueOptions options{ - sizeof(DispatcherQueueOptions), /* dwSize */ - DQTYPE_THREAD_CURRENT, /* threadType */ - DQTAT_COM_ASTA /* apartmentType */ - }; - - // Need to have a Dispatcher on the current thread to be able to create a Compositor - winrt::check_hresult(CreateDispatcherQueueController( - options, - reinterpret_cast( - winrt::put_abi(g_dispatcherQueueController)))); - - g_compositor = winrt::Windows::UI::Composition::Compositor(); - return RunRNTester(showCmd); -} + // Initialize WinRT. + winrt::init_apartment(winrt::apartment_type::single_threaded); + + // Enable per monitor DPI scaling + SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + + // Create a DispatcherQueue for this thread. This is needed for Composition, Content, and + // Input APIs. + auto dispatcherQueueController{winrt::Microsoft::UI::Dispatching::DispatcherQueueController::CreateOnCurrentThread()}; + + // Create a Compositor for all Content on this thread. + auto compositor{winrt::Microsoft::UI::Composition::Compositor()}; + + // Create a top-level window. + auto window = winrt::Microsoft::UI::Windowing::AppWindow::Create(); + window.Title(windowTitle); + window.Resize({1000, 1000}); + window.Show(); + auto hwnd = winrt::Microsoft::UI::GetWindowFromWindowId(window.Id()); + global_hwnd = hwnd; + auto scaleFactor = ScaleFactor(hwnd); + + auto host = CreateReactNativeHost(hwnd, compositor); + + // Start the react-native instance, which will create a JavaScript runtime and load the applications bundle + host.ReloadInstance(); + + // Create a RootView which will present a react-native component + winrt::Microsoft::ReactNative::ReactViewOptions viewOptions; + viewOptions.ComponentName(mainComponentName); + auto rootView = winrt::Microsoft::ReactNative::CompositionRootView(compositor); + rootView.ReactViewHost(winrt::Microsoft::ReactNative::ReactCoreInjection::MakeViewHost(host, viewOptions)); + + // Update the size of the RootView when the AppWindow changes size + window.Changed([wkRootView = winrt::make_weak(rootView)]( + winrt::Microsoft::UI::Windowing::AppWindow const &window, + winrt::Microsoft::UI::Windowing::AppWindowChangedEventArgs const &args) { + if (args.DidSizeChange() || args.DidVisibilityChange()) { + if (auto rootView = wkRootView.get()) { + UpdateRootViewSizeToAppWindow(rootView, window); + } + } + }); + + // Quit application when main window is closed + window.Destroying( + [host](winrt::Microsoft::UI::Windowing::AppWindow const &window, winrt::IInspectable const & /*args*/) { + // Before we shutdown the application - unload the ReactNativeHost to give the javascript a chance to save any + // state + auto async = host.UnloadInstance(); + async.Completed([host](auto asyncInfo, winrt::Windows::Foundation::AsyncStatus asyncStatus) { + assert(asyncStatus == winrt::Windows::Foundation::AsyncStatus::Completed); + host.InstanceSettings().UIDispatcher().Post([]() { PostQuitMessage(0); }); + }); + }); + + // DesktopChildSiteBridge create a ContentSite that can host the RootView ContentIsland + auto bridge = winrt::Microsoft::UI::Content::DesktopChildSiteBridge::Create(compositor, window.Id()); + bridge.Connect(rootView.Island()); + bridge.ResizePolicy(winrt::Microsoft::UI::Content::ContentSizePolicy::ResizeContentToParentWindow); + + auto invScale = 1.0f / scaleFactor; + rootView.RootVisual().Scale({invScale, invScale, invScale}); + rootView.ScaleFactor(scaleFactor); + + // Set the intialSize of the root view + UpdateRootViewSizeToAppWindow(rootView, window); + + bridge.Show(); + + // Run the main application event loop + dispatcherQueueController.DispatcherQueue().RunEventLoop(); + + // Rundown the DispatcherQueue. This drains the queue and raises events to let components + // know the message loop has finished. + dispatcherQueueController.ShutdownQueue(); + + bridge.Close(); + bridge = nullptr; + + // Destroy all Composition objects + compositor.Close(); + compositor = nullptr; +} \ No newline at end of file diff --git a/vnext/templates/cpp-app/windows/MyApp/pch.h b/vnext/templates/cpp-app/windows/MyApp/pch.h index b93cec13351..77dc1aba971 100644 --- a/vnext/templates/cpp-app/windows/MyApp/pch.h +++ b/vnext/templates/cpp-app/windows/MyApp/pch.h @@ -19,7 +19,13 @@ // WinRT Header Files #include #include +#include #include +#include +#include +#include +#include +#include // C RunTime Header Files #include diff --git a/vnext/templates/cpp-lib/template.config.js b/vnext/templates/cpp-lib/template.config.js index 156fc8dce77..3f7a463d0e1 100644 --- a/vnext/templates/cpp-lib/template.config.js +++ b/vnext/templates/cpp-lib/template.config.js @@ -16,15 +16,24 @@ const glob = util.promisify(require('glob')); const templateUtils = require('../templateUtils'); -function getExampleInfo(config = {}, options = {}) { - const projectPath = config?.root ?? process.cwd(); - const exampleProjectPath = path.join(projectPath, 'example'); +function resolveArgs(config = {}, options = {}) { + const projectRoot = config?.root ?? process.cwd(); - const exists = existsSync(exampleProjectPath); - const exProjectConfig = exists + const libConfig = {...config}; + const libOptions = {...options}; + + if (libConfig.project?.windows?.project?.projectFile?.startsWith('Error:')) { + libConfig.project.windows = + templateUtils.getWindowsDependencyConfig(projectRoot); + } + + const exampleProjectPath = path.join(projectRoot, 'example'); + + const exExists = existsSync(exampleProjectPath); + const exProjectConfig = exExists ? templateUtils.getWindowsProjectConfig(exampleProjectPath) : null; - const exOptions = exists + const exOptions = exExists ? { ...options, template: 'cpp-app', @@ -34,42 +43,48 @@ function getExampleInfo(config = {}, options = {}) { : null; return { - exists, - config: { + libConfig, + libOptions, + exExists, + exConfig: { root: exampleProjectPath, project: { windows: exProjectConfig, }, }, - options: exOptions, + exOptions, }; } const exampleTemplateConfig = require('../cpp-app/template.config'); async function preInstall(config = {}, options = {}) { - const exampleInfo = getExampleInfo(config, options); + const {exExists, exConfig, exOptions} = resolveArgs(config, options); - if (exampleInfo.exists) { - if (options?.logging) { + if (exExists) { + if (exOptions?.logging) { console.log('Running cpp-app template preInstall() for example...'); } - await exampleTemplateConfig.preInstall( - exampleInfo.config, - exampleInfo.options, - ); + await exampleTemplateConfig.preInstall(exConfig, exOptions); } } async function getFileMappings(config = {}, options = {}) { - const {rnwVersion, devMode} = templateUtils.getRnwInfo(config, options); + const {libConfig, libOptions, exExists, exConfig, exOptions} = resolveArgs( + config, + options, + ); + + const {rnwVersion, devMode} = templateUtils.getRnwInfo(libConfig, libOptions); const projectName = - config?.project?.windows?.project?.projectName ?? options?.name ?? 'MyLib'; - const namespace = options?.namespace ?? projectName; + libConfig?.project?.windows?.projects[0]?.projectName ?? + libOptions?.name ?? + 'MyLib'; + const namespace = libOptions?.namespace ?? projectName; const namespaceCpp = namespace.replace(/\./g, '::'); const projectGuid = - config?.project?.windows?.project?.projectGuid + libConfig?.project?.windows?.projects[0]?.projectGuid ?.replace('{', '') .replace('}', '') ?? uuid.v4(); const currentUser = username.sync(); // Gets the current username depending on the platform. @@ -93,7 +108,7 @@ async function getFileMappings(config = {}, options = {}) { projectGuidLower: `{${projectGuid.toLowerCase()}}`, projectGuidUpper: `{${projectGuid.toUpperCase()}}`, - currentUser: currentUser, + currentUser, devMode, @@ -108,8 +123,6 @@ async function getFileMappings(config = {}, options = {}) { nodir: true, }); - const exampleInfo = getExampleInfo(config, options); - for (const file of templateFiles) { const fileMapping = { from: path.resolve(__dirname, path.normalize(file)), @@ -118,7 +131,7 @@ async function getFileMappings(config = {}, options = {}) { }; // Don't copy example files if there is no example in the destination - if (!exampleInfo.exists && fileMapping.to.startsWith('example')) { + if (!exExists && fileMapping.to.startsWith('example')) { continue; } @@ -143,10 +156,10 @@ async function getFileMappings(config = {}, options = {}) { } // Add the file mappings from the cpp-app template for the example app - if (exampleInfo.exists) { + if (exExists) { const exampleFileMappings = await exampleTemplateConfig.getFileMappings( - exampleInfo.config, - exampleInfo.options, + exConfig, + exOptions, ); for (const exFileMap of exampleFileMappings) { @@ -163,13 +176,20 @@ async function getFileMappings(config = {}, options = {}) { } async function postInstall(config = {}, options = {}) { + const {libConfig, libOptions, exExists, exConfig, exOptions} = resolveArgs( + config, + options, + ); + const projectName = - config?.project?.windows?.project?.projectName ?? options?.name ?? 'MyLib'; - const namespace = options?.namespace ?? projectName; + libConfig?.project?.windows?.projects[0]?.projectName ?? + libOptions?.name ?? + 'MyLib'; + const namespace = libOptions?.namespace ?? projectName; const namespaceCpp = namespace.replace(/\./g, '::'); // Update package.json codegen - await templateUtils.updateProjectPackageJson(config, options, { + await templateUtils.updateProjectPackageJson(libConfig, libOptions, { codegenConfig: { windows: { namespace: namespaceCpp + 'Codegen', @@ -179,56 +199,27 @@ async function postInstall(config = {}, options = {}) { }, }); - // Fix babel config's out of date preset - await templateUtils.replaceInFile( - config, - options, - 'babel.config.js', - 'module:metro-react-native-babel-preset', - 'module:@react-native/babel-preset', - ); - - const exampleInfo = getExampleInfo(config, options); - - if (exampleInfo.exists) { - const {rnwVersion} = templateUtils.getRnwInfo( - exampleInfo.config, - exampleInfo.options, - ); + if (exExists) { + const {rnwVersion} = templateUtils.getRnwInfo(exConfig, exOptions); // Update example package.json with new scripts and dependencies - await templateUtils.updateProjectPackageJson( - exampleInfo.config, - exampleInfo.options, - { - scripts: { - windows: 'react-native run-windows', - 'test:windows': 'jest --config jest.config.windows.js', - }, - dependencies: { - 'react-native-windows': rnwVersion, - }, - devDependencies: { - '@rnx-kit/jest-preset': '^0.1.16', - }, + await templateUtils.updateProjectPackageJson(exConfig, exOptions, { + scripts: { + windows: 'react-native run-windows', + 'test:windows': 'jest --config jest.config.windows.js', }, - ); - - // Fix babel config's out of date preset - await templateUtils.replaceInFile( - exampleInfo.config, - exampleInfo.options, - 'babel.config.js', - 'module:metro-react-native-babel-preset', - 'module:@react-native/babel-preset', - ); + dependencies: { + 'react-native-windows': rnwVersion, + }, + devDependencies: { + '@rnx-kit/jest-preset': '^0.1.16', + }, + }); - // Install recently added dependencies (doesn't work, uses wrong yarn) - //await templateUtils.runNpmInstall(exampleInfo.config, exampleInfo.options); + // Install recently added dependencies + await templateUtils.runNpmInstall(libConfig, libOptions); - console.log( - 'Run yarn to install new dependencies for the example project.', - ); + console.log("\n Run 'yarn example windows' start the example project.\n"); } } diff --git a/vnext/templates/cpp-lib/windows/ExperimentalFeatures.props b/vnext/templates/cpp-lib/windows/ExperimentalFeatures.props index 6b4e69eb6c1..be73cb5575a 100644 --- a/vnext/templates/cpp-lib/windows/ExperimentalFeatures.props +++ b/vnext/templates/cpp-lib/windows/ExperimentalFeatures.props @@ -3,7 +3,7 @@ true - false + true true diff --git a/vnext/templates/cpp-lib/windows/MyLib/MyLib.cpp b/vnext/templates/cpp-lib/windows/MyLib/MyLib.cpp index 1a67d656a64..5dd2b28ca5e 100644 --- a/vnext/templates/cpp-lib/windows/MyLib/MyLib.cpp +++ b/vnext/templates/cpp-lib/windows/MyLib/MyLib.cpp @@ -7,11 +7,11 @@ namespace winrt::{{ namespaceCpp }} // See https://microsoft.github.io/react-native-windows/docs/native-modules for details on writing native modules -void {{ name }}::Initialize(React::ReactContext const &reactContext) noexcept { +void {{ pascalName }}::Initialize(React::ReactContext const &reactContext) noexcept { m_context = reactContext; } -double {{ name }}::multiply(double a, double b) noexcept { +double {{ pascalName }}::multiply(double a, double b) noexcept { return a * b; } diff --git a/vnext/templates/cpp-lib/windows/MyLib/MyLib.h b/vnext/templates/cpp-lib/windows/MyLib/MyLib.h index 8ac21c72e56..4f89df5b5bb 100644 --- a/vnext/templates/cpp-lib/windows/MyLib/MyLib.h +++ b/vnext/templates/cpp-lib/windows/MyLib/MyLib.h @@ -13,8 +13,8 @@ namespace winrt::{{ namespaceCpp }} { -REACT_MODULE({{ name }}) -struct {{ name }} +REACT_MODULE({{ pascalName }}) +struct {{ pascalName }} { using ModuleSpec = {{ namespaceCpp }}Codegen::{{ pascalName }}Spec; diff --git a/vnext/templates/templateUtils.js b/vnext/templates/templateUtils.js index 4f966e91e54..91e98433303 100644 --- a/vnext/templates/templateUtils.js +++ b/vnext/templates/templateUtils.js @@ -17,12 +17,13 @@ const _ = require('lodash'); const pkgUtils = require('@react-native-windows/package-utils'); const projectConfig = require('@react-native-windows/cli').projectConfig; +const dependencyConfig = require('@react-native-windows/cli').dependencyConfig; function getRnwInfo(config = {}, options = {}) { - const projectPath = config?.root ?? process.cwd(); + const projectRoot = config?.root ?? process.cwd(); const rnwPath = path.dirname( - require.resolve('react-native-windows', [projectPath]), + require.resolve('react-native-windows', [projectRoot]), ); const rnwVersion = require(path.join(rnwPath, 'package.json')).version; @@ -52,6 +53,18 @@ function getWindowsProjectConfig(root) { return projectConfig(root, userConfig); } +function getWindowsDependencyConfig(root) { + if (!existsSync(root)) { + return {}; + } + + const userConfigPath = path.join(root, 'react-native.config.js'); + + const userConfig = existsSync(userConfigPath) ? require(userConfigPath) : {}; + + return dependencyConfig(root, userConfig); +} + function pascalCase(str) { const camelCase = _.camelCase(str); return camelCase[0].toUpperCase() + camelCase.substr(1); @@ -78,12 +91,12 @@ async function replaceInFile( } async function runNpmInstall(config = {}, options = {}) { - const projectPath = config?.root ?? process.cwd(); + const projectRoot = config?.root ?? process.cwd(); if (options?.logging) { - console.log(`Installing dependencies for ${projectPath}...`); + console.log(`Installing dependencies for ${projectRoot}...`); } - const isYarn = existsSync(path.join(projectPath, 'yarn.lock')); + const isYarn = existsSync(path.join(projectRoot, 'yarn.lock')); await exec( isYarn ? 'yarn' : 'npm i', options?.logging ? {stdio: 'inherit'} : {}, @@ -91,19 +104,19 @@ async function runNpmInstall(config = {}, options = {}) { } async function updateProjectPackageJson(config = {}, options = {}, props = {}) { - const projectPath = config?.root ?? process.cwd(); + const projectRoot = config?.root ?? process.cwd(); const projectPackage = await pkgUtils.WritableNpmPackage.fromPath( - projectPath, + projectRoot, ); if (!projectPackage) { throw new Error( - `The directory '${projectPath}' is not the root of an npm package`, + `The directory '${projectRoot}' is not the root of an npm package`, ); } if (options?.logging) { - console.log(`Modifying ${path.join(projectPath, 'package.json')}...`); + console.log(`Modifying ${path.join(projectRoot, 'package.json')}...`); } await projectPackage.mergeProps(props); } @@ -111,6 +124,7 @@ async function updateProjectPackageJson(config = {}, options = {}, props = {}) { module.exports = { getRnwInfo, getWindowsProjectConfig, + getWindowsDependencyConfig, pascalCase, replaceInFile, runNpmInstall, From 0187fa18f7c09481a886ac7f1a2f48c266afc868 Mon Sep 17 00:00:00 2001 From: "Jon Thysell (JAUNTY)" Date: Tue, 16 Jan 2024 08:58:46 -0800 Subject: [PATCH 08/10] fix yarn missing --- vnext/templates/templateUtils.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/vnext/templates/templateUtils.js b/vnext/templates/templateUtils.js index 91e98433303..d3c9646d95a 100644 --- a/vnext/templates/templateUtils.js +++ b/vnext/templates/templateUtils.js @@ -97,10 +97,15 @@ async function runNpmInstall(config = {}, options = {}) { console.log(`Installing dependencies for ${projectRoot}...`); } const isYarn = existsSync(path.join(projectRoot, 'yarn.lock')); - await exec( - isYarn ? 'yarn' : 'npm i', - options?.logging ? {stdio: 'inherit'} : {}, - ); + const cmd = isYarn ? 'yarn' : 'npm i'; + + try { + await exec(cmd, options?.logging ? {stdio: 'inherit'} : {}); + } catch (ex) { + console.log( + `Failed to install dependencies for ${projectRoot}. Please run '${cmd}' manually to update the dependencies.`, + ); + } } async function updateProjectPackageJson(config = {}, options = {}, props = {}) { From 530fa2ebf367a781216c3f55749bce76945b66d4 Mon Sep 17 00:00:00 2001 From: "Jon Thysell (JAUNTY)" Date: Tue, 16 Jan 2024 15:25:59 -0800 Subject: [PATCH 09/10] Match example output text with android/ios examples --- vnext/templates/cpp-lib/template.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vnext/templates/cpp-lib/template.config.js b/vnext/templates/cpp-lib/template.config.js index 3f7a463d0e1..da150434da0 100644 --- a/vnext/templates/cpp-lib/template.config.js +++ b/vnext/templates/cpp-lib/template.config.js @@ -219,7 +219,9 @@ async function postInstall(config = {}, options = {}) { // Install recently added dependencies await templateUtils.runNpmInstall(libConfig, libOptions); - console.log("\n Run 'yarn example windows' start the example project.\n"); + console.log( + '\nRun the example app on Windows:\n\n > yarn example windows\n', + ); } } From 426eeb7a8905a9836844bb8caa78e283c6a9d2f5 Mon Sep 17 00:00:00 2001 From: "Jon Thysell (JAUNTY)" Date: Tue, 16 Jan 2024 16:03:37 -0800 Subject: [PATCH 10/10] remove image asset resolver --- vnext/templates/cpp-app/metro.config.js | 2 -- vnext/templates/cpp-lib/example/metro.config.js | 3 --- 2 files changed, 5 deletions(-) diff --git a/vnext/templates/cpp-app/metro.config.js b/vnext/templates/cpp-app/metro.config.js index f5e6fd068c8..686d4a9eda7 100644 --- a/vnext/templates/cpp-app/metro.config.js +++ b/vnext/templates/cpp-app/metro.config.js @@ -48,8 +48,6 @@ const config = { inlineRequires: true, }, }), - // This fixes the 'missing-asset-registry-path` error (see https://github.com/microsoft/react-native-windows/issues/11437) - assetRegistryPath: 'react-native/Libraries/Image/AssetRegistry', }, }; diff --git a/vnext/templates/cpp-lib/example/metro.config.js b/vnext/templates/cpp-lib/example/metro.config.js index 408bdd6bdcd..84f46590d09 100644 --- a/vnext/templates/cpp-lib/example/metro.config.js +++ b/vnext/templates/cpp-lib/example/metro.config.js @@ -68,9 +68,6 @@ const config = { inlineRequires: true, }, }), - - // This fixes the 'missing-asset-registry-path` error (see https://github.com/microsoft/react-native-windows/issues/11437) - assetRegistryPath: 'react-native/Libraries/Image/AssetRegistry', }, };