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.
|
@ -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 &);
|
||||
}
|
После Ширина: | Высота: | Размер: 1.4 KiB |
После Ширина: | Высота: | Размер: 7.5 KiB |
После Ширина: | Высота: | Размер: 2.9 KiB |
После Ширина: | Высота: | Размер: 1.6 KiB |
После Ширина: | Высота: | Размер: 1.2 KiB |
После Ширина: | Высота: | Размер: 1.4 KiB |
После Ширина: | Высота: | Размер: 3.1 KiB |
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
#include "resource.h"
|
После Ширина: | Высота: | Размер: 45 KiB |
|
@ -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
|
||||
|
||||
|
После Ширина: | Высота: | Размер: 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>
|
|
@ -0,0 +1 @@
|
|||
#include "pch.h"
|
|
@ -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>
|
|
@ -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>
|
|
@ -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);
|
||||
|
|
|
@ -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"),
|
||||
};
|
||||
}
|
|
@ -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"
|
||||
),
|
||||
};
|
||||
}
|