feat(windows): add support for New Architecture (#1885)

Run `install-windows-test-app --use-fabric` to enable New Architecture
on 0.74 or higher. Note that this feature is experimental and
incomplete.
This commit is contained in:
Tommy Nguyen 2024-03-19 18:14:24 +01:00 коммит произвёл GitHub
Родитель 08bbe5d9f0
Коммит 6b94728ffb
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
79 изменённых файлов: 1840 добавлений и 1079 удалений

8
.github/workflows/build.yml поставляемый
Просмотреть файл

@ -584,9 +584,11 @@ jobs:
uses: ./.github/actions/yarn
with:
immutable: ${{ github.event_name != 'schedule' }}
- name: Build bundle and create solution
- name: Bundle JavaScript
run: |
yarn build:windows
- name: Generate Visual Studio solution
run: |
yarn install-windows-test-app
working-directory: example
- name: Test `react-native config`
@ -642,9 +644,11 @@ jobs:
uses: ./.github/actions/init-test-app
with:
platform: ${{ matrix.template }}
- name: Build bundle and create solution
- name: Bundle JavaScript
run: |
yarn build:windows
- name: Generate Visual Studio solution
run: |
if ("${{ matrix.template }}" -eq "all") { yarn install-windows-test-app }
else { yarn install-windows-test-app --project-directory=. }
working-directory: template-example

Просмотреть файл

@ -59,11 +59,11 @@
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros">
<ReactTestAppDir Condition="'$(ReactTestAppDir)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-test-app\package.json'))\node_modules\react-native-test-app\windows\ReactTestApp\</ReactTestAppDir>
<ReactTestAppProjectDir Condition="'$(ReactTestAppProjectDir)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\.generated\windows\ReactTestApp\ReactTestApp.vcxproj'))\node_modules\.generated\windows\ReactTestApp\</ReactTestAppProjectDir>
<ReactAppProjectDir Condition="'$(ReactAppProjectDir)'==''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\.generated\windows\UWP\ReactTestApp.vcxproj'))\node_modules\.generated\windows\UWP</ReactAppProjectDir>
<ReactAppSharedDir Condition="'$(ReactAppSharedDir)'==''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-test-app\package.json'))\node_modules\react-native-test-app\windows\Shared</ReactAppSharedDir>
</PropertyGroup>
<PropertyGroup>
<IncludePath>$(ReactTestAppDir);$(IncludePath)</IncludePath>
<IncludePath>$(ReactAppSharedDir);$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
@ -78,7 +78,7 @@
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories);$(ReactTestAppProjectDir)\$(Platform)\$(Configuration)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories);$(ReactAppProjectDir)\$(Platform)\$(Configuration)</AdditionalLibraryDirectories>
<AdditionalDependencies>%(AdditionalDependencies);Manifest.obj;pch.obj</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>

Просмотреть файл

@ -50,8 +50,8 @@
"test-app.gradle",
"test_app.rb",
"visionos",
"windows/*.{mjs,props}",
"windows/ReactTestApp"
"windows",
"!.clang-format"
],
"main": "scripts/configure-projects.js",
"bin": {

Просмотреть файл

@ -10,6 +10,7 @@ const path = require("node:path");
const {
findNearest,
getPackageVersion,
readTextFile,
toVersionNumber,
v,
} = require("./helpers");
@ -76,22 +77,15 @@ function iosProjectPath() {
}
/**
* @param {string} sourceDir
* @param {string} solutionFile
* @returns {ProjectParams["windows"]["project"]}
*/
function windowsProjectPath(sourceDir) {
return {
projectFile: path.relative(
sourceDir,
path.join(
"node_modules",
".generated",
"windows",
"ReactTestApp",
"ReactTestApp.vcxproj"
)
),
};
function windowsProjectPath(solutionFile, fs = nodefs) {
const sln = readTextFile(solutionFile, fs);
const m = sln.match(
/([^"]*?node_modules[/\\].generated[/\\]windows[/\\].*?\.vcxproj)/
);
return { projectFile: m ? m[1] : `(Failed to parse '${solutionFile}')` };
}
/**
@ -132,10 +126,11 @@ function configureProjects({ android, ios, windows }, fs = nodefs) {
}
if (windows && fs.existsSync(windows.solutionFile)) {
const { sourceDir, solutionFile } = windows;
config.windows = {
sourceDir: windows.sourceDir,
solutionFile: path.relative(windows.sourceDir, windows.solutionFile),
project: windowsProjectPath(path.resolve(projectRoot, windows.sourceDir)),
sourceDir,
solutionFile: path.relative(sourceDir, solutionFile),
project: windowsProjectPath(solutionFile, fs),
};
}

Просмотреть файл

@ -38,8 +38,7 @@ function getLanguage(output) {
indent: " ",
level: 1,
header: [
"#ifndef REACTTESTAPP_MANIFEST_H_",
"#define REACTTESTAPP_MANIFEST_H_",
"#pragma once",
"",
"#include <any>",
"#include <map>",
@ -56,8 +55,6 @@ function getLanguage(output) {
"",
"} // namespace ReactTestApp",
"",
"#endif // REACTTESTAPP_MANIFEST_H_",
"",
].join("\n"),
},
arrayProperty: (name, type, required) => {
@ -234,7 +231,7 @@ function main() {
"Manifest.kt"
),
path.join(scriptsDir, "..", "ios", "ReactTestApp", "Manifest.swift"),
path.join(scriptsDir, "..", "windows", "ReactTestApp", "Manifest.h"),
path.join(scriptsDir, "..", "windows", "Shared", "Manifest.h"),
].forEach((output) => generate(schema, output).catch(console.error));
}

Просмотреть файл

@ -105,23 +105,6 @@ function readJSONFile(path, fs = nodefs) {
return JSON.parse(readTextFile(path, fs));
}
/**
* Returns version string of specified module.
* @param {string} module
* @returns {string}
*/
function getPackageVersion(module, startDir = process.cwd(), fs = nodefs) {
const options = { paths: [startDir] };
const manifestPath = require.resolve(`${module}/package.json`, options);
const mod = readJSONFile(manifestPath, fs);
const version = mod["version"];
if (typeof version !== "string") {
throw new Error(`Invalid version number: ${module}@${version}`);
}
return version;
}
/**
* @template T
* @param {string[]} dependencyChain
@ -157,6 +140,33 @@ function v(major, minor, patch) {
return major * 1000000 + minor * 1000 + patch;
}
/**
* Writes data to specified file path.
* @param {string} file
* @param {string} data
* @returns {Promise<void>}
*/
function writeTextFile(file, data, fs = nodefs.promises) {
return fs.writeFile(file, data, { encoding: "utf-8", mode: 0o644 });
}
/**
* Returns version string of specified module.
* @param {string} module
* @returns {string}
*/
function getPackageVersion(module, startDir = process.cwd(), fs = nodefs) {
const options = { paths: [startDir] };
const manifestPath = require.resolve(`${module}/package.json`, options);
const mod = readJSONFile(manifestPath, fs);
const version = mod["version"];
if (typeof version !== "string") {
throw new Error(`Invalid version number: ${module}@${version}`);
}
return version;
}
exports.findFile = findFile;
exports.findNearest = findNearest;
exports.getPackageVersion = getPackageVersion;
@ -167,3 +177,4 @@ exports.readTextFile = readTextFile;
exports.requireTransitive = requireTransitive;
exports.toVersionNumber = toVersionNumber;
exports.v = v;
exports.writeTextFile = writeTextFile;

Просмотреть файл

@ -86,6 +86,51 @@ type InferredOptionTypes<O> = { [key in keyof O]: InferredOptionType<O[key]> };
export type Args<O> = InferredOptionTypes<O> & { _: string[] };
/***********************
* windows/project.mjs *
***********************/
export type AppxBundle = {
appName: string;
appxManifest: string;
assetItems: string;
assetItemFilters: string;
assetFilters: string;
packageCertificate: string;
singleApp?: string;
};
export type MSBuildProjectOptions = {
autolink: boolean;
useFabric?: boolean;
useHermes?: boolean;
useNuGet: boolean;
};
export type MSBuildProjectParams = {
projDir: string;
projectFileName: string;
projectFiles: [string, Record<string, string>?][];
solutionTemplatePath: string;
};
export type ProjectInfo = {
version: string;
versionNumber: number;
bundle: AppxBundle;
hermesVersion: string | false | null;
nugetDependencies: [string, string][];
useExperimentalNuGet: boolean;
useFabric: boolean;
usePackageReferences: boolean;
xamlVersion: string;
};
export type MSBuildProjectConfigurator = (
info: ProjectInfo,
options: MSBuildProjectOptions
) => MSBuildProjectParams;
/************************
* windows/test-app.mjs *
************************/

Просмотреть файл

@ -71,6 +71,7 @@ function validateManifest(manifestPath, fs = nodefs) {
/**
* @param {("file" | "stdout")=} outputMode Whether to output to `file` or `stdout`
* @param {string=} projectRoot Path to root of project
* @returns {number}
*/
function validate(
outputMode = "stdout",
@ -81,7 +82,7 @@ function validate(
const manifest = validateManifest(manifestPath, fs);
if (typeof manifest === "number") {
process.exitCode = manifest;
return;
return manifest;
}
const nodeModulesPath = findFile(NODE_MODULES, projectRoot, fs);
@ -90,7 +91,7 @@ function validate(
`Failed to find '${NODE_MODULES}'. Please make sure you've installed npm dependencies.`
);
process.exitCode = 2;
return;
return 2;
}
const copy = JSON.stringify(manifest);
@ -103,15 +104,12 @@ function validate(
.digest("hex");
const cppHeader = [
"// clang-format off",
"#ifndef REACTTESTAPP_APP_JSON_H_",
"#define REACTTESTAPP_APP_JSON_H_",
"#pragma once",
"",
`#define ReactTestApp_AppManifest "${escapedCopy}"`,
`#define ReactTestApp_AppManifestChecksum "${checksum}"`,
`#define ReactTestApp_AppManifestLength ${copy.length}`,
"",
"#endif // REACTTESTAPP_APP_JSON_H_",
"",
].join("\n");
const manifestCopyDest = path.join(
nodeModulesPath,
@ -130,6 +128,8 @@ function validate(
} else {
console.log(escapedCopy);
}
return 0;
}
exports.validate = validate;

Просмотреть файл

@ -208,41 +208,65 @@ describe("npm pack", () => {
"visionos/ReactTestAppUITests/ReactTestAppUITests.swift",
"visionos/test_app.rb",
"windows/ExperimentalFeatures.props",
"windows/ReactTestApp/App.cpp",
"windows/ReactTestApp/App.h",
"windows/ReactTestApp/App.idl",
"windows/ReactTestApp/App.xaml",
"windows/ReactTestApp/Assets/LockScreenLogo.scale-200.png",
"windows/ReactTestApp/Assets/SplashScreen.scale-200.png",
"windows/ReactTestApp/Assets/Square150x150Logo.scale-200.png",
"windows/ReactTestApp/Assets/Square44x44Logo.scale-200.png",
"windows/ReactTestApp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png",
"windows/ReactTestApp/Assets/StoreLogo.png",
"windows/ReactTestApp/Assets/Wide310x150Logo.scale-200.png",
"windows/ReactTestApp/AutolinkedNativeModules.g.cpp",
"windows/ReactTestApp/AutolinkedNativeModules.g.h",
"windows/ReactTestApp/AutolinkedNativeModules.g.props",
"windows/ReactTestApp/AutolinkedNativeModules.g.targets",
"windows/ReactTestApp/MainPage.cpp",
"windows/ReactTestApp/MainPage.h",
"windows/ReactTestApp/MainPage.idl",
"windows/ReactTestApp/MainPage.xaml",
"windows/ReactTestApp/Manifest.cpp",
"windows/ReactTestApp/Manifest.h",
"windows/ReactTestApp/Package.appxmanifest",
"windows/ReactTestApp/PropertySheet.props",
"windows/ReactTestApp/ReactInstance.cpp",
"windows/ReactTestApp/ReactInstance.h",
"windows/ReactTestApp/ReactPackageProvider.cpp",
"windows/ReactTestApp/ReactPackageProvider.h",
"windows/ReactTestApp/ReactTestApp.vcxproj",
"windows/ReactTestApp/ReactTestApp.vcxproj.filters",
"windows/ReactTestApp/Session.h",
"windows/ReactTestApp/ValidateManifest.targets",
"windows/ReactTestApp/packages.config",
"windows/ReactTestApp/pch.cpp",
"windows/ReactTestApp/pch.h",
"windows/Shared/JSValueWriterHelper.h",
"windows/Shared/Manifest.cpp",
"windows/Shared/Manifest.h",
"windows/Shared/ReactInstance.cpp",
"windows/Shared/ReactInstance.h",
"windows/Shared/Session.h",
"windows/Shared/ValidateManifest.targets",
"windows/UWP/App.cpp",
"windows/UWP/App.h",
"windows/UWP/App.idl",
"windows/UWP/App.xaml",
"windows/UWP/Assets/LockScreenLogo.scale-200.png",
"windows/UWP/Assets/SplashScreen.scale-200.png",
"windows/UWP/Assets/Square150x150Logo.scale-200.png",
"windows/UWP/Assets/Square44x44Logo.scale-200.png",
"windows/UWP/Assets/Square44x44Logo.targetsize-24_altform-unplated.png",
"windows/UWP/Assets/StoreLogo.png",
"windows/UWP/Assets/Wide310x150Logo.scale-200.png",
"windows/UWP/AutolinkedNativeModules.g.cpp",
"windows/UWP/AutolinkedNativeModules.g.h",
"windows/UWP/AutolinkedNativeModules.g.props",
"windows/UWP/AutolinkedNativeModules.g.targets",
"windows/UWP/MainPage.cpp",
"windows/UWP/MainPage.h",
"windows/UWP/MainPage.idl",
"windows/UWP/MainPage.xaml",
"windows/UWP/Package.appxmanifest",
"windows/UWP/PropertySheet.props",
"windows/UWP/ReactTestApp.vcxproj",
"windows/UWP/ReactTestApp.vcxproj.filters",
"windows/UWP/packages.config",
"windows/UWP/pch.cpp",
"windows/UWP/pch.h",
"windows/Win32/AutolinkedNativeModules.g.cpp",
"windows/Win32/AutolinkedNativeModules.g.h",
"windows/Win32/Images/LockScreenLogo.scale-200.png",
"windows/Win32/Images/SplashScreen.scale-200.png",
"windows/Win32/Images/Square150x150Logo.scale-200.png",
"windows/Win32/Images/Square44x44Logo.scale-200.png",
"windows/Win32/Images/Square44x44Logo.targetsize-24_altform-unplated.png",
"windows/Win32/Images/StoreLogo.png",
"windows/Win32/Images/Wide310x150Logo.scale-200.png",
"windows/Win32/Main.cpp",
"windows/Win32/Main.h",
"windows/Win32/Main.ico",
"windows/Win32/Main.rc",
"windows/Win32/Main.small.ico",
"windows/Win32/Package.appxmanifest",
"windows/Win32/ReactApp.Package.wapproj",
"windows/Win32/ReactApp.vcxproj",
"windows/Win32/ReactApp.vcxproj.filters",
"windows/Win32/pch.cpp",
"windows/Win32/pch.h",
"windows/Win32/resource.h",
"windows/Win32/targetver.h",
"windows/project.mjs",
"windows/test-app.mjs",
"windows/uwp.mjs",
"windows/win32.mjs",
]);
});
});

Просмотреть файл

@ -1,68 +0,0 @@
// @ts-check
import { equal, match } from "node:assert/strict";
import { afterEach, describe, it } from "node:test";
import { copy as copyActual } from "../../windows/test-app.mjs";
import { fs, setMockFiles, toJSON } from "../fs.mock.mjs";
/**
* Waits until the specified predicate returns `true`.
* @param {() => boolean} predicate
*/
async function waitUntil(predicate) {
for (let i = 0; i < 3; ++i) {
if (predicate()) {
break;
}
await new Promise((resolve) => setTimeout(resolve, 100));
}
}
describe("copy()", () => {
/** @type {typeof copyActual} */
const copy = (src, dest) => copyActual(src, dest, fs);
afterEach(() => setMockFiles());
it("recursively copies all files under directory", async () => {
const pngMagic = "‰PNG␍␊␚␊";
setMockFiles({
"assets/1.png": pngMagic,
"assets/2.png": pngMagic,
"assets/3.png": pngMagic,
"assets/more/1.png": pngMagic,
"assets/more/2.png": pngMagic,
"assets/more/3.png": pngMagic,
});
// Wait until all files have been copied
const writeDone = waitUntil(() => {
const files = Object.keys(toJSON());
return files.length === 12;
});
copy("assets", "assets copy");
await writeDone;
const expected = [
/\/assets\/1\.png$/,
/\/assets\/2\.png$/,
/\/assets\/3\.png$/,
/\/assets\/more\/1\.png$/,
/\/assets\/more\/2\.png$/,
/\/assets\/more\/3\.png$/,
/\/assets copy\/1\.png$/,
/\/assets copy\/2\.png$/,
/\/assets copy\/3\.png$/,
/\/assets copy\/more\/1\.png$/,
/\/assets copy\/more\/2\.png$/,
/\/assets copy\/more\/3\.png$/,
];
const files = Object.keys(toJSON());
equal(files.length, expected.length);
for (let i = 0; i < expected.length; ++i) {
match(files[i], expected[i]);
}
});
});

Просмотреть файл

@ -1,7 +1,6 @@
// @ts-check
import { equal, fail, match, rejects } from "node:assert/strict";
import { afterEach, describe, it } from "node:test";
import { promisify } from "node:util";
import { readTextFile as readTextFileActual } from "../../scripts/helpers.js";
import { copyAndReplace as copyAndReplaceActual } from "../../windows/test-app.mjs";
import { fs, setMockFiles } from "../fs.mock.mjs";
@ -9,57 +8,56 @@ import { spy } from "../spy.mjs";
describe("copyAndReplace()", () => {
/** @type {typeof copyAndReplaceActual} */
const copyAndReplace = (src, dst, r, cb) =>
copyAndReplaceActual(src, dst, r, cb, fs);
const copyAndReplaceAsync = promisify(copyAndReplace);
const copyAndReplace = (src, dst, r) =>
copyAndReplaceActual(src, dst, r, fs.promises);
/** @type {typeof readTextFileActual} */
const readTextFile = (p) => readTextFileActual(p, fs);
afterEach(() => setMockFiles());
it("copies files if no modifications are needed", async (t) => {
t.mock.method(fs, "copyFile");
// TODO: Skip because `memfs` hasn't implemented `fs.cp` or `fs.promises.cp`
it.skip("copies files if no modifications are needed", async (t) => {
t.mock.method(fs.promises, "cp");
setMockFiles({
"ReactTestApp.png": "binary",
"test/.placeholder": "",
});
await copyAndReplaceAsync(
await copyAndReplace(
"ReactTestApp.png",
"test/ReactTestApp.png",
undefined
);
equal(spy(fs.copyFile).calls.length, 1);
equal(spy(fs.promises.cp).calls.length, 1);
equal(readTextFile("test/ReactTestApp.png"), "binary");
});
it("replaces file content", async (t) => {
t.mock.method(fs, "copyFile");
t.mock.method(fs.promises, "cp");
setMockFiles({
"ReactTestApp.png": "binary",
"test/.placeholder": "",
});
await copyAndReplaceAsync("ReactTestApp.png", "test/ReactTestApp.png", {
await copyAndReplace("ReactTestApp.png", "test/ReactTestApp.png", {
binary: "text",
});
equal(spy(fs.copyFile).calls.length, 0);
equal(spy(fs.promises.cp).calls.length, 0);
equal(readTextFile("test/ReactTestApp.png"), "text");
});
it("throws on error", async () => {
await rejects(copyAndReplaceAsync("ReactTestApp.png", "", {}), (err) => {
await rejects(copyAndReplace("ReactTestApp.png", "", {}), (err) => {
if (!(err instanceof Error)) {
fail("Expected an Error");
}
match(err.message, /ENOENT/);
return true;
});
await rejects(copyAndReplaceAsync("ReactTestApp.sln", "", {}), (err) => {
await rejects(copyAndReplace("ReactTestApp.sln", "", {}), (err) => {
if (!(err instanceof Error)) {
fail("Expected an Error");
}

Просмотреть файл

@ -47,21 +47,4 @@ describe("generateSolution()", () => {
"Could not find 'react-native-windows'"
);
});
it("exits if 'react-native-test-app' folder cannot be found", () => {
setMockFiles({
[path.resolve("", "package.json")]: testManifest,
[path.resolve(
"",
"node_modules",
"react-native-windows",
"package.json"
)]: "{}",
});
equal(
generateSolution("test", options),
"Could not find 'react-native-test-app'"
);
});
});

Просмотреть файл

@ -2,7 +2,7 @@
import { deepEqual, equal, match } from "node:assert/strict";
import * as path from "node:path";
import { afterEach, describe, it } from "node:test";
import { getBundleResources as getBundleResourcesActual } from "../../windows/test-app.mjs";
import { getBundleResources as getBundleResourcesActual } from "../../windows/project.mjs";
import { fs, setMockFiles } from "../fs.mock.mjs";
import { spy } from "../spy.mjs";

Просмотреть файл

@ -2,7 +2,7 @@
import { equal } from "node:assert/strict";
import * as path from "node:path";
import { afterEach, describe, it } from "node:test";
import { getHermesVersion as getHermesVersionActual } from "../../windows/test-app.mjs";
import { getHermesVersion as getHermesVersionActual } from "../../windows/project.mjs";
import { fs, setMockFiles } from "../fs.mock.mjs";
/**

Просмотреть файл

@ -1,12 +1,12 @@
// @ts-check
import { equal } from "node:assert/strict";
import { describe, it } from "node:test";
import { nuGetPackage } from "../../windows/test-app.mjs";
import { nugetPackage } from "../../windows/project.mjs";
describe("nuGetPackage()", () => {
describe("nugetPackage()", () => {
it("returns a NuGet package entry", () => {
equal(
nuGetPackage("com.reacttestapp.id", "1.0.0"),
nugetPackage("com.reacttestapp.id", "1.0.0"),
'<package id="com.reacttestapp.id" version="1.0.0" targetFramework="native"/>'
);
});

Просмотреть файл

@ -1,7 +1,7 @@
// @ts-check
import { deepEqual, equal, match } from "node:assert/strict";
import { afterEach, describe, it } from "node:test";
import { parseResources as parseResourcesActual } from "../../windows/test-app.mjs";
import { parseResources as parseResourcesActual } from "../../windows/project.mjs";
import { fs, setMockFiles } from "../fs.mock.mjs";
import { spy } from "../spy.mjs";

Просмотреть файл

@ -2,11 +2,18 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Microsoft.ReactNative Experimental Features">
<!--
Enables default usage of Hermes.
Enables New Architecture.
See https://reactnative.dev/docs/new-architecture-intro
-->
<UseFabric>false</UseFabric>
<!--
Use Hermes instead of Chakra as the JS engine (enabled by default on 0.73+).
See https://microsoft.github.io/react-native-windows/docs/hermes
-->
<!-- UseHermes>true</UseHermes -->
<UseHermes>true</UseHermes>
<!--
Changes compilation to assume use of WinUI 3 instead of System XAML.
@ -15,5 +22,16 @@
See https://microsoft.github.io/react-native-windows/docs/winui3
-->
<UseWinUI3>false</UseWinUI3>
<!--
Changes compilation to assume use of Microsoft.ReactNative NuGet packages
instead of building the framework from source.
Requires creation of new project.
See https://microsoft.github.io/react-native-windows/docs/nuget
-->
<UseExperimentalNuget>false</UseExperimentalNuget>
<ReactExperimentalFeaturesSet>true</ReactExperimentalFeaturesSet>
</PropertyGroup>
</Project>

Просмотреть файл

@ -1,13 +0,0 @@
#include "pch.h"
#include "ReactPackageProvider.h"
#include <NativeModules.h>
using winrt::Microsoft::ReactNative::IReactPackageBuilder;
using winrt::ReactTestApp::implementation::ReactPackageProvider;
void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept
{
AddAttributedModules(packageBuilder);
}

Просмотреть файл

@ -1,12 +0,0 @@
#pragma once
#include <winrt/Microsoft.ReactNative.h>
namespace winrt::ReactTestApp::implementation
{
struct ReactPackageProvider
: implements<ReactPackageProvider, Microsoft::ReactNative::IReactPackageProvider> {
public:
void CreatePackage(Microsoft::ReactNative::IReactPackageBuilder const &) noexcept;
};
} // namespace winrt::ReactTestApp::implementation

Просмотреть файл

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<DisableFastUpToDateCheck>True</DisableFastUpToDateCheck>
<ValidateManifestCommand>node -e "require('react-native-test-app/scripts/validate-manifest').validate('file')"</ValidateManifestCommand>
</PropertyGroup>
<Target Name="ValidateManifest" BeforeTargets="PrepareForBuild">
<Exec Command="$(ValidateManifestCommand)" WorkingDirectory="$(SolutionDir)" />
</Target>
</Project>

Просмотреть файл

@ -0,0 +1,45 @@
#pragma once
#include <any>
#include <map>
#include <string>
#include <vector>
#include <winrt/Microsoft.ReactNative.h>
#include <winrt/base.h>
namespace ReactApp
{
void JSValueWriterWriteValue(winrt::Microsoft::ReactNative::IJSValueWriter const &writer,
std::any const &value)
{
if (value.type() == typeid(bool)) {
writer.WriteBoolean(std::any_cast<bool>(value));
} else if (value.type() == typeid(std::int64_t)) {
writer.WriteInt64(std::any_cast<std::int64_t>(value));
} else if (value.type() == typeid(std::uint64_t)) {
writer.WriteInt64(std::any_cast<std::uint64_t>(value));
} else if (value.type() == typeid(double)) {
writer.WriteDouble(std::any_cast<double>(value));
} else if (value.type() == typeid(std::nullopt)) {
writer.WriteNull();
} else if (value.type() == typeid(std::string)) {
writer.WriteString(winrt::to_hstring(std::any_cast<std::string>(value)));
} else if (value.type() == typeid(std::vector<std::any>)) {
writer.WriteArrayBegin();
for (auto &&entry : std::any_cast<std::vector<std::any>>(value)) {
JSValueWriterWriteValue(writer, entry);
}
writer.WriteArrayEnd();
} else if (value.type() == typeid(std::map<std::string, std::any>)) {
writer.WriteObjectBegin();
for (auto &[key, val] : std::any_cast<std::map<std::string, std::any>>(value)) {
writer.WritePropertyName(winrt::to_hstring(key));
JSValueWriterWriteValue(writer, val);
}
writer.WriteObjectEnd();
} else {
assert(false);
}
}
} // namespace ReactApp

Просмотреть файл

Просмотреть файл

@ -1,8 +1,7 @@
// This file was generated by generate-manifest.mjs.
// DO NOT MODIFY. ALL CHANGES WILL BE OVERWRITTEN.
#ifndef REACTTESTAPP_MANIFEST_H_
#define REACTTESTAPP_MANIFEST_H_
#pragma once
#include <any>
#include <map>
@ -33,5 +32,3 @@ namespace ReactTestApp
std::optional<std::tuple<Manifest, std::string>> GetManifest(const char *const json = nullptr);
} // namespace ReactTestApp
#endif // REACTTESTAPP_MANIFEST_H_

Просмотреть файл

@ -12,20 +12,26 @@
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Web.Http.Headers.h>
#if __has_include("AppRegistry.h")
#include "AppRegistry.h"
#endif // __has_include("AppRegistry.h")
#include "AutolinkedNativeModules.g.h"
#include "ReactPackageProvider.h"
using facebook::jsi::Runtime;
using ReactTestApp::ReactInstance;
using winrt::Microsoft::ReactNative::InstanceLoadedEventArgs;
using winrt::ReactTestApp::implementation::ReactPackageProvider;
using winrt::Windows::Foundation::IAsyncOperation;
using winrt::Windows::Foundation::IInspectable;
using winrt::Windows::Foundation::PropertyValue;
using winrt::Windows::Foundation::Uri;
using winrt::Windows::Storage::ApplicationData;
using winrt::Windows::Web::Http::HttpClient;
namespace winrt
{
using winrt::Microsoft::ReactNative::InstanceLoadedEventArgs;
using winrt::Microsoft::ReactNative::IReactPackageBuilder;
using winrt::Microsoft::ReactNative::IReactPackageProvider;
using winrt::Windows::Foundation::IAsyncOperation;
using winrt::Windows::Foundation::IInspectable;
using winrt::Windows::Foundation::PropertyValue;
using winrt::Windows::Foundation::Uri;
using winrt::Windows::Storage::ApplicationData;
using winrt::Windows::Web::Http::HttpClient;
} // namespace winrt
namespace
{
@ -36,19 +42,53 @@ namespace
winrt::hstring const kUseFastRefresh = L"useFastRefresh";
winrt::hstring const kUseWebDebugger = L"useWebDebugger";
std::optional<winrt::hstring> GetBundleName(std::optional<winrt::hstring> const &bundleRoot)
{
constexpr std::wstring_view const bundleExtension = L".bundle";
std::filesystem::path bundlePath{L"Bundle\\"};
if (bundleRoot.has_value()) {
std::wstring_view root = bundleRoot.value();
for (auto &&ext : {L".windows", L".native", L""}) {
bundlePath.replace_filename(root).replace_extension(ext) += bundleExtension;
if (std::filesystem::exists(bundlePath)) {
return winrt::hstring{bundlePath.stem().wstring()};
}
}
} else {
for (auto &&main : ReactTestApp::JSBundleNames) {
bundlePath.replace_filename(main) += bundleExtension;
if (std::filesystem::exists(bundlePath)) {
return winrt::hstring{main};
}
}
}
return std::nullopt;
}
bool RetrieveLocalSetting(winrt::hstring const &key, bool defaultValue)
{
auto localSettings = ApplicationData::Current().LocalSettings();
auto localSettings = winrt::ApplicationData::Current().LocalSettings();
auto values = localSettings.Values();
return winrt::unbox_value_or<bool>(values.Lookup(key), defaultValue);
}
void StoreLocalSetting(winrt::hstring const &key, bool value)
{
auto localSettings = ApplicationData::Current().LocalSettings();
auto localSettings = winrt::ApplicationData::Current().LocalSettings();
auto values = localSettings.Values();
values.Insert(key, PropertyValue::CreateBoolean(value));
values.Insert(key, winrt::PropertyValue::CreateBoolean(value));
}
struct ReactPackageProvider
: winrt::implements<ReactPackageProvider, winrt::IReactPackageProvider> {
// IReactPackageProvider details
void CreatePackage(winrt::IReactPackageBuilder const &packageBuilder) noexcept
{
AddAttributedModules(packageBuilder, true);
}
};
} // namespace
std::vector<std::wstring_view> const ReactTestApp::JSBundleNames = {
@ -67,10 +107,10 @@ ReactInstance::ReactInstance()
reactNativeHost_.PackageProviders());
reactNativeHost_.InstanceSettings().InstanceLoaded(
[this](IInspectable const & /*sender*/, InstanceLoadedEventArgs const &args) {
[this](winrt::IInspectable const & /*sender*/, winrt::InstanceLoadedEventArgs const &args) {
context_ = args.Context();
#if __has_include(<JSI/JsiApiContext.h>)
#if __has_include("AppRegistry.h") && __has_include(<JSI/JsiApiContext.h>)
if (!onComponentsRegistered_) {
return;
}
@ -86,10 +126,27 @@ ReactInstance::ReactInstance()
#endif // defined(_DEBUG) && !defined(DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION)
}
});
#endif // __has_include(<JSI/JsiApiContext.h>)
#endif // __has_include("AppRegistry.h") && __has_include(<JSI/JsiApiContext.h>)
});
}
#if __has_include(<winrt/Microsoft.UI.Composition.h>)
ReactInstance::ReactInstance(HWND hwnd,
winrt::Microsoft::UI::Composition::Compositor const &compositor)
: ReactInstance()
{
winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId(
reactNativeHost_.InstanceSettings().Properties(), reinterpret_cast<uint64_t>(hwnd));
// By using the MicrosoftCompositionContextHelper here, React Native Windows
// will use Lifted Visuals for its tree.
winrt::Microsoft::ReactNative::Composition::CompositionUIService::SetCompositionContext(
reactNativeHost_.InstanceSettings().Properties(),
winrt::Microsoft::ReactNative::Composition::MicrosoftCompositionContextHelper::
CreateContext(compositor));
}
#endif // __has_include(<winrt/Microsoft.UI.Composition.h>)
bool ReactInstance::LoadJSBundleFrom(JSBundleSource source)
{
source_ = source;
@ -151,7 +208,7 @@ void ReactInstance::BreakOnFirstLine(bool breakOnFirstLine)
std::tuple<winrt::hstring, int> ReactInstance::BundlerAddress() const
{
auto localSettings = ApplicationData::Current().LocalSettings();
auto localSettings = winrt::ApplicationData::Current().LocalSettings();
auto values = localSettings.Values();
auto host = winrt::unbox_value_or<winrt::hstring>(values.Lookup(kBundlerHost), {});
auto port = winrt::unbox_value_or<int>(values.Lookup(kBundlerPort), 0);
@ -160,19 +217,19 @@ std::tuple<winrt::hstring, int> ReactInstance::BundlerAddress() const
void ReactInstance::BundlerAddress(winrt::hstring host, int port)
{
auto localSettings = ApplicationData::Current().LocalSettings();
auto localSettings = winrt::ApplicationData::Current().LocalSettings();
auto values = localSettings.Values();
if (host.empty()) {
values.Remove(kBundlerHost);
} else {
values.Insert(kBundlerHost, PropertyValue::CreateString(host));
values.Insert(kBundlerHost, winrt::PropertyValue::CreateString(host));
}
if (port <= 0) {
values.Remove(kBundlerPort);
} else {
values.Insert(kBundlerPort, PropertyValue::CreateInt32(port));
values.Insert(kBundlerPort, winrt::PropertyValue::CreateInt32(port));
}
Reload();
@ -228,36 +285,10 @@ void ReactInstance::UseWebDebugger(bool useWebDebugger)
Reload();
}
std::optional<winrt::hstring>
ReactTestApp::GetBundleName(std::optional<winrt::hstring> const &bundleRoot)
winrt::IAsyncOperation<bool> ReactTestApp::IsDevServerRunning()
{
constexpr std::wstring_view const bundleExtension = L".bundle";
std::filesystem::path bundlePath{L"Bundle\\"};
if (bundleRoot.has_value()) {
std::wstring_view root = bundleRoot.value();
for (auto &&ext : {L".windows", L".native", L""}) {
bundlePath.replace_filename(root).replace_extension(ext) += bundleExtension;
if (std::filesystem::exists(bundlePath)) {
return winrt::hstring{bundlePath.stem().wstring()};
}
}
} else {
for (auto &&main : JSBundleNames) {
bundlePath.replace_filename(main) += bundleExtension;
if (std::filesystem::exists(bundlePath)) {
return winrt::hstring{main};
}
}
}
return std::nullopt;
}
IAsyncOperation<bool> ReactTestApp::IsDevServerRunning()
{
Uri uri(L"http://localhost:8081/status");
HttpClient httpClient;
winrt::Uri uri(L"http://localhost:8081/status");
winrt::HttpClient httpClient;
try {
auto r = co_await httpClient.GetAsync(uri);
co_return r.IsSuccessStatusCode();

Просмотреть файл

@ -10,6 +10,10 @@
#include <winrt/Microsoft.ReactNative.h>
#include <winrt/Windows.Foundation.h>
#if __has_include(<winrt/Microsoft.UI.Composition.h>)
#include <winrt/Microsoft.UI.Composition.h>
#endif // __has_include(<winrt/Microsoft.UI.Composition.h>)
#include <ReactContext.h>
namespace ReactTestApp
@ -30,6 +34,10 @@ namespace ReactTestApp
ReactInstance();
#if __has_include(<winrt/Microsoft.UI.Composition.h>)
ReactInstance(HWND hwnd, winrt::Microsoft::UI::Composition::Compositor const &);
#endif // __has_include(<winrt/Microsoft.UI.Composition.h>)
auto const &ReactHost() const
{
return reactNativeHost_;
@ -94,7 +102,6 @@ namespace ReactTestApp
OnComponentsRegistered onComponentsRegistered_;
};
std::optional<winrt::hstring> GetBundleName(std::optional<winrt::hstring> const &bundleRoot);
winrt::Windows::Foundation::IAsyncOperation<bool> IsDevServerRunning();
} // namespace ReactTestApp

Просмотреть файл

Просмотреть файл

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ReactAppManifest Condition="'$(ReactAppManifest)'==''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'app.json'))</ReactAppManifest>
<ReactAppManifestHeader Condition="'$(ReactAppManifestHeader)'==''">$(MSBuildProjectDirectory)\..\..\app.json.h</ReactAppManifestHeader>
<ValidateManifestCommand>node -e "require('react-native-test-app/scripts/validate-manifest').validate('file')"</ValidateManifestCommand>
</PropertyGroup>
<Target Name="ValidateManifest" Inputs="$(ReactAppManifest)" Outputs="$(ReactAppManifestHeader)" BeforeTargets="PrepareForBuild">
<Exec Command="$(ValidateManifestCommand)" WorkingDirectory="$(SolutionDir)" />
</Target>
</Project>

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 1.4 KiB

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 7.5 KiB

После

Ширина:  |  Высота:  |  Размер: 7.5 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 2.9 KiB

После

Ширина:  |  Высота:  |  Размер: 2.9 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 1.6 KiB

После

Ширина:  |  Высота:  |  Размер: 1.6 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 1.2 KiB

После

Ширина:  |  Высота:  |  Размер: 1.2 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 1.4 KiB

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 3.1 KiB

После

Ширина:  |  Высота:  |  Размер: 3.1 KiB

Просмотреть файл

@ -6,5 +6,5 @@
namespace winrt::Microsoft::ReactNative
{
void RegisterAutolinkedNativeModulePackages(
Windows::Foundation::Collections::IVector<IReactPackageProvider> const &);
winrt::Windows::Foundation::Collections::IVector<IReactPackageProvider> const &);
}

Просмотреть файл

@ -10,6 +10,7 @@
#include <winrt/Windows.UI.Xaml.Automation.Peers.h>
#include <winrt/Windows.UI.Xaml.Automation.Provider.h>
#include "JSValueWriterHelper.h"
#include "MainPage.g.cpp"
#include "Session.h"
@ -83,38 +84,6 @@ namespace
sender, value, L"Enable Remote JS Debugging", L"Disable Remote JS Debugging");
}
void WritePropertyValue(std::any const &propertyValue, IJSValueWriter const &writer)
{
if (propertyValue.type() == typeid(bool)) {
writer.WriteBoolean(std::any_cast<bool>(propertyValue));
} else if (propertyValue.type() == typeid(std::int64_t)) {
writer.WriteInt64(std::any_cast<std::int64_t>(propertyValue));
} else if (propertyValue.type() == typeid(std::uint64_t)) {
writer.WriteInt64(std::any_cast<std::uint64_t>(propertyValue));
} else if (propertyValue.type() == typeid(double)) {
writer.WriteDouble(std::any_cast<double>(propertyValue));
} else if (propertyValue.type() == typeid(std::nullopt)) {
writer.WriteNull();
} else if (propertyValue.type() == typeid(std::string)) {
writer.WriteString(winrt::to_hstring(std::any_cast<std::string>(propertyValue)));
} else if (propertyValue.type() == typeid(std::vector<std::any>)) {
writer.WriteArrayBegin();
for (auto &&e : std::any_cast<std::vector<std::any>>(propertyValue)) {
WritePropertyValue(e, writer);
}
writer.WriteArrayEnd();
} else if (propertyValue.type() == typeid(std::map<std::string, std::any>)) {
writer.WriteObjectBegin();
for (auto &&e : std::any_cast<std::map<std::string, std::any>>(propertyValue)) {
writer.WritePropertyName(winrt::to_hstring(e.first));
WritePropertyValue(e.second, writer);
}
writer.WriteObjectEnd();
} else {
assert(false);
}
}
void InitializeReactRootView(ReactNativeHost const &reactNativeHost,
ReactRootView reactRootView,
Component const &component)
@ -128,10 +97,9 @@ namespace
[initialProps = component.initialProperties](IJSValueWriter const &writer) {
if (initialProps.has_value()) {
writer.WriteObjectBegin();
for (auto &&property : initialProps.value()) {
auto &value = property.second;
writer.WritePropertyName(winrt::to_hstring(property.first));
WritePropertyValue(value, writer);
for (auto &[key, value] : initialProps.value()) {
writer.WritePropertyName(winrt::to_hstring(key));
ReactApp::JSValueWriterWriteValue(writer, value);
}
writer.WriteObjectEnd();
}

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -16,22 +16,20 @@
<ApplicationType>Windows Store</ApplicationType>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
</PropertyGroup>
<PropertyGroup Label="NuGet">
<ResolveNuGetPackages>false</ResolveNuGetPackages>
<UseExperimentalNuget>false</UseExperimentalNuget>
</PropertyGroup>
<PropertyGroup Label="ReactNativeWindowsProps">
<ProjectRootDir Condition="'$(ProjectRootDir)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'app.json'))</ProjectRootDir>
<ReactNativeWindowsDir Condition="'$(ReactNativeWindowsDir)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\</ReactNativeWindowsDir>
<ReactTestAppDir Condition="'$(ReactTestAppDir)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-test-app\package.json'))\node_modules\react-native-test-app\windows\ReactTestApp</ReactTestAppDir>
<ReactTestAppCommonDir Condition="'$(ReactTestAppCommonDir)' == ''">$(ReactTestAppDir)\..\..\common</ReactTestAppCommonDir>
<ReactTestAppGeneratedDir Condition="'$(ReactTestAppGeneratedDir)' == ''">$(MSBuildProjectDirectory)\..\..</ReactTestAppGeneratedDir>
<ProjectRootDir Condition="'$(ProjectRootDir)'==''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'app.json'))</ProjectRootDir>
<ReactAppWinDir Condition="'$(ReactAppWinDir)'==''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-test-app\package.json'))\node_modules\react-native-test-app\windows</ReactAppWinDir>
<ReactAppCommonDir Condition="'$(ReactAppCommonDir)'==''">$(ReactAppWinDir)\..\common</ReactAppCommonDir>
<ReactAppSharedDir Condition="'$(ReactAppSharedDir)'==''">$(ReactAppWinDir)\Shared</ReactAppSharedDir>
<ReactAppUniversalDir Condition="'$(ReactAppUniversalDir)'==''">$(ReactAppWinDir)\UWP</ReactAppUniversalDir>
<ReactAppGeneratedDir Condition="'$(ReactAppGeneratedDir)'==''">$(MSBuildProjectDirectory)\..\..</ReactAppGeneratedDir>
<ReactNativeWindowsDir Condition="'$(ReactNativeWindowsDir)'==''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\</ReactNativeWindowsDir>
<WinUI2xVersionDisabled />
</PropertyGroup>
<Import Project="$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.WindowsSdk.Default.props" Condition="Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.WindowsSdk.Default.props')" />
<PropertyGroup Label="Fallback Windows SDK Versions">
<WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion Condition=" '$(WindowsTargetPlatformMinVersion)' == '' ">10.0.17763.0</WindowsTargetPlatformMinVersion>
<WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)'==''">10.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion Condition="'$(WindowsTargetPlatformMinVersion)'==''">10.0.17763.0</WindowsTargetPlatformMinVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<ItemGroup Label="ProjectConfigurations">
@ -88,7 +86,7 @@
<Import Project="$(SolutionDir)packages\$(WinUIPackageProps)" Condition="'$(WinUIPackageProps)' != '' And Exists('$(SolutionDir)packages\$(WinUIPackageProps)')" />
</ImportGroup>
<PropertyGroup>
<IncludePath>$(ReactTestAppDir);$(ReactTestAppCommonDir);$(ReactTestAppGeneratedDir);$(IncludePath)</IncludePath>
<IncludePath>$(ReactAppUniversalDir);$(ReactAppSharedDir);$(ReactAppCommonDir);$(ReactAppGeneratedDir);$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
@ -116,25 +114,25 @@
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="$(ReactTestAppDir)\pch.h" />
<ClInclude Include="$(ReactTestAppDir)\App.h">
<DependentUpon>$(ReactTestAppDir)\App.xaml</DependentUpon>
<ClInclude Include="$(ReactAppUniversalDir)\pch.h" />
<ClInclude Include="$(ReactAppUniversalDir)\App.h">
<DependentUpon>$(ReactAppUniversalDir)\App.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="$(ReactTestAppCommonDir)\AppRegistry.h" />
<ClInclude Include="$(ReactTestAppDir)\AutolinkedNativeModules.g.h" />
<ClInclude Include="$(ReactTestAppDir)\MainPage.h">
<DependentUpon>$(ReactTestAppDir)\MainPage.xaml</DependentUpon>
<ClInclude Include="$(ReactAppCommonDir)\AppRegistry.h" />
<ClInclude Include="$(ReactAppUniversalDir)\AutolinkedNativeModules.g.h" />
<ClInclude Include="$(ReactAppSharedDir)\JSValueWriterHelper.h" />
<ClInclude Include="$(ReactAppUniversalDir)\MainPage.h">
<DependentUpon>$(ReactAppUniversalDir)\MainPage.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="$(ReactTestAppDir)\Manifest.h" />
<ClInclude Include="$(ReactTestAppDir)\ReactInstance.h" />
<ClInclude Include="$(ReactTestAppDir)\ReactPackageProvider.h" />
<ClInclude Include="$(ReactTestAppDir)\Session.h" />
<ClInclude Include="$(ReactAppSharedDir)\Manifest.h" />
<ClInclude Include="$(ReactAppSharedDir)\ReactInstance.h" />
<ClInclude Include="$(ReactAppSharedDir)\Session.h" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="$(ReactTestAppDir)\App.xaml">
<ApplicationDefinition Include="$(ReactAppUniversalDir)\App.xaml">
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="$(ReactTestAppDir)\MainPage.xaml">
<Page Include="$(ReactAppUniversalDir)\MainPage.xaml">
<SubType>Designer</SubType>
</Page>
</ItemGroup>
@ -142,7 +140,7 @@
<AppxManifest Include="$(ProjectRootDir)\$(ReactTestAppPackageManifest)" Condition="Exists('$(ProjectRootDir)\$(ReactTestAppPackageManifest)')">
<SubType>Designer</SubType>
</AppxManifest>
<AppxManifest Include="$(ReactTestAppDir)\Package.appxmanifest" Condition="!Exists('$(ProjectRootDir)\$(ReactTestAppPackageManifest)')">
<AppxManifest Include="$(ReactAppUniversalDir)\Package.appxmanifest" Condition="!Exists('$(ProjectRootDir)\$(ReactTestAppPackageManifest)')">
<SubType>Designer</SubType>
</AppxManifest>
</ItemGroup>
@ -156,30 +154,29 @@
<!-- ReactTestApp asset items -->
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(ReactTestAppDir)\pch.cpp">
<ClCompile Include="$(ReactAppUniversalDir)\pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="$(ReactTestAppDir)\App.cpp">
<DependentUpon>$(ReactTestAppDir)\App.xaml</DependentUpon>
<ClCompile Include="$(ReactAppUniversalDir)\App.cpp">
<DependentUpon>$(ReactAppUniversalDir)\App.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="$(ReactTestAppCommonDir)\AppRegistry.cpp">
<ClCompile Include="$(ReactAppCommonDir)\AppRegistry.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="AutolinkedNativeModules.g.cpp" />
<ClCompile Include="$(ReactTestAppDir)\MainPage.cpp">
<DependentUpon>$(ReactTestAppDir)\MainPage.xaml</DependentUpon>
<ClCompile Include="$(ReactAppUniversalDir)\MainPage.cpp">
<DependentUpon>$(ReactAppUniversalDir)\MainPage.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="$(ReactTestAppDir)\Manifest.cpp" />
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
<ClCompile Include="$(ReactTestAppDir)\ReactInstance.cpp" />
<ClCompile Include="$(ReactTestAppDir)\ReactPackageProvider.cpp" />
<ClCompile Include="$(ReactAppSharedDir)\Manifest.cpp" />
<ClCompile Include="$(GeneratedFilesDir)\module.g.cpp" />
<ClCompile Include="$(ReactAppSharedDir)\ReactInstance.cpp" />
</ItemGroup>
<ItemGroup>
<Midl Include="$(ReactTestAppDir)\App.idl">
<DependentUpon>$(ReactTestAppDir)\App.xaml</DependentUpon>
<Midl Include="$(ReactAppUniversalDir)\App.idl">
<DependentUpon>$(ReactAppUniversalDir)\App.xaml</DependentUpon>
</Midl>
<Midl Include="$(ReactTestAppDir)\MainPage.idl">
<DependentUpon>$(ReactTestAppDir)\MainPage.xaml</DependentUpon>
<Midl Include="$(ReactAppUniversalDir)\MainPage.idl">
<DependentUpon>$(ReactAppUniversalDir)\MainPage.xaml</DependentUpon>
</Midl>
</ItemGroup>
<ItemGroup>
@ -189,7 +186,7 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ReactNativeWindowsTargets">
<Import Project="$(ReactTestAppDir)\ValidateManifest.targets" />
<Import Project="$(ReactAppSharedDir)\ValidateManifest.targets" />
<Import Project="$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppApp.targets" Condition="Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppApp.targets')" />
</ImportGroup>
<Target Name="EnsureReactNativeWindowsTargets" BeforeTargets="PrepareForBuild">
@ -200,13 +197,13 @@
<Error Condition="!Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppApp.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Uwp.CppApp.targets'))" />
</Target>
<PropertyGroup Label="ReactNativeWindowsProps">
<ReactNativeWindowsVersion Condition="'$(ReactNativeWindowsVersion)' == ''">$(ReactNativeWindowsNpmVersion)</ReactNativeWindowsVersion>
<ReactNativeWindowsVersion Condition="'$(ReactNativeWindowsVersion)'==''">$(ReactNativeWindowsNpmVersion)</ReactNativeWindowsVersion>
</PropertyGroup>
<ImportGroup Label="ExtensionTargets">
<Import Project="$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.211028.7\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.Windows.CppWinRT.2.0.211028.7\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="$(SolutionDir)packages\Microsoft.ReactNative.$(ReactNativeWindowsVersion)\build\native\Microsoft.ReactNative.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.ReactNative.$(ReactNativeWindowsVersion)\build\native\Microsoft.ReactNative.targets')" />
<Import Project="$(SolutionDir)packages\Microsoft.ReactNative.Cxx.$(ReactNativeWindowsVersion)\build\native\Microsoft.ReactNative.Cxx.targets" Condition="Exists('$(SolutionDir)packages\Microsoft.ReactNative.Cxx.$(ReactNativeWindowsVersion)\build\native\Microsoft.ReactNative.Cxx.targets')" />
<Import Project="$(SolutionDir)packages\ReactNative.Hermes.Windows.$(HermesVersion)\build\native\ReactNative.Hermes.Windows.targets" Condition="Exists('$(SolutionDir)packages\ReactNative.Hermes.Windows.$(HermesVersion)\build\native\ReactNative.Hermes.Windows.targets') AND '$(UseHermes)' == 'true'" />
<Import Project="$(SolutionDir)packages\ReactNative.Hermes.Windows.$(HermesVersion)\build\native\ReactNative.Hermes.Windows.targets" Condition="Exists('$(SolutionDir)packages\ReactNative.Hermes.Windows.$(HermesVersion)\build\native\ReactNative.Hermes.Windows.targets') AND '$(UseHermes)'=='true'" />
<Import Project="$(SolutionDir)packages\$(WinUIPackageName).$(WinUIPackageVersion)\build\native\$(WinUIPackageName).targets" Condition="Exists('$(SolutionDir)packages\$(WinUIPackageName).$(WinUIPackageVersion)\build\native\$(WinUIPackageName).targets')" />
<Import Project="$(SolutionDir)packages\nlohmann.json.3.10.4\build\native\nlohmann.json.targets" Condition="Exists('$(SolutionDir)packages\nlohmann.json.3.10.4\build\native\nlohmann.json.targets')" />
<!-- ReactTestApp additional targets -->

Просмотреть файл

@ -1,36 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ApplicationDefinition Include="$(ReactTestAppDir)\App.xaml" />
<ApplicationDefinition Include="$(ReactAppUniversalDir)\App.xaml" />
</ItemGroup>
<ItemGroup>
<Page Include="$(ReactTestAppDir)\MainPage.xaml" />
<Page Include="$(ReactAppUniversalDir)\MainPage.xaml" />
</ItemGroup>
<ItemGroup>
<Midl Include="$(ReactTestAppDir)\App.idl" />
<Midl Include="$(ReactTestAppDir)\MainPage.idl" />
<Midl Include="$(ReactAppUniversalDir)\App.idl" />
<Midl Include="$(ReactAppUniversalDir)\MainPage.idl" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(ReactTestAppDir)\pch.cpp" />
<ClCompile Include="$(ReactTestAppDir)\App.cpp" />
<ClCompile Include="$(ReactTestAppCommonDir)\AppRegistry.cpp" />
<ClCompile Include="$(ReactAppUniversalDir)\pch.cpp" />
<ClCompile Include="$(ReactAppUniversalDir)\App.cpp" />
<ClCompile Include="$(ReactAppCommonDir)\AppRegistry.cpp" />
<ClCompile Include="$(ProjectDir)\AutolinkedNativeModules.g.cpp" />
<ClCompile Include="$(ReactTestAppDir)\MainPage.cpp" />
<ClCompile Include="$(ReactTestAppDir)\Manifest.cpp" />
<ClCompile Include="$(ReactTestAppDir)\ReactInstance.cpp" />
<ClCompile Include="$(ReactTestAppDir)\ReactPackageProvider.cpp" />
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
<ClCompile Include="$(ReactAppUniversalDir)\MainPage.cpp" />
<ClCompile Include="$(ReactAppSharedDir)\Manifest.cpp" />
<ClCompile Include="$(ReactAppSharedDir)\ReactInstance.cpp" />
<ClCompile Include="$(GeneratedFilesDir)\module.g.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(ReactTestAppDir)\pch.h" />
<ClInclude Include="$(ReactTestAppDir)\App.h" />
<ClInclude Include="$(ReactTestAppCommonDir)\AppRegistry.h" />
<ClInclude Include="$(ReactTestAppDir)\AutolinkedNativeModules.g.h" />
<ClInclude Include="$(ReactTestAppDir)\MainPage.h" />
<ClInclude Include="$(ReactTestAppDir)\Manifest.h" />
<ClInclude Include="$(ReactTestAppDir)\ReactInstance.h" />
<ClInclude Include="$(ReactTestAppDir)\ReactPackageProvider.h" />
<ClInclude Include="$(ReactTestAppDir)\Session.h" />
<ClInclude Include="$(ReactAppUniversalDir)\pch.h" />
<ClInclude Include="$(ReactAppUniversalDir)\App.h" />
<ClInclude Include="$(ReactAppCommonDir)\AppRegistry.h" />
<ClInclude Include="$(ReactAppUniversalDir)\AutolinkedNativeModules.g.h" />
<ClInclude Include="$(ReactAppSharedDir)\JSValueWriterHelper.h" />
<ClInclude Include="$(ReactAppUniversalDir)\MainPage.h" />
<ClInclude Include="$(ReactAppSharedDir)\Manifest.h" />
<ClInclude Include="$(ReactAppSharedDir)\ReactInstance.h" />
<ClInclude Include="$(ReactAppSharedDir)\Session.h" />
</ItemGroup>
<ItemGroup>
<Image Include="Assets\SplashScreen.scale-200.png">
@ -55,7 +54,7 @@
</ItemGroup>
<ItemGroup>
<AppxManifest Include="$(ProjectRootDir)\$(ReactTestAppPackageManifest)" />
<AppxManifest Include="$(ReactTestAppDir)\Package.appxmanifest" />
<AppxManifest Include="$(ReactAppUniversalDir)\Package.appxmanifest" />
</ItemGroup>
<ItemGroup>
<Filter Include="Assets">

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -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<winrt::Microsoft::ReactNative::IReactPackageProvider> const& packageProviders)
{ {{ &autolinkCppPackageProviders }}
}
}

Просмотреть файл

@ -0,0 +1,10 @@
#pragma once
#include <winrt/Microsoft.ReactNative.h>
#include <winrt/Windows.Foundation.Collections.h>
namespace winrt::Microsoft::ReactNative
{
void RegisterAutolinkedNativeModulePackages(
winrt::Windows::Foundation::Collections::IVector<IReactPackageProvider> const &);
}

Двоичные данные
windows/Win32/Images/LockScreenLogo.scale-200.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

Двоичные данные
windows/Win32/Images/SplashScreen.scale-200.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 7.5 KiB

Двоичные данные
windows/Win32/Images/Square150x150Logo.scale-200.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.9 KiB

Двоичные данные
windows/Win32/Images/Square44x44Logo.scale-200.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.6 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.2 KiB

Двоичные данные
windows/Win32/Images/StoreLogo.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

Двоичные данные
windows/Win32/Images/Wide310x150Logo.scale-200.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.1 KiB

197
windows/Win32/Main.cpp Normal file
Просмотреть файл

@ -0,0 +1,197 @@
#include "pch.h"
#include "Main.h"
#include "JSValueWriterHelper.h"
#include "Manifest.h"
#include "ReactInstance.h"
namespace winrt
{
using winrt::Microsoft::ReactNative::CompositionRootView;
using winrt::Microsoft::ReactNative::IJSValueWriter;
using winrt::Microsoft::ReactNative::ReactCoreInjection;
using winrt::Microsoft::ReactNative::ReactViewOptions;
using winrt::Microsoft::UI::Composition::Compositor;
using winrt::Microsoft::UI::Content::ContentSizePolicy;
using winrt::Microsoft::UI::Content::DesktopChildSiteBridge;
using winrt::Microsoft::UI::Dispatching::DispatcherQueueController;
using winrt::Microsoft::UI::Windowing::AppWindow;
using winrt::Microsoft::UI::Windowing::AppWindowChangedEventArgs;
using winrt::Microsoft::UI::Windowing::OverlappedPresenter;
using winrt::Microsoft::UI::Windowing::OverlappedPresenterState;
using winrt::Windows::Foundation::AsyncStatus;
using winrt::Windows::Foundation::Size;
} // namespace winrt
namespace
{
#if _DEBUG
constexpr bool kDebug = true;
#else
constexpr bool kDebug = false;
#endif
constexpr bool kSingleAppMode = static_cast<bool>(ENABLE_SINGLE_APP_MODE);
float ScaleFactor(HWND hwnd) noexcept
{
return GetDpiForWindow(hwnd) / static_cast<float>(USER_DEFAULT_SCREEN_DPI);
}
void UpdateRootViewSizeToAppWindow(winrt::CompositionRootView const &rootView,
winrt::AppWindow const &window)
{
// Do not relayout when minimized
if (window.Presenter().as<winrt::OverlappedPresenter>().State() ==
winrt::OverlappedPresenterState::Minimized) {
return;
}
auto hwnd = winrt::Microsoft::UI::GetWindowFromWindowId(window.Id());
auto scaleFactor = ScaleFactor(hwnd);
winrt::Size size{window.ClientSize().Width / scaleFactor,
window.ClientSize().Height / scaleFactor};
rootView.Arrange(size);
rootView.Size(size);
}
winrt::ReactViewOptions MakeReactViewOptions(ReactTestApp::Component const &component)
{
winrt::ReactViewOptions viewOptions;
viewOptions.ComponentName(winrt::to_hstring(component.appKey));
auto initialProps = component.initialProperties.value_or(std::map<std::string, std::any>{});
initialProps["concurrentRoot"] = true;
viewOptions.InitialProps(
[initialProps = std::move(initialProps)](winrt::IJSValueWriter const &writer) {
writer.WriteObjectBegin();
for (auto &[key, value] : initialProps) {
writer.WritePropertyName(winrt::to_hstring(key));
ReactApp::JSValueWriterWriteValue(writer, value);
}
writer.WriteObjectEnd();
});
return viewOptions;
}
} // namespace
_Use_decl_annotations_ int CALLBACK WinMain(HINSTANCE /* instance */,
HINSTANCE,
PSTR /* commandLine */,
int /* showCmd */)
{
auto result = ::ReactTestApp::GetManifest();
assert(result.has_value() && "Failed to parse app manifest");
auto &[manifest, checksum] = *result;
assert(manifest.components.has_value() && (*manifest.components).size() > 0 &&
"At least one component must be declared");
// 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::DispatcherQueueController::CreateOnCurrentThread();
// Create a Compositor for all Content on this thread.
auto compositor = winrt::Compositor{};
// Create a top-level window.
auto window = winrt::AppWindow::Create();
window.Title(winrt::to_hstring(manifest.displayName));
window.Resize({600, 800});
window.Show();
auto hwnd = winrt::Microsoft::UI::GetWindowFromWindowId(window.Id());
auto scaleFactor = ScaleFactor(hwnd);
auto instance = ReactTestApp::ReactInstance{hwnd, compositor};
if (manifest.bundleRoot.has_value()) {
auto &bundleRoot = *manifest.bundleRoot;
instance.BundleRoot(std::make_optional(winrt::to_hstring(bundleRoot)));
}
// Start the react-native instance, which will create a JavaScript runtime and load the
// applications bundle
if constexpr (kDebug) {
instance.LoadJSBundleFrom(ReactTestApp::JSBundleSource::DevServer);
} else {
instance.LoadJSBundleFrom(ReactTestApp::JSBundleSource::Embedded);
}
// Create a RootView which will present a react-native component
winrt::ReactViewOptions viewOptions;
if constexpr (kSingleAppMode) {
assert(manifest.singleApp.has_value() ||
!"`ENABLE_SINGLE_APP_MODE` shouldn't have been true");
for (auto &component : *manifest.components) {
if (component.slug == *manifest.singleApp) {
viewOptions = MakeReactViewOptions(component);
break;
}
}
} else {
// TODO: Implement session restoration
auto &component = (*manifest.components)[0];
viewOptions = MakeReactViewOptions(component);
}
auto rootView = winrt::CompositionRootView{compositor};
rootView.ReactViewHost(
winrt::ReactCoreInjection::MakeViewHost(instance.ReactHost(), viewOptions));
// Update the size of the RootView when the AppWindow changes size
window.Changed(
[wkRootView = winrt::make_weak(rootView)](winrt::AppWindow const &window,
winrt::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 = instance.ReactHost()](winrt::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::AsyncStatus asyncStatus) {
assert(asyncStatus == winrt::AsyncStatus::Completed);
host.InstanceSettings().UIDispatcher().Post([]() { PostQuitMessage(0); });
});
});
// DesktopChildSiteBridge create a ContentSite that can host the RootView ContentIsland
auto bridge = winrt::DesktopChildSiteBridge::Create(compositor, window.Id());
bridge.Connect(rootView.Island());
bridge.ResizePolicy(winrt::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;
}

3
windows/Win32/Main.h Normal file
Просмотреть файл

@ -0,0 +1,3 @@
#pragma once
#include "resource.h"

Двоичные данные
windows/Win32/Main.ico Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 45 KiB

71
windows/Win32/Main.rc Normal file
Просмотреть файл

@ -0,0 +1,71 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICON1 ICON "Main.small.ico"
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

Двоичные данные
windows/Win32/Main.small.ico Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 45 KiB

Просмотреть файл

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap"
>
<Identity
Name="40411fc5-8e92-4d46-b68d-b62df44b1366"
Publisher="CN=ReactApp"
Version="1.0.0.0"
/>
<Properties>
<DisplayName>ReactApp</DisplayName>
<PublisherDisplayName>ReactApp</PublisherDisplayName>
<Logo>Images\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily
Name="Windows.Universal"
MinVersion="10.0.0.0"
MaxVersionTested="10.0.0.0"
/>
<TargetDeviceFamily
Name="Windows.Desktop"
MinVersion="10.0.17763.0"
MaxVersionTested="10.0.17763.0"
/>
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application
Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="$targetentrypoint$"
>
<uap:VisualElements
DisplayName="ReactApp"
Description="React Native app"
BackgroundColor="transparent"
Square150x150Logo="Images\Square150x150Logo.png"
Square44x44Logo="Images\Square44x44Logo.png"
>
<uap:DefaultTile Wide310x150Logo="Images\Wide310x150Logo.png" />
<uap:SplashScreen Image="Images\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>

Просмотреть файл

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)\ExperimentalFeatures.props" Condition="Exists('$(SolutionDir)\ExperimentalFeatures.props')" />
<PropertyGroup>
<ProjectGuid>{b44cead7-fbff-4a17-95eb-ff5434bbd79d}</ProjectGuid>
<DefaultLanguage>en-US</DefaultLanguage>
<EntryPointProjectUniqueName>ReactApp.vcxproj</EntryPointProjectUniqueName>
<DebuggerType>NativeOnly</DebuggerType>
<BackgroundTaskDebugEngines>NativeOnly</BackgroundTaskDebugEngines>
</PropertyGroup>
<PropertyGroup Label="ReactNativeWindowsProps">
<ReactNativeWindowsDir Condition="'$(ReactNativeWindowsDir)'==''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\</ReactNativeWindowsDir>
</PropertyGroup>
<Import Project="$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.WindowsSdk.Default.props" />
<PropertyGroup>
<WapProjPath Condition="'$(WapProjPath)'==''">$(MSBuildExtensionsPath)\Microsoft\DesktopBridge\</WapProjPath>
</PropertyGroup>
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.props" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x86">
<Configuration>Debug</Configuration>
<Platform>x86</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x86">
<Configuration>Release</Configuration>
<Platform>x86</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ImportGroup Label="ReactNativeWindowsPropertySheets">
<Import Project="$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Composition.Package.props" Condition="Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Composition.Package.props')" />
</ImportGroup>
<ItemGroup>
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
</ItemGroup>
<ItemGroup>
<Content Include="Images\SplashScreen.scale-200.png" />
<Content Include="Images\LockScreenLogo.scale-200.png" />
<Content Include="Images\Square150x150Logo.scale-200.png" />
<Content Include="Images\Square44x44Logo.scale-200.png" />
<Content Include="Images\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Images\StoreLogo.png" />
<Content Include="Images\Wide310x150Logo.scale-200.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="ReactApp.vcxproj">
<SkipGetTargetFrameworkProperties>True</SkipGetTargetFrameworkProperties>
</ProjectReference>
</ItemGroup>
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.targets" />
<ImportGroup Label="ReactNativeWindowsTargets">
<Import Project="$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Composition.Package.targets" Condition="Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Composition.Package.targets')" />
</ImportGroup>
<Target Name="EnsureReactNativeWindowsTargets" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references targets in your node_modules\react-native-windows folder that are missing. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Composition.Package.props')" Text="$([System.String]::Format('$(ErrorText)', '$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Composition.Package.props'))" />
<Error Condition="!Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Composition.Package.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Composition.Package.targets'))" />
</Target>
</Project>

Просмотреть файл

@ -0,0 +1,155 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)\ExperimentalFeatures.props" Condition="Exists('$(SolutionDir)\ExperimentalFeatures.props')" />
<PropertyGroup Label="Globals">
<CppWinRTOptimized>true</CppWinRTOptimized>
<MinimalCoreWin>true</MinimalCoreWin>
<ProjectGuid>{B44CEAD7-FBFF-4A17-95EA-FF5434BBD79D}</ProjectGuid>
<ProjectName>ReactApp</ProjectName>
<Keyword>Win32Proj</Keyword>
<RootNamespace>ReactApp</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<DefaultLanguage>en-US</DefaultLanguage>
<MinimumVisualStudioVersion>17.0</MinimumVisualStudioVersion>
<AppxPackage>false</AppxPackage>
</PropertyGroup>
<PropertyGroup Label="ReactNativeWindowsProps">
<ReactAppWinDir Condition="'$(ReactAppWinDir)'==''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-test-app\package.json'))\node_modules\react-native-test-app\windows</ReactAppWinDir>
<ReactAppSharedDir Condition="'$(ReactAppSharedDir)'==''">$(ReactAppWinDir)\Shared</ReactAppSharedDir>
<ReactAppWin32Dir Condition="'$(ReactAppWin32Dir)'==''">$(ReactAppWinDir)\Win32</ReactAppWin32Dir>
<ReactAppGeneratedDir Condition="'$(ReactAppGeneratedDir)'==''">$(MSBuildProjectDirectory)\..\..</ReactAppGeneratedDir>
<ReactNativeWindowsDir Condition="'$(ReactNativeWindowsDir)'==''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\</ReactNativeWindowsDir>
</PropertyGroup>
<Import Project="$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.WindowsSdk.Default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="ReactNativeWindowsPropertySheets">
<Import Project="$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Composition.CppApp.props" />
</ImportGroup>
<ItemDefinitionGroup>
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<SDLCheck>true</SDLCheck>
<AdditionalOptions>%(AdditionalOptions) /bigobj</AdditionalOptions>
<DisableSpecificWarnings></DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalDependencies>shell32.lib;user32.lib;windowsapp.lib;%(AdditionalDependenices)</AdditionalDependencies>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<ClCompile>
<LanguageStandard>stdcpp20</LanguageStandard>
<PreprocessorDefinitions>ENABLE_SINGLE_APP_MODE=0;REACT_NATIVE_VERSION=1000000000;USE_FABRIC=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ReactAppWin32Dir);$(ReactAppSharedDir);$(ReactAppGeneratedDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<PropertyGroup Label="UserMacros" />
<ItemGroup>
<ClInclude Include="$(ReactAppSharedDir)\JSValueWriterHelper.h" />
<ClInclude Include="$(ReactAppWin32Dir)\Main.h" />
<ClInclude Include="$(ReactAppSharedDir)\Manifest.h" />
<ClInclude Include="$(ReactAppSharedDir)\ReactInstance.h" />
<ClInclude Include="$(ReactAppSharedDir)\Session.h" />
<ClInclude Include="$(ReactAppWin32Dir)\AutolinkedNativeModules.g.h" />
<ClInclude Include="$(ReactAppWin32Dir)\pch.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="$(ReactAppWin32Dir)\targetver.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(ReactAppWin32Dir)\Main.cpp" />
<ClCompile Include="$(ReactAppSharedDir)\Manifest.cpp" />
<ClCompile Include="$(ReactAppSharedDir)\ReactInstance.cpp" />
<ClCompile Include="AutolinkedNativeModules.g.cpp" />
<ClCompile Include="$(ReactAppWin32Dir)\pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Main.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="Main.ico" />
<Image Include="Main.small.ico" />
<!-- ReactTestApp asset items -->
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ReactNativeWindowsTargets">
<Import Project="$(ReactAppSharedDir)\ValidateManifest.targets" />
<Import Project="$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Composition.CppApp.targets" Condition="Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Composition.CppApp.targets')" />
<!-- ReactTestApp additional targets -->
</ImportGroup>
<ItemGroup>
<PackageReference Include="nlohmann.json" Version="3.11.2" />
</ItemGroup>
<Target Name="EnsureReactNativeWindowsTargets" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references targets in your node_modules\react-native-windows folder. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Composition.CppApp.props')" Text="$([System.String]::Format('$(ErrorText)', '$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Composition.CppApp.props'))" />
<Error Condition="!Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Composition.CppApp.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.Composition.CppApp.targets'))" />
</Target>
</Project>

Просмотреть файл

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(ReactAppSharedDir)\JSValueWriterHelper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(ReactAppWin32Dir)\Main.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(ReactAppSharedDir)\Manifest.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(ReactAppSharedDir)\ReactInstance.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(ReactAppSharedDir)\Session.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(ReactAppWin32Dir)\AutolinkedNativeModules.g.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(ReactAppWin32Dir)\pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(ReactAppWin32Dir)\targetver.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(ReactAppWin32Dir)\Main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(ReactAppSharedDir)\Manifest.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(ReactAppSharedDir)\ReactInstance.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="AutolinkedNativeModules.g.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(ReactAppWin32Dir)\pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Main.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Image Include="Main.ico">
<Filter>Resource Files</Filter>
</Image>
<Image Include="Main.small.ico">
<Filter>Resource Files</Filter>
</Image>
</ItemGroup>
</Project>

1
windows/Win32/pch.cpp Normal file
Просмотреть файл

@ -0,0 +1 @@
#include "pch.h"

31
windows/Win32/pch.h Normal file
Просмотреть файл

@ -0,0 +1,31 @@
#pragma once
#include "targetver.h"
#define NOMINMAX 1
#define WIN32_LEAN_AND_MEAN 1
#define WINRT_LEAN_AND_MEAN 1
// Windows Header Files
#include <windows.h>
#undef GetCurrentTime
#include <pathcch.h>
#include <unknwn.h>
// WinRT Header Files
#include <CppWinRTIncludes.h>
#include <winrt/Microsoft.ReactNative.Composition.h>
#include <winrt/Microsoft.ReactNative.h>
#include <winrt/Microsoft.UI.Composition.h>
#include <winrt/Microsoft.UI.Content.h>
#include <winrt/Microsoft.UI.Dispatching.h>
#include <winrt/Microsoft.UI.Windowing.h>
#include <winrt/Microsoft.UI.interop.h>
#include <winrt/base.h>
// C RunTime Header Files
#include <malloc.h>
#include <memory.h>
#include <stdlib.h>
#include <tchar.h>

14
windows/Win32/resource.h Normal file
Просмотреть файл

@ -0,0 +1,14 @@
#define IDI_ICON1 1008
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 130
#define _APS_NEXT_RESOURCE_VALUE 129
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 110
#endif // APSTUDIO_READONLY_SYMBOLS
#endif // _APS_NO_MFC

Просмотреть файл

@ -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 <SDKDDKVer.h>

442
windows/project.mjs Normal file
Просмотреть файл

@ -0,0 +1,442 @@
// @ts-check
import { XMLParser } from "fast-xml-parser";
import * as nodefs from "node:fs";
import * as path from "node:path";
import { v5 as uuidv5 } from "uuid";
import {
findNearest,
getPackageVersion,
readJSONFile,
readTextFile,
requireTransitive,
toVersionNumber,
v,
} from "../scripts/helpers.js";
import { fileURLToPath } from "node:url";
/**
* @typedef {import("../scripts/types").AppManifest} AppManifest
* @typedef {import("../scripts/types").AppxBundle} AppxBundle
* @typedef {import("../scripts/types").AssetItems} AssetItems;
* @typedef {import("../scripts/types").Assets} Assets;
* @typedef {import("../scripts/types").MSBuildProjectOptions} MSBuildProjectOptions;
* @typedef {import("../scripts/types").ProjectInfo} ProjectInfo;
*/
const uniqueFilterIdentifier = "e48dc53e-40b1-40cb-970a-f89935452892";
/**
* Returns whether specified object is Error-like.
* @param {unknown} e
* @returns {e is Error}
*/
function isErrorLike(e) {
return typeof e === "object" && e !== null && "name" in e && "message" in e;
}
/**
* Normalizes specified path.
* @param {string} p
* @returns {string}
*/
function normalizePath(p) {
return p.replace(/[/\\]+/g, "\\");
}
/**
* Returns path to the specified asset relative to the project path.
* @param {string} projectPath
* @param {string} assetPath
* @returns {string}
*/
function projectRelativePath(projectPath, assetPath) {
return normalizePath(
path.isAbsolute(assetPath)
? path.relative(projectPath, assetPath)
: assetPath
);
}
/**
* @param {Required<AppManifest>["windows"]} certificate
* @param {string} projectPath
* @returns {string}
*/
function generateCertificateItems(
{ certificateKeyFile, certificateThumbprint, certificatePassword },
projectPath
) {
const items = [];
if (typeof certificateKeyFile === "string") {
items.push(
"<AppxPackageSigningEnabled>true</AppxPackageSigningEnabled>",
`<PackageCertificateKeyFile>$(ProjectRootDir)\\${projectRelativePath(
projectPath,
certificateKeyFile
)}</PackageCertificateKeyFile>`
);
}
if (typeof certificateThumbprint === "string") {
items.push(
`<PackageCertificateThumbprint>${certificateThumbprint}</PackageCertificateThumbprint>`
);
}
if (typeof certificatePassword === "string") {
items.push(
`<PackageCertificatePassword>${certificatePassword}</PackageCertificatePassword>`
);
}
return items.join("\n ");
}
/**
* @param {string[]} resources
* @param {string} projectPath
* @param {AssetItems} assets
* @param {string} currentFilter
* @param {string} source
* @returns {AssetItems}
*/
function generateContentItems(
resources,
projectPath,
assets = { assetFilters: [], assetItemFilters: [], assetItems: [] },
currentFilter = "Assets",
source = "",
fs = nodefs
) {
const { assetFilters, assetItemFilters, assetItems } = assets;
for (const resource of resources) {
const resourcePath = path.isAbsolute(resource)
? path.relative(projectPath, resource)
: resource;
if (!fs.existsSync(resourcePath)) {
console.warn(`warning: resource not found: ${resource}`);
continue;
}
if (fs.statSync(resourcePath).isDirectory()) {
const filter =
"Assets\\" +
normalizePath(
source ? path.relative(source, resource) : path.basename(resource)
);
const id = uuidv5(filter, uniqueFilterIdentifier);
assetFilters.push(
`<Filter Include="${filter}">`,
` <UniqueIdentifier>{${id}}</UniqueIdentifier>`,
`</Filter>`
);
const files = fs
.readdirSync(resourcePath)
.map((file) => path.join(resource, file));
generateContentItems(
files,
projectPath,
assets,
filter,
source || path.dirname(resource),
fs
);
} else {
const assetPath = normalizePath(path.relative(projectPath, resourcePath));
/**
* When a resources folder is included in the manifest, the directory
* structure within the folder must be maintained. For example, given
* `dist/assets`, we must output:
*
* `<DestinationFolders>$(OutDir)\\Bundle\\assets\\...</DestinationFolders>`
* `<DestinationFolders>$(OutDir)\\Bundle\\assets\\node_modules\\...</DestinationFolders>`
* ...
*
* Resource paths are always prefixed with `$(OutDir)\\Bundle`.
*/
const destination =
source &&
`\\${normalizePath(path.relative(source, path.dirname(resource)))}`;
assetItems.push(
`<CopyFileToFolders Include="$(ProjectRootDir)\\${assetPath}">`,
` <DestinationFolders>$(OutDir)\\Bundle${destination}</DestinationFolders>`,
"</CopyFileToFolders>"
);
assetItemFilters.push(
`<CopyFileToFolders Include="$(ProjectRootDir)\\${assetPath}">`,
` <Filter>${currentFilter}</Filter>`,
"</CopyFileToFolders>"
);
}
}
return assets;
}
/**
* Finds NuGet dependencies.
*
* Visual Studio (?) currently does not download transitive dependencies. This
* is a workaround until `react-native-windows` autolinking adds support.
*
* @see {@link https://github.com/microsoft/react-native-windows/issues/9578}
* @param {string} rnWindowsPath
* @returns {[string, string][]}
*/
function getNuGetDependencies(rnWindowsPath, fs = nodefs) {
const pkgJson = findNearest("package.json", undefined, fs);
if (!pkgJson) {
return [];
}
/** @type {import("@react-native-community/cli")} */
const { loadConfig } = requireTransitive(
["@react-native-community/cli"],
rnWindowsPath
);
const dependencies = Object.values(loadConfig().dependencies);
const xml = new XMLParser({
ignoreAttributes: false,
transformTagName: (tag) => tag.toLowerCase(),
});
const lowerCase = (/** @type{Record<string, string>} */ refs) => {
for (const key of Object.keys(refs)) {
refs[key.toLowerCase()] = refs[key];
}
return refs;
};
/** @type {Record<string, [string, string]>} */
const packageRefs = {};
for (const { root, platforms } of dependencies) {
/** @type {{ projects?: Record<string, string>[]; sourceDir?: string; }?} */
const windows = platforms?.["windows"];
if (!windows || !Array.isArray(windows.projects)) {
continue;
}
const projects = windows.projects.map(({ projectFile }) =>
path.join(root, windows.sourceDir || ".", projectFile)
);
if (!Array.isArray(projects)) {
continue;
}
// Look for `PackageReference` entries:
//
// <Project>
// <ImportGroup>
// <PackageReference ... />
// <PackageReference ... />
// </ImportGroup>
// </Project>
//
for (const vcxproj of projects) {
const proj = xml.parse(readTextFile(vcxproj, fs));
const itemGroup = proj.project?.itemgroup;
if (!itemGroup) {
continue;
}
const itemGroups = Array.isArray(itemGroup) ? itemGroup : [itemGroup];
for (const group of itemGroups) {
const pkgRef = group["packagereference"];
if (!pkgRef) {
continue;
}
const refs = Array.isArray(pkgRef) ? pkgRef : [pkgRef];
for (const ref of refs) {
// Attributes are not case-sensitive
lowerCase(ref);
const id = ref["@_include"];
const version = ref["@_version"];
if (!id || !version) {
continue;
}
// Package ids are not case-sensitive
packageRefs[id.toLowerCase()] = [id, version];
}
}
}
}
// Remove dependencies managed by us
const config = fileURLToPath(new URL("UWP/packages.config", import.meta.url));
const matches = readTextFile(config, fs).matchAll(/package id="(.+?)"/g);
for (const m of matches) {
const id = m[1].toLowerCase();
delete packageRefs[id];
}
return Object.values(packageRefs);
}
/**
* Maps NuGet dependencies to `<Import>` elements.
* @param {[string, string][]} refs
* @returns {string}
*/
export function importTargets(refs) {
return refs
.map(
([id, version]) =>
`<Import Project="$(SolutionDir)packages\\${id}.${version}\\build\\native\\${id}.targets" Condition="Exists('$(SolutionDir)packages\\${id}.${version}\\build\\native\\${id}.targets')" />`
)
.join("\n ");
}
/**
* Returns a NuGet package entry for specified package id and version.
* @param {string} id NuGet package id
* @param {string} version NuGet package version
* @returns {string}
*/
export function nugetPackage(id, version) {
return `<package id="${id}" version="${version}" targetFramework="native"/>`;
}
/**
* @param {string[] | { windows?: string[] } | undefined} resources
* @param {string} projectPath
* @returns {Assets}
*/
export function parseResources(resources, projectPath, fs = nodefs) {
if (!Array.isArray(resources)) {
if (resources && resources.windows) {
return parseResources(resources.windows, projectPath, fs);
}
return { assetItems: "", assetItemFilters: "", assetFilters: "" };
}
const { assetItems, assetItemFilters, assetFilters } = generateContentItems(
resources,
projectPath,
/* assets */ undefined,
/* currentFilter */ undefined,
/* source */ undefined,
fs
);
return {
assetItems: assetItems.join("\n "),
assetItemFilters: assetItemFilters.join("\n "),
assetFilters: assetFilters.join("\n "),
};
}
/**
* Reads manifest file and and resolves paths to bundle resources.
* @param {string | null} manifestFilePath Path to the closest manifest file.
* @returns {AppxBundle} Application name, and paths to directories and files to include.
*/
export function getBundleResources(manifestFilePath, fs = nodefs) {
// Default value if manifest or 'name' field don't exist.
const defaultName = "ReactTestApp";
// Default `Package.appxmanifest` path. The project will automatically use our
// fallback if there is no file at this path.
const defaultAppxManifest = "windows/Package.appxmanifest";
if (manifestFilePath) {
try {
/** @type {AppManifest} */
const manifest = readJSONFile(manifestFilePath, fs);
const { name, singleApp, resources, windows } = manifest;
const projectPath = path.dirname(manifestFilePath);
return {
appName: name || defaultName,
singleApp,
appxManifest: projectRelativePath(
projectPath,
(windows && windows.appxManifest) || defaultAppxManifest
),
packageCertificate: generateCertificateItems(
windows || {},
projectPath
),
...parseResources(resources, projectPath, fs),
};
} catch (e) {
if (isErrorLike(e)) {
console.warn(`Could not parse 'app.json':\n${e.message}`);
} else {
throw e;
}
}
} else {
console.warn("Could not find 'app.json' file.");
}
return {
appName: defaultName,
appxManifest: defaultAppxManifest,
assetItems: "",
assetItemFilters: "",
assetFilters: "",
packageCertificate: "",
};
}
/**
* Returns the version of Hermes that should be installed.
* @param {string} rnwPath Path to `react-native-windows`.
* @returns {string | null}
*/
export function getHermesVersion(rnwPath, fs = nodefs) {
const jsEnginePropsPath = path.join(
rnwPath,
"PropertySheets",
"JSEngine.props"
);
const props = readTextFile(jsEnginePropsPath, fs);
const m = props.match(/<HermesVersion.*?>(.+?)<\/HermesVersion>/);
return m && m[1];
}
/**
* @param {MSBuildProjectOptions} options
* @param {string} rnWindowsPath
* @param {string} destPath
* @returns {ProjectInfo}
*/
export function projectInfo(
{ useFabric, useHermes, useNuGet },
rnWindowsPath,
destPath,
fs = nodefs
) {
const version = getPackageVersion("react-native-windows", rnWindowsPath, fs);
const versionNumber = toVersionNumber(version);
const newArch =
Boolean(useFabric) && (versionNumber === 0 || versionNumber >= v(0, 74, 0));
if (useFabric && !newArch) {
console.warn(
"warning: New Architecture requires `react-native-windows` 0.74+"
);
}
return {
version,
versionNumber,
bundle: getBundleResources(findNearest("app.json", destPath, fs), fs),
hermesVersion:
(newArch || (useHermes ?? versionNumber >= v(0, 73, 0))) &&
getHermesVersion(rnWindowsPath, fs),
nugetDependencies: getNuGetDependencies(rnWindowsPath),
useExperimentalNuGet: !newArch && useNuGet,
useFabric: newArch,
usePackageReferences: versionNumber === 0 || versionNumber >= v(0, 68, 0),
xamlVersion:
versionNumber === 0 || versionNumber >= v(0, 73, 0)
? "2.8.0"
: versionNumber >= v(0, 67, 0)
? "2.7.0"
: "2.6.0",
};
}

Просмотреть файл

@ -1,66 +1,34 @@
#!/usr/bin/env node
// @ts-check
import { XMLParser } from "fast-xml-parser";
import { spawn } from "node:child_process";
import * as nodefs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { fileURLToPath } from "node:url";
import { v5 as uuidv5 } from "uuid";
import {
findNearest,
getPackageVersion,
isMain,
readJSONFile,
readTextFile,
requireTransitive,
toVersionNumber,
v,
writeTextFile,
} from "../scripts/helpers.js";
import { parseArgs } from "../scripts/parseargs.mjs";
import { validate } from "../scripts/validate-manifest.js";
import { projectInfo } from "./project.mjs";
import { configureForUWP } from "./uwp.mjs";
import { configureForWin32 } from "./win32.mjs";
/**
* @typedef {import("../scripts/types").AppManifest} AppManifest
* @typedef {import("../scripts/types").AssetItems} AssetItems;
* @typedef {import("../scripts/types").Assets} Assets;
* @typedef {import("../scripts/types").MSBuildProjectOptions} MSBuildProjectOptions;
*/
const templateView = {
name: "ReactTestApp",
projectGuidUpper: "{B44CEAD7-FBFF-4A17-95EA-FF5434BBD79D}",
packageGuidUpper: "{B44CEAD7-FBFF-4A17-95EB-FF5434BBD79D}", // .wapproj
projectGuidUpper: "{B44CEAD7-FBFF-4A17-95EA-FF5434BBD79D}", // .vcxproj
useExperimentalNuget: false,
};
const uniqueFilterIdentifier = "e48dc53e-40b1-40cb-970a-f89935452892";
/** @type {{ recursive: true, mode: 0o755 }} */
const mkdirRecursiveOptions = { recursive: true, mode: 0o755 };
/** @type {{ encoding: "utf-8", mode: 0o644 }} */
const textFileWriteOptions = { encoding: "utf-8", mode: 0o644 };
/**
* Copies the specified directory.
* @param {string} src
* @param {string} dest
*/
export function copy(src, dest, fs = nodefs) {
fs.mkdir(dest, mkdirRecursiveOptions, (err) => {
rethrow(err);
fs.readdir(src, { withFileTypes: true }, (err, files) => {
rethrow(err);
files.forEach((file) => {
const source = path.join(src, file.name);
const target = path.join(dest, file.name);
file.isDirectory()
? copy(source, target, fs)
: fs.copyFile(source, target, rethrow);
});
});
});
}
/**
* Finds all Visual Studio projects in specified directory.
* @param {string} projectDir
@ -96,312 +64,6 @@ export function findUserProjects(projectDir, projects = [], fs = nodefs) {
}, projects);
}
/**
* Finds NuGet dependencies.
*
* Visual Studio (?) currently does not download transitive dependencies. This
* is a workaround until `react-native-windows` autolinking adds support.
*
* @see {@link https://github.com/microsoft/react-native-windows/issues/9578}
* @param {string} rnWindowsPath
* @returns {[string, string][]}
*/
function getNuGetDependencies(rnWindowsPath, fs = nodefs) {
const pkgJson = findNearest("package.json");
if (!pkgJson) {
return [];
}
/** @type {import("@react-native-community/cli")} */
const { loadConfig } = requireTransitive(
["@react-native-community/cli"],
rnWindowsPath
);
const dependencies = Object.values(loadConfig().dependencies);
const xml = new XMLParser({
ignoreAttributes: false,
transformTagName: (tag) => tag.toLowerCase(),
});
const lowerCase = (/** @type{Record<string, string>} */ refs) => {
for (const key of Object.keys(refs)) {
refs[key.toLowerCase()] = refs[key];
}
return refs;
};
/** @type {Record<string, [string, string]>} */
const packageRefs = {};
for (const { root, platforms } of dependencies) {
/** @type {{ projects?: Record<string, string>[]; sourceDir?: string; }?} */
const windows = platforms?.["windows"];
if (!windows || !Array.isArray(windows.projects)) {
continue;
}
const projects = windows.projects.map(({ projectFile }) =>
path.join(root, windows.sourceDir || ".", projectFile)
);
if (!Array.isArray(projects)) {
continue;
}
// Look for `PackageReference` entries:
//
// <Project>
// <ImportGroup>
// <PackageReference ... />
// <PackageReference ... />
// </ImportGroup>
// </Project>
//
for (const vcxproj of projects) {
const proj = xml.parse(readTextFile(vcxproj, fs));
const itemGroup = proj.project?.itemgroup;
if (!itemGroup) {
continue;
}
const itemGroups = Array.isArray(itemGroup) ? itemGroup : [itemGroup];
for (const group of itemGroups) {
const pkgRef = group["packagereference"];
if (!pkgRef) {
continue;
}
const refs = Array.isArray(pkgRef) ? pkgRef : [pkgRef];
for (const ref of refs) {
// Attributes are not case-sensitive
lowerCase(ref);
const id = ref["@_include"];
const version = ref["@_version"];
if (!id || !version) {
continue;
}
// Package ids are not case-sensitive
packageRefs[id.toLowerCase()] = [id, version];
}
}
}
}
// Remove dependencies managed by us
const config = fileURLToPath(
new URL("ReactTestApp/packages.config", import.meta.url)
);
const matches = readTextFile(config, fs).matchAll(/package id="(.+?)"/g);
for (const m of matches) {
const id = m[1].toLowerCase();
delete packageRefs[id];
}
return Object.values(packageRefs);
}
/**
* Maps NuGet dependencies to `<Import>` elements.
* @param {[string, string][]} refs
* @returns {string}
*/
function importTargets(refs) {
return refs
.map(
([id, version]) =>
`<Import Project="$(SolutionDir)packages\\${id}.${version}\\build\\native\\${id}.targets" Condition="Exists('$(SolutionDir)packages\\${id}.${version}\\build\\native\\${id}.targets')" />`
)
.join("\n ");
}
/**
* Returns whether specified object is Error-like.
* @param {unknown} e
* @returns {e is Error}
*/
function isErrorLike(e) {
return typeof e === "object" && e !== null && "name" in e && "message" in e;
}
/**
* Normalizes specified path.
* @param {string} p
* @returns {string}
*/
function normalizePath(p) {
return p.replace(/[/\\]+/g, "\\");
}
/**
* Returns a NuGet package entry for specified package id and version.
* @param {string} id NuGet package id
* @param {string} version NuGet package version
* @returns {string}
*/
export function nuGetPackage(id, version) {
return `<package id="${id}" version="${version}" targetFramework="native"/>`;
}
/**
* @param {Required<AppManifest>["windows"]} certificate
* @param {string} projectPath
* @returns {string}
*/
function generateCertificateItems(
{ certificateKeyFile, certificateThumbprint, certificatePassword },
projectPath
) {
const items = [];
if (typeof certificateKeyFile === "string") {
items.push(
"<AppxPackageSigningEnabled>true</AppxPackageSigningEnabled>",
`<PackageCertificateKeyFile>$(ProjectRootDir)\\${projectRelativePath(
projectPath,
certificateKeyFile
)}</PackageCertificateKeyFile>`
);
}
if (typeof certificateThumbprint === "string") {
items.push(
`<PackageCertificateThumbprint>${certificateThumbprint}</PackageCertificateThumbprint>`
);
}
if (typeof certificatePassword === "string") {
items.push(
`<PackageCertificatePassword>${certificatePassword}</PackageCertificatePassword>`
);
}
return items.join("\n ");
}
/**
* @param {string[]} resources
* @param {string} projectPath
* @param {AssetItems} assets
* @param {string} currentFilter
* @param {string} source
* @returns {AssetItems}
*/
function generateContentItems(
resources,
projectPath,
assets = { assetFilters: [], assetItemFilters: [], assetItems: [] },
currentFilter = "Assets",
source = "",
fs = nodefs
) {
const { assetFilters, assetItemFilters, assetItems } = assets;
for (const resource of resources) {
const resourcePath = path.isAbsolute(resource)
? path.relative(projectPath, resource)
: resource;
if (!fs.existsSync(resourcePath)) {
console.warn(`warning: resource not found: ${resource}`);
continue;
}
if (fs.statSync(resourcePath).isDirectory()) {
const filter =
"Assets\\" +
normalizePath(
source ? path.relative(source, resource) : path.basename(resource)
);
const id = uuidv5(filter, uniqueFilterIdentifier);
assetFilters.push(
`<Filter Include="${filter}">`,
` <UniqueIdentifier>{${id}}</UniqueIdentifier>`,
`</Filter>`
);
const files = fs
.readdirSync(resourcePath)
.map((file) => path.join(resource, file));
generateContentItems(
files,
projectPath,
assets,
filter,
source || path.dirname(resource),
fs
);
} else {
const assetPath = normalizePath(path.relative(projectPath, resourcePath));
/**
* When a resources folder is included in the manifest, the directory
* structure within the folder must be maintained. For example, given
* `dist/assets`, we must output:
*
* `<DestinationFolders>$(OutDir)\\Bundle\\assets\\...</DestinationFolders>`
* `<DestinationFolders>$(OutDir)\\Bundle\\assets\\node_modules\\...</DestinationFolders>`
* ...
*
* Resource paths are always prefixed with `$(OutDir)\\Bundle`.
*/
const destination =
source &&
`\\${normalizePath(path.relative(source, path.dirname(resource)))}`;
assetItems.push(
`<CopyFileToFolders Include="$(ProjectRootDir)\\${assetPath}">`,
` <DestinationFolders>$(OutDir)\\Bundle${destination}</DestinationFolders>`,
"</CopyFileToFolders>"
);
assetItemFilters.push(
`<CopyFileToFolders Include="$(ProjectRootDir)\\${assetPath}">`,
` <Filter>${currentFilter}</Filter>`,
"</CopyFileToFolders>"
);
}
}
return assets;
}
/**
* @param {string[] | { windows?: string[] } | undefined} resources
* @param {string} projectPath
* @returns {Assets}
*/
export function parseResources(resources, projectPath, fs = nodefs) {
if (!Array.isArray(resources)) {
if (resources && resources.windows) {
return parseResources(resources.windows, projectPath, fs);
}
return { assetItems: "", assetItemFilters: "", assetFilters: "" };
}
const { assetItems, assetItemFilters, assetFilters } = generateContentItems(
resources,
projectPath,
/* assets */ undefined,
/* currentFilter */ undefined,
/* source */ undefined,
fs
);
return {
assetItems: assetItems.join("\n "),
assetItemFilters: assetItemFilters.join("\n "),
assetFilters: assetFilters.join("\n "),
};
}
/**
* Returns path to the specified asset relative to the project path.
* @param {string} projectPath
* @param {string} assetPath
* @returns {string}
*/
function projectRelativePath(projectPath, assetPath) {
return normalizePath(
path.isAbsolute(assetPath)
? path.relative(projectPath, assetPath)
: assetPath
);
}
/**
* Replaces parts in specified content.
* @param {string} content Content to be replaced.
@ -416,16 +78,6 @@ export function replaceContent(content, replacements) {
);
}
/**
* Rethrows specified error.
* @param {Error | null} error
*/
function rethrow(error) {
if (error) {
throw error;
}
}
/**
* Returns a solution entry for specified project.
* @param {{ path: string; name: string; guid: string; }} project
@ -448,129 +100,30 @@ export function toProjectEntry(project, destPath) {
* @param {string} srcPath Path to the file to be copied.
* @param {string} destPath Destination path.
* @param {Record<string, string> | undefined} replacements e.g. {'TextToBeReplaced': 'Replacement'}
* @param {(error: Error | null) => void=} callback Callback for when the copy operation is done.
* @returns {Promise<void>}
*/
export function copyAndReplace(
export async function copyAndReplace(
srcPath,
destPath,
replacements,
callback = rethrow,
fs = nodefs
fs = nodefs.promises
) {
const stat = fs.statSync(srcPath);
if (stat.isDirectory()) {
copy(srcPath, destPath, fs);
} else if (!replacements) {
fs.copyFile(srcPath, destPath, callback);
} else {
// Treat as text file
fs.readFile(srcPath, { encoding: "utf-8" }, (err, data) => {
if (err) {
callback(err);
return;
}
fs.writeFile(
destPath,
replaceContent(data, replacements),
{
encoding: "utf-8",
mode: stat.mode,
},
callback
);
});
}
}
/**
* Reads manifest file and and resolves paths to bundle resources.
* @param {string | null} manifestFilePath Path to the closest manifest file.
* @returns {{
* appName: string;
* appxManifest: string;
* assetItems: string;
* assetItemFilters: string;
* assetFilters: string;
* packageCertificate: string;
* singleApp?: string;
* }} Application name, and paths to directories and files to include.
*/
export function getBundleResources(manifestFilePath, fs = nodefs) {
// Default value if manifest or 'name' field don't exist.
const defaultName = "ReactTestApp";
// Default `Package.appxmanifest` path. The project will automatically use our
// fallback if there is no file at this path.
const defaultAppxManifest = "windows/Package.appxmanifest";
if (manifestFilePath) {
try {
/** @type {AppManifest} */
const manifest = readJSONFile(manifestFilePath, fs);
const { name, singleApp, resources, windows } = manifest;
const projectPath = path.dirname(manifestFilePath);
return {
appName: name || defaultName,
singleApp,
appxManifest: projectRelativePath(
projectPath,
(windows && windows.appxManifest) || defaultAppxManifest
),
packageCertificate: generateCertificateItems(
windows || {},
projectPath
),
...parseResources(resources, projectPath, fs),
};
} catch (e) {
if (isErrorLike(e)) {
console.warn(`Could not parse 'app.json':\n${e.message}`);
} else {
throw e;
}
}
} else {
console.warn("Could not find 'app.json' file.");
if (!replacements) {
return fs.cp(srcPath, destPath, { recursive: true });
}
return {
appName: defaultName,
appxManifest: defaultAppxManifest,
assetItems: "",
assetItemFilters: "",
assetFilters: "",
packageCertificate: "",
};
}
/**
* Returns the version of Hermes that should be installed.
* @param {string} rnwPath Path to `react-native-windows`.
* @returns {string | null}
*/
export function getHermesVersion(rnwPath, fs = nodefs) {
const jsEnginePropsPath = path.join(
rnwPath,
"PropertySheets",
"JSEngine.props"
);
const props = readTextFile(jsEnginePropsPath, fs);
const m = props.match(/<HermesVersion.*?>(.+?)<\/HermesVersion>/);
return m && m[1];
// Treat as text file
const data = await fs.readFile(srcPath, { encoding: "utf-8" });
return writeTextFile(destPath, replaceContent(data, replacements), fs);
}
/**
* Generates Visual Studio solution.
* @param {string} destPath Destination path.
* @param {{ autolink: boolean; useHermes: boolean | undefined; useNuGet: boolean; }} options
* @param {MSBuildProjectOptions} options
* @returns {string | undefined} An error message; `undefined` otherwise.
*/
export function generateSolution(
destPath,
{ autolink, useHermes, useNuGet },
fs = nodefs
) {
export function generateSolution(destPath, options, fs = nodefs) {
if (!destPath) {
return "Missing or invalid destination path";
}
@ -591,16 +144,21 @@ export function generateSolution(
return "Could not find 'react-native-windows'";
}
const rnTestAppPath = findNearest(
path.join(nodeModulesDir, "react-native-test-app"),
undefined,
fs
);
if (!rnTestAppPath) {
return "Could not find 'react-native-test-app'";
if (validate("file", destPath) !== 0) {
return "App manifest validation failed!";
}
const info = projectInfo(options, rnWindowsPath, destPath, fs);
const { projDir, projectFileName, projectFiles, solutionTemplatePath } =
info.useFabric
? configureForWin32(info, options)
: configureForUWP(info, options);
const solutionTemplate = path.join(rnWindowsPath, solutionTemplatePath);
if (!fs.existsSync(solutionTemplate)) {
return "Could not find solution template";
}
const projDir = "ReactTestApp";
const projectFilesDestPath = path.join(
path.dirname(projectManifest),
nodeModulesDir,
@ -609,116 +167,19 @@ export function generateSolution(
projDir
);
const mkdirRecursiveOptions = { recursive: true, mode: 0o755 };
fs.mkdirSync(projectFilesDestPath, mkdirRecursiveOptions);
fs.mkdirSync(destPath, mkdirRecursiveOptions);
validate("file", destPath);
const manifestFilePath = findNearest("app.json", destPath, fs);
const {
appName,
appxManifest,
assetItems,
assetItemFilters,
assetFilters,
packageCertificate,
singleApp,
} = getBundleResources(manifestFilePath, fs);
const rnWindowsVersion = getPackageVersion(
"react-native-windows",
rnWindowsPath,
fs
);
const rnWindowsVersionNumber = toVersionNumber(rnWindowsVersion);
const hermesVersion = useHermes && getHermesVersion(rnWindowsPath, fs);
const usePackageReferences =
rnWindowsVersionNumber === 0 || rnWindowsVersionNumber >= v(0, 68, 0);
const xamlVersion =
rnWindowsVersionNumber === 0 || rnWindowsVersionNumber >= v(0, 73, 0)
? "2.8.0"
: rnWindowsVersionNumber >= v(0, 67, 0)
? "2.7.0"
: "2.6.0";
const nuGetDependencies = getNuGetDependencies(rnWindowsPath);
/** @type {[string, Record<string, string>?][]} */
const projectFiles = [
["Assets"],
["AutolinkedNativeModules.g.cpp"],
["AutolinkedNativeModules.g.props"],
["AutolinkedNativeModules.g.targets"],
["Package.appxmanifest"],
["PropertySheet.props"],
[
"ReactTestApp.vcxproj",
{
"REACT_NATIVE_VERSION=1000000000;": `REACT_NATIVE_VERSION=${rnWindowsVersionNumber};`,
"\\$\\(ReactTestAppPackageManifest\\)": appxManifest,
"\\$\\(ReactNativeWindowsNpmVersion\\)": rnWindowsVersion,
"<!-- ReactTestApp asset items -->": assetItems,
"<!-- ReactTestApp additional targets -->":
importTargets(nuGetDependencies),
...(typeof singleApp === "string"
? { "ENABLE_SINGLE_APP_MODE=0;": "ENABLE_SINGLE_APP_MODE=1;" }
: undefined),
...(useNuGet
? {
"<UseExperimentalNuget>false</UseExperimentalNuget>":
"<UseExperimentalNuget>true</UseExperimentalNuget>",
"<WinUI2xVersionDisabled />": `<WinUI2xVersion>${xamlVersion}</WinUI2xVersion>`,
}
: undefined),
...(packageCertificate
? {
"<AppxPackageSigningEnabled>false</AppxPackageSigningEnabled>":
packageCertificate,
}
: undefined),
},
],
[
"ReactTestApp.vcxproj.filters",
{
"<!-- ReactTestApp asset item filters -->": assetItemFilters,
"<!-- ReactTestApp asset filters -->": assetFilters,
"\\$\\(ReactTestAppPackageManifest\\)": appxManifest,
},
],
[
"packages.config",
{
'<package id="Microsoft.UI.Xaml" version="0.0.0" targetFramework="native"/>':
nuGetPackage("Microsoft.UI.Xaml", xamlVersion),
"<!-- additional packages -->": nuGetDependencies
.map(([id, version]) => nuGetPackage(id, version))
.join("\n "),
...(useNuGet && !usePackageReferences
? {
'<!-- package id="Microsoft.ReactNative" version="1000.0.0" targetFramework="native"/ -->':
nuGetPackage("Microsoft.ReactNative", rnWindowsVersion),
'<!-- package id="Microsoft.ReactNative.Cxx" version="1000.0.0" targetFramework="native"/ -->':
nuGetPackage("Microsoft.ReactNative.Cxx", rnWindowsVersion),
}
: undefined),
...(hermesVersion && !usePackageReferences
? {
'<!-- package id="ReactNative.Hermes.Windows" version="0.0.0" targetFramework="native"/ -->':
nuGetPackage("ReactNative.Hermes.Windows", hermesVersion),
}
: undefined),
},
],
];
/** @type {typeof copyAndReplace} */
const copyAndReplaceAsync = (src, dst, r) =>
copyAndReplace(src, dst, r, fs.promises);
const copyTasks = projectFiles.map(([file, replacements]) =>
copyAndReplace(
copyAndReplaceAsync(
fileURLToPath(new URL(`${projDir}/${file}`, import.meta.url)),
path.join(projectFilesDestPath, file),
replacements,
undefined,
fs
replacements
)
);
@ -726,55 +187,44 @@ export function generateSolution(
.map((project) => toProjectEntry(project, destPath))
.join(os.EOL);
const solutionTemplatePath = findNearest(
path.join(
nodeModulesDir,
"react-native-windows",
"template",
"cpp-app",
"proj",
"MyApp.sln"
),
undefined,
fs
);
if (!solutionTemplatePath) {
throw new Error("Failed to find solution template");
}
/** @type {import("mustache")} */
const mustache = requireTransitive(
["@react-native-windows/cli", "mustache"],
rnWindowsPath
);
const reactTestAppProjectPath = path.join(
projectFilesDestPath,
"ReactTestApp.vcxproj"
);
const solutionTask = fs.writeFile(
path.join(destPath, `${appName}.sln`),
mustache
.render(readTextFile(solutionTemplatePath, fs), {
...templateView,
useExperimentalNuget: useNuGet,
})
// The current version of this template (v0.63.18) assumes that
// `react-native-windows` is always installed in
// `..\node_modules\react-native-windows`.
.replace(
/"\.\.\\node_modules\\react-native-windows\\/g,
`"${path.relative(destPath, rnWindowsPath)}\\`
)
.replace(
"ReactTestApp\\ReactTestApp.vcxproj",
path.relative(destPath, reactTestAppProjectPath)
)
.replace(
/EndProject\r?\nGlobal/,
["EndProject", additionalProjectEntries, "Global"].join(os.EOL)
),
textFileWriteOptions,
rethrow
const vcxprojPath = path.join(projectFilesDestPath, projectFileName);
const vcxprojLocalPath = path.relative(destPath, vcxprojPath);
copyTasks.push(
writeTextFile(
path.join(destPath, `${info.bundle.appName}.sln`),
mustache
.render(readTextFile(solutionTemplate, fs), {
...templateView,
name: path.basename(projectFileName, path.extname(projectFileName)),
useExperimentalNuget: info.useExperimentalNuGet,
})
// The current version of this template (v0.63.18) assumes that
// `react-native-windows` is always installed in
// `..\node_modules\react-native-windows`.
.replace(
/"\.\.\\node_modules\\react-native-windows\\/g,
`"${path.relative(destPath, rnWindowsPath)}\\`
)
.replace("ReactApp\\ReactApp.vcxproj", vcxprojLocalPath) // Win32
.replace(
"ReactApp.Package\\ReactApp.Package.wapproj", // Win32
vcxprojLocalPath.replace(
"ReactApp.vcxproj",
"ReactApp.Package.wapproj"
)
)
.replace("ReactTestApp\\ReactTestApp.vcxproj", vcxprojLocalPath) // UWP
.replace(
/EndProject\r?\nGlobal/,
["EndProject", additionalProjectEntries, "Global"].join(os.EOL)
),
fs.promises
)
);
const experimentalFeaturesPropsFilename = "ExperimentalFeatures.props";
@ -783,110 +233,92 @@ export function generateSolution(
experimentalFeaturesPropsFilename
);
if (!fs.existsSync(experimentalFeaturesPropsPath)) {
copyAndReplace(
fileURLToPath(
new URL(experimentalFeaturesPropsFilename, import.meta.url)
),
experimentalFeaturesPropsPath,
{
...(useHermes != null && (usePackageReferences || hermesVersion)
? {
"<!-- UseHermes>true</UseHermes -->": `<UseHermes>${useHermes}</UseHermes>`,
}
: undefined),
},
undefined,
fs
);
const { useHermes } = options;
const {
hermesVersion,
useExperimentalNuGet,
useFabric,
usePackageReferences,
} = info;
const url = new URL(experimentalFeaturesPropsFilename, import.meta.url);
copyAndReplaceAsync(fileURLToPath(url), experimentalFeaturesPropsPath, {
"<UseFabric>false</UseFabric>": `<UseFabric>${useFabric}</UseFabric>`,
"<UseHermes>true</UseHermes>": `<UseHermes>${Boolean(hermesVersion) || (useHermes != null && usePackageReferences)}</UseHermes>`,
"<UseWinUI3>false</UseWinUI3>": `<UseWinUI3>${useFabric}</UseWinUI3>`,
"<UseExperimentalNuget>false</UseExperimentalNuget>": `<UseExperimentalNuget>${useExperimentalNuGet}</UseExperimentalNuget>`,
});
}
// TODO: Remove when we drop support for 0.67.
// Patch building with Visual Studio 2022. For more details, see
// https://github.com/microsoft/react-native-windows/issues/9559
if (rnWindowsVersionNumber < v(0, 68, 0)) {
if (info.versionNumber < v(0, 68, 0)) {
const dispatchQueue = path.join(
rnWindowsPath,
"Mso",
"dispatchQueue",
"dispatchQueue.h"
);
copyAndReplace(
dispatchQueue,
dispatchQueue,
{
"template <typename T>\\s*inline void MustBeNoExceptVoidFunctor\\(\\) {\\s*static_assert\\(false":
"namespace details {\n template <typename>\n constexpr bool always_false = false;\n}\n\ntemplate <typename T>\ninline void MustBeNoExceptVoidFunctor() {\n static_assert(details::always_false<T>",
},
undefined,
fs
);
copyAndReplaceAsync(dispatchQueue, dispatchQueue, {
"template <typename T>\\s*inline void MustBeNoExceptVoidFunctor\\(\\) {\\s*static_assert\\(false":
"namespace details {\n template <typename>\n constexpr bool always_false = false;\n}\n\ntemplate <typename T>\ninline void MustBeNoExceptVoidFunctor() {\n static_assert(details::always_false<T>",
});
}
// TODO: Remove when we drop support for 0.69.
// Patch building with Visual Studio 2022. For more details, see
// https://github.com/microsoft/react-native-windows/pull/10373
if (rnWindowsVersionNumber < v(0, 70, 0)) {
if (info.versionNumber < v(0, 70, 0)) {
const helpers = path.join(
rnWindowsPath,
"Microsoft.ReactNative",
"Utils",
"Helpers.h"
);
copyAndReplace(
helpers,
helpers,
{
"inline typename T asEnum": "inline T asEnum",
},
undefined,
fs
);
copyAndReplaceAsync(helpers, helpers, {
"inline typename T asEnum": "inline T asEnum",
});
}
if (useNuGet) {
const nugetConfigPath =
findNearest(
// In 0.70, the template was renamed from `NuGet.Config` to `NuGet_Config`
path.join(
nodeModulesDir,
"react-native-windows",
"template",
"shared-app",
"proj",
"NuGet_Config"
),
undefined,
fs
) ||
findNearest(
// In 0.64, the template was moved into `react-native-windows`
path.join(
nodeModulesDir,
"react-native-windows",
"template",
"shared-app",
"proj",
"NuGet.Config"
),
undefined,
fs
);
if (info.useExperimentalNuGet) {
// In 0.64, the template was moved into `react-native-windows`
const nugetConfigPath0_64 = path.join(
rnWindowsPath,
"template",
"shared-app",
"proj",
"NuGet.Config"
);
// In 0.70, the template was renamed from `NuGet.Config` to `NuGet_Config`
const nugetConfigPath0_70 = path.join(
rnWindowsPath,
"template",
"shared-app",
"proj",
"NuGet_Config"
);
const nugetConfigPath = fs.existsSync(nugetConfigPath0_70)
? nugetConfigPath0_70
: fs.existsSync(nugetConfigPath0_64)
? nugetConfigPath0_64
: null;
const nugetConfigDestPath = path.join(destPath, "NuGet.Config");
if (nugetConfigPath && !fs.existsSync(nugetConfigDestPath)) {
fs.writeFile(
nugetConfigDestPath,
mustache.render(readTextFile(nugetConfigPath, fs), {}),
textFileWriteOptions,
rethrow
copyTasks.push(
writeTextFile(
nugetConfigDestPath,
mustache.render(readTextFile(nugetConfigPath, fs), {}),
fs.promises
)
);
}
}
if (autolink) {
Promise.all([...copyTasks, solutionTask]).then(() => {
if (options.autolink) {
Promise.all(copyTasks).then(() => {
spawn(
path.join(path.dirname(process.argv0), "npx.cmd"),
["react-native", "autolink-windows", "--proj", reactTestAppProjectPath],
["react-native", "autolink-windows", "--proj", vcxprojPath],
{ stdio: "inherit" }
).on("close", (code) => {
if (code !== 0) {
@ -901,7 +333,7 @@ export function generateSolution(
if (isMain(import.meta.url)) {
parseArgs(
"Generate a Visual Studio solution for React Test App",
"Generate a Visual Studio solution for a React Native app",
{
"project-directory": {
description:
@ -915,12 +347,17 @@ if (isMain(import.meta.url)) {
type: "boolean",
default: os.platform() === "win32",
},
"use-fabric": {
description: "Use New Architecture [experimental] (supported on 0.73+)",
type: "boolean",
},
"use-hermes": {
description: "Use Hermes JavaScript engine (experimental)",
description:
"Use Hermes instead of Chakra as the JS engine (enabled by default on 0.73+)",
type: "boolean",
},
"use-nuget": {
description: "Use NuGet packages (experimental)",
description: "Use NuGet packages [experimental]",
type: "boolean",
default: false,
},
@ -928,10 +365,11 @@ if (isMain(import.meta.url)) {
({
"project-directory": projectDirectory,
autolink,
"use-fabric": useFabric,
"use-hermes": useHermes,
"use-nuget": useNuGet,
}) => {
const options = { autolink, useHermes, useNuGet };
const options = { autolink, useFabric, useHermes, useNuGet };
const error = generateSolution(path.resolve(projectDirectory), options);
if (error) {
console.error(error);

88
windows/uwp.mjs Normal file
Просмотреть файл

@ -0,0 +1,88 @@
// @ts-check
import * as path from "node:path";
import { importTargets, nugetPackage } from "./project.mjs";
/** @type {import("../scripts/types").MSBuildProjectConfigurator} */
export function configureForUWP(
{
bundle,
hermesVersion,
nugetDependencies,
usePackageReferences,
version,
versionNumber,
xamlVersion,
},
{ useNuGet }
) {
return {
projDir: "UWP",
projectFileName: "ReactTestApp.vcxproj",
projectFiles: [
["Assets"],
["AutolinkedNativeModules.g.cpp"],
["AutolinkedNativeModules.g.props"],
["AutolinkedNativeModules.g.targets"],
["Package.appxmanifest"],
["PropertySheet.props"],
[
"ReactTestApp.vcxproj",
{
"REACT_NATIVE_VERSION=1000000000;": `REACT_NATIVE_VERSION=${versionNumber};`,
"\\$\\(ReactTestAppPackageManifest\\)": bundle.appxManifest,
"\\$\\(ReactNativeWindowsNpmVersion\\)": version,
"<!-- ReactTestApp asset items -->": bundle.assetItems,
"<!-- ReactTestApp additional targets -->":
importTargets(nugetDependencies),
...(typeof bundle.singleApp === "string"
? { "ENABLE_SINGLE_APP_MODE=0;": "ENABLE_SINGLE_APP_MODE=1;" }
: undefined),
...(useNuGet
? {
"<WinUI2xVersionDisabled />": `<WinUI2xVersion>${xamlVersion}</WinUI2xVersion>`,
}
: undefined),
...(bundle.packageCertificate
? {
"<AppxPackageSigningEnabled>false</AppxPackageSigningEnabled>":
bundle.packageCertificate,
}
: undefined),
},
],
[
"ReactTestApp.vcxproj.filters",
{
"<!-- ReactTestApp asset item filters -->": bundle.assetItemFilters,
"<!-- ReactTestApp asset filters -->": bundle.assetFilters,
"\\$\\(ReactTestAppPackageManifest\\)": bundle.appxManifest,
},
],
[
"packages.config",
{
'<package id="Microsoft.UI.Xaml" version="0.0.0" targetFramework="native"/>':
nugetPackage("Microsoft.UI.Xaml", xamlVersion),
"<!-- additional packages -->": nugetDependencies
.map(([id, version]) => nugetPackage(id, version))
.join("\n "),
...(useNuGet && !usePackageReferences
? {
'<!-- package id="Microsoft.ReactNative" version="1000.0.0" targetFramework="native"/ -->':
nugetPackage("Microsoft.ReactNative", version),
'<!-- package id="Microsoft.ReactNative.Cxx" version="1000.0.0" targetFramework="native"/ -->':
nugetPackage("Microsoft.ReactNative.Cxx", version),
}
: undefined),
...(hermesVersion && !usePackageReferences
? {
'<!-- package id="ReactNative.Hermes.Windows" version="0.0.0" targetFramework="native"/ -->':
nugetPackage("ReactNative.Hermes.Windows", hermesVersion),
}
: undefined),
},
],
],
solutionTemplatePath: path.join("template", "cpp-app", "proj", "MyApp.sln"),
};
}

49
windows/win32.mjs Normal file
Просмотреть файл

@ -0,0 +1,49 @@
// @ts-check
import * as path from "node:path";
import { importTargets } from "./project.mjs";
/** @type {import("../scripts/types").MSBuildProjectConfigurator} */
export function configureForWin32(
{ bundle, nugetDependencies, versionNumber },
_options
) {
return {
projDir: "Win32",
projectFileName: "ReactApp.vcxproj",
projectFiles: [
["AutolinkedNativeModules.g.cpp"],
["Images"],
["Main.ico"],
["Main.rc"],
["Main.small.ico"],
["Package.appxmanifest"],
["ReactApp.Package.wapproj"],
[
"ReactApp.vcxproj",
{
"REACT_NATIVE_VERSION=1000000000;": `REACT_NATIVE_VERSION=${versionNumber};`,
"<!-- ReactTestApp asset items -->": bundle.assetItems,
"<!-- ReactTestApp additional targets -->":
importTargets(nugetDependencies),
...(typeof bundle.singleApp === "string"
? { "ENABLE_SINGLE_APP_MODE=0;": "ENABLE_SINGLE_APP_MODE=1;" }
: undefined),
},
],
[
"ReactApp.vcxproj.filters",
{
"<!-- ReactTestApp asset item filters -->": bundle.assetItemFilters,
"<!-- ReactTestApp asset filters -->": bundle.assetFilters,
},
],
["resource.h"],
],
solutionTemplatePath: path.join(
"templates",
"cpp-app",
"windows",
"MyApp.sln"
),
};
}