@rnv/engine-rn-windows
Version:
ReNative Engine to build for Windows platform with react native support.
410 lines • 24.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.copyProjectTemplateAndReplace = void 0;
var tslib_1 = require("tslib");
/**
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
* @format
*/
// DEPS
var core_1 = require("@rnv/core");
var path = require('path');
var username = require('username');
var uuid = require('uuid');
var childProcess = require('child_process');
var fs = require('fs');
var os = require('os');
// eslint-disable-next-line no-unused-vars
// const _ = require('lodash');
var findUp = require('find-up');
var generator_common_1 = require('./generator-common');
var configUtils_1 = require('./config/configUtils');
// EXTRACTS FROM RNV
var getAppFolder = core_1.Common.getAppFolder, getAppTitle = core_1.Common.getAppTitle, getConfigProp = core_1.Common.getConfigProp;
var copyFolderContentsRecursive = core_1.FileUtils.copyFolderContentsRecursive, copyFolderContentsRecursiveSync = core_1.FileUtils.copyFolderContentsRecursiveSync;
var logError = core_1.Logger.logError, logDefault = core_1.Logger.logDefault, logInfo = core_1.Logger.logInfo, logWarning = core_1.Logger.logWarning, logSuccess = core_1.Logger.logSuccess;
var copyAssetsFolder = core_1.ProjectManager.copyAssetsFolder;
// CONSTS
var bundleDir = 'Bundle';
// FUNCTIONS
function generateCertificate(srcPath, currentUser, c, options) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var appFolder, timeout, thumbprint;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
logDefault('Generating self-signed certificate');
appFolder = getAppFolder(true);
if (os.platform() === 'win32') {
try {
timeout = 10000;
thumbprint = childProcess
.execSync(
// eslint-disable-next-line max-len
"powershell -NoProfile -Command \"Write-Output (New-SelfSignedCertificate -KeyUsage DigitalSignature -KeyExportPolicy Exportable -Subject 'CN=".concat(currentUser, "' -TextExtension @('2.5.29.37={text}1.3.6.1.5.5.7.3.3', '2.5.29.19={text}Subject Type:End Entity') -CertStoreLocation 'Cert:\\CurrentUser\\My').Thumbprint\""), { timeout: timeout })
.toString()
.trim();
childProcess.execSync(
// eslint-disable-next-line max-len
"powershell -NoProfile -Command \"$pwd = (ConvertTo-SecureString -String password -Force -AsPlainText); Export-PfxCertificate -Cert 'cert:\\CurrentUser\\My\\".concat(thumbprint, "' -FilePath ").concat(path.join(appFolder, c.runtime.appId, c.runtime.appId), "_TemporaryKey.pfx -Password $pwd\""), { timeout: timeout });
logSuccess('Self-signed certificate generated successfully.');
return [2 /*return*/, thumbprint];
}
catch (err) {
logError('Failed to generate Self-signed certificate.');
}
}
logWarning('Using Default Certificate. Use Visual Studio to renew it.');
return [4 /*yield*/, generator_common_1.copyAndReplaceWithChangedCallback(path.join(srcPath, 'keys', 'MyApp_TemporaryKey.pfx'), c.paths.project.dir, path.join(appFolder, c.runtime.appId, "".concat(c.runtime.appId, "_TemporaryKey.pfx")), undefined, options)];
case 1:
_a.sent();
return [2 /*return*/, null];
}
});
});
}
// Existing high cyclomatic complexity
function copyProjectTemplateAndReplace(c, options) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var appTitle, appFolder, RNIconsPluginPath, language, experimentalNuGetDependency, useWinUI3, nuGetTestVersion, useHermes, nuGetTestFeed, namespaceCpp, reactNative, rnVersion, RNWTemplatePath, srcPath, sharedPath, projDir, projectGuid, rnwVersion, nugetVersion, packageGuid, currentUser, mainComponentName, appJsonPath, certificateThumbprint, xamlNamespace, xamlNamespaceCpp, winui3PropsPath, winui3Props, winui3Version, csNugetPackages, cppNugetPackages, isMonorepo, monoRoot, templateVars, commonMappings, _i, commonMappings_1, mapping, csMappings, _a, csMappings_1, mapping, cppMappings, _b, cppMappings_1, mapping, sharedProjMappings, _c, sharedProjMappings_1, mapping, appFolderFull, glyphmapsDir, RNIconsGlyphmapsPluginPath;
return tslib_1.__generator(this, function (_d) {
switch (_d.label) {
case 0:
if (!c.paths.project.dir) {
throw new Error('Need a path to copy to');
}
appTitle = getAppTitle();
appFolder = getAppFolder(true);
RNIconsPluginPath = path.join(path.dirname(require.resolve('react-native-vector-icons/package.json', {
paths: [c.paths.project.dir],
})), 'Fonts');
language = getConfigProp('language', options.language);
experimentalNuGetDependency = getConfigProp(c, c.platform, 'experimentalNuGetDependency', options.experimentalNuGetDependency);
useWinUI3 = getConfigProp('useWinUI3', options.useWinUI3);
nuGetTestVersion = getConfigProp('nuGetTestVersion', options.nuGetTestVersion);
useHermes = !!getConfigProp('reactNativeEngine', options.reactNativeEngine) === 'hermes';
nuGetTestFeed = getConfigProp('nuGetTestFeed', options.nuGetTestFeed);
generator_common_1.createDir(path.join(c.paths.project.dir, appFolder));
generator_common_1.createDir(path.join(c.paths.project.dir, appFolder, c.runtime.appId));
generator_common_1.createDir(path.join(c.paths.project.dir, appFolder, c.runtime.appId, bundleDir));
generator_common_1.createDir(path.join(c.paths.project.dir, appFolder, c.runtime.appId, 'BundleBuilder'));
namespaceCpp = toCppNamespace(c.runtime.appId);
if (experimentalNuGetDependency) {
logInfo('Using experimental NuGet dependency.');
}
if (useWinUI3) {
logInfo('Using experimental WinUI3 dependency.');
}
reactNative = require('react-native/package.json');
rnVersion = parseFloat(reactNative.version);
// In 0.64 version of RN Windows the location was changed and some folders were renamed (separation between lib abd app)
if (rnVersion >= 0.64) {
RNWTemplatePath = path.join(path.dirname(require.resolve('react-native-windows/package.json', {
paths: [c.paths.project.dir],
})), 'template');
// TODO Add support for developing libs, not just apps using renative (RN Windows added this in 0.64 version)
srcPath = path.join(RNWTemplatePath, "".concat(language, "-app"));
sharedPath = path.join(RNWTemplatePath, 'shared-app');
}
else if (rnVersion >= 0.63) {
RNWTemplatePath = path.join(path.dirname(require.resolve('@react-native-windows/cli/package.json', {
paths: [c.paths.project.dir],
})), 'templates');
srcPath = path.join(RNWTemplatePath, "".concat(language));
sharedPath = path.join(RNWTemplatePath, 'shared');
}
else {
logError("ReNative's React Native Windows engine does not support version of React Native older than 0.63");
}
projDir = 'proj';
projectGuid = uuid.v4();
rnwVersion = require('react-native-windows/package.json').version;
nugetVersion = nuGetTestVersion || rnwVersion;
packageGuid = uuid.v4();
currentUser = username.sync();
mainComponentName = c.runtime.appId;
return [4 /*yield*/, findUp('app.json', { cwd: c.paths.project.dir })];
case 1:
appJsonPath = _d.sent();
if (appJsonPath) {
mainComponentName = JSON.parse(fs.readFileSync(appJsonPath, 'utf8')).name;
}
if (!!fs.existsSync(path.join(appFolder, c.runtime.appId, "".concat(c.runtime.appId, "_TemporaryKey.pfx")))) return [3 /*break*/, 3];
return [4 /*yield*/, generateCertificate(srcPath, currentUser, c, options)];
case 2:
certificateThumbprint = _d.sent();
return [3 /*break*/, 4];
case 3:
logInfo('[generateCertificate] Certificate already exists, skipping generation of a new one.');
_d.label = 4;
case 4:
xamlNamespace = useWinUI3 ? 'Microsoft.UI.Xaml' : 'Windows.UI.Xaml';
xamlNamespaceCpp = toCppNamespace(xamlNamespace);
winui3PropsPath = require.resolve('react-native-windows/PropertySheets/WinUI.props', {
paths: [process.cwd()],
});
winui3Props = configUtils_1.readProjectFile(winui3PropsPath);
winui3Version = configUtils_1.findPropertyValue(winui3Props, 'WinUI3Version', winui3PropsPath);
csNugetPackages = [
{
id: 'Microsoft.NETCore.UniversalWindowsPlatform',
version: '6.2.9',
},
];
cppNugetPackages = [
{
id: 'Microsoft.Windows.CppWinRT',
version: rnVersion > 0.64 ? '2.0.210312.4' : '2.0.200615.7',
propsTopOfFile: true,
hasProps: true,
hasTargets: true,
},
{
id: useWinUI3 ? 'Microsoft.WinUI' : 'Microsoft.UI.Xaml',
version: useWinUI3 ? winui3Version : '2.3.191129002',
hasProps: false,
hasTargets: false,
},
];
if (experimentalNuGetDependency) {
csNugetPackages.push({
id: 'Microsoft.ReactNative.Managed',
version: nugetVersion,
});
cppNugetPackages.push({
id: 'Microsoft.ReactNative',
version: nugetVersion,
hasProps: false,
hasTargets: true,
});
cppNugetPackages.push({
id: 'Microsoft.ReactNative.Cxx',
version: nugetVersion,
hasProps: false,
hasTargets: true,
});
}
if (useHermes && rnVersion > 0.64) {
cppNugetPackages.push({
id: 'ReactNative.Hermes.Windows',
version: '0.7.2',
hasProps: false,
hasTargets: true,
});
}
isMonorepo = getConfigProp('isMonorepo', false);
monoRoot = getConfigProp('monoRoot') || '..\\..';
templateVars = {
rnwPackagePath: isMonorepo
? "".concat(monoRoot.replace(/\//g, '\\'), "\\..\\..\\node_modules\\react-native-windows")
: '..\\..\\node_modules\\react-native-windows',
useMustache: true,
regExpPatternsToRemove: [],
name: c.runtime.appId,
namespace: namespaceCpp,
title: appTitle,
namespaceCpp: namespaceCpp,
languageIsCpp: language === 'cpp',
mainComponentName: mainComponentName,
// Visual Studio is very picky about the casing of the guids for projects, project references and the solution
// https://www.bing.com/search?q=visual+studio+project+guid+casing&cvid=311a5ad7f9fc41089507b24600d23ee7&FORM=ANAB01&PC=U531
// we therefore have to precariously use the right casing in the right place or risk building in VS breaking.
projectGuidLower: "{".concat(projectGuid.toLowerCase(), "}"),
projectGuidUpper: "{".concat(projectGuid.toUpperCase(), "}"),
// packaging and signing variables:
packageGuid: packageGuid,
currentUser: currentUser,
certificateThumbprint: certificateThumbprint,
useExperimentalNuget: experimentalNuGetDependency,
nuGetTestFeed: nuGetTestFeed,
// cpp template variables
useWinUI3: useWinUI3,
useHermes: useHermes,
xamlNamespace: xamlNamespace,
xamlNamespaceCpp: xamlNamespaceCpp,
cppNugetPackages: cppNugetPackages,
// Development port config
devPort: c.runtime.port || 8092,
// cs template variables
csNugetPackages: csNugetPackages,
// autolinking template variables
autolinkPropertiesForProps: '',
autolinkProjectReferencesForTargets: '',
autolinkCsUsingNamespaces: '',
autolinkCsReactPackageProviders: '',
autolinkCppIncludes: '',
autolinkCppPackageProviders: '\n UNREFERENCED_PARAMETER(packageProviders);',
hasAdditionalAssets: RNIconsPluginPath && fs.existsSync(RNIconsPluginPath),
};
commonMappings = [
// app common mappings
{
from: path.join(RNWTemplatePath, 'index.windows.bundle'),
to: path.join(appFolder, c.runtime.appId, bundleDir, 'index.windows.bundle'),
},
{
from: path.join(srcPath, projDir, 'MyApp.sln'),
to: path.join(appFolder, "".concat(c.runtime.appId, ".sln")),
},
{
from: path.join(RNWTemplatePath, 'index.windows.bundle'),
to: path.join(appFolder, c.runtime.appId, bundleDir, 'index.windows.bundle'),
},
{
from: path.join(RNWTemplatePath, 'app.json'),
to: 'app.json',
},
{
from: path.join(RNWTemplatePath, 'app.json'),
to: path.join(appFolder, c.runtime.appId, bundleDir, 'assets', 'app.json'),
},
];
// Do not override metro inside the project if one already exists
if (!fs.existsSync(path.join(c.paths.project.dir, 'metro.config.js'))) {
commonMappings.push({
from: path.join(RNWTemplatePath, 'metro.config.js'),
to: 'metro.config.js',
});
}
if (!fs.existsSync(path.join(appFolder, c.runtime.appId, bundleDir, 'assets'))) {
fs.mkdirSync(path.join(appFolder, c.runtime.appId, bundleDir, 'assets'), { recursive: true });
}
_i = 0, commonMappings_1 = commonMappings;
_d.label = 5;
case 5:
if (!(_i < commonMappings_1.length)) return [3 /*break*/, 8];
mapping = commonMappings_1[_i];
return [4 /*yield*/, generator_common_1.copyAndReplaceWithChangedCallback(mapping.from, c.paths.project.dir, mapping.to, templateVars, options)];
case 6:
_d.sent();
_d.label = 7;
case 7:
_i++;
return [3 /*break*/, 5];
case 8:
if (!(language === 'cs')) return [3 /*break*/, 13];
csMappings = [
// cs app mappings
{
from: path.join(srcPath, projDir, 'MyApp.csproj'),
to: path.join(appFolder, c.runtime.appId, "".concat(c.runtime.appId, ".csproj")),
},
];
_a = 0, csMappings_1 = csMappings;
_d.label = 9;
case 9:
if (!(_a < csMappings_1.length)) return [3 /*break*/, 12];
mapping = csMappings_1[_a];
return [4 /*yield*/, generator_common_1.copyAndReplaceWithChangedCallback(mapping.from, c.paths.project.dir, mapping.to, templateVars, options)];
case 10:
_d.sent();
_d.label = 11;
case 11:
_a++;
return [3 /*break*/, 9];
case 12: return [3 /*break*/, 17];
case 13:
cppMappings = [
// cpp app mappings
{
from: path.join(srcPath, projDir, 'MyApp.vcxproj'),
to: path.join(appFolder, c.runtime.appId, "".concat(c.runtime.appId, ".vcxproj")),
},
{
from: path.join(srcPath, projDir, 'MyApp.vcxproj.filters'),
to: path.join(appFolder, c.runtime.appId, "".concat(c.runtime.appId, ".vcxproj.filters")),
},
{
from: path.join(srcPath, projDir, 'packages.config'),
to: path.join(appFolder, c.runtime.appId, 'packages.config'),
},
];
_b = 0, cppMappings_1 = cppMappings;
_d.label = 14;
case 14:
if (!(_b < cppMappings_1.length)) return [3 /*break*/, 17];
mapping = cppMappings_1[_b];
return [4 /*yield*/, generator_common_1.copyAndReplaceWithChangedCallback(mapping.from, c.paths.project.dir, mapping.to, templateVars, options)];
case 15:
_d.sent();
_d.label = 16;
case 16:
_b++;
return [3 /*break*/, 14];
case 17:
if (!(options.experimentalNuGetDependency || rnVersion > 0.64)) return [3 /*break*/, 21];
if (!fs.existsSync(path.join(sharedPath, projDir))) return [3 /*break*/, 21];
sharedProjMappings = [];
sharedProjMappings.push({
from: path.join(sharedPath, projDir, 'NuGet.Config'),
to: path.join(appFolder, 'NuGet.Config'),
});
if (fs.existsSync(path.join(sharedPath, projDir, 'ExperimentalFeatures.props'))) {
sharedProjMappings.push({
from: path.join(sharedPath, projDir, 'ExperimentalFeatures.props'),
to: path.join(appFolder, 'ExperimentalFeatures.props'),
});
}
_c = 0, sharedProjMappings_1 = sharedProjMappings;
_d.label = 18;
case 18:
if (!(_c < sharedProjMappings_1.length)) return [3 /*break*/, 21];
mapping = sharedProjMappings_1[_c];
return [4 /*yield*/, generator_common_1.copyAndReplaceWithChangedCallback(mapping.from, c.paths.project.dir, mapping.to, templateVars, options)];
case 19:
_d.sent();
_d.label = 20;
case 20:
_c++;
return [3 /*break*/, 18];
case 21:
// Firstly attempt to copy assets specified in project, if user has none specified use default from renative
return [4 /*yield*/, copyAssetsFolder(c.runtime.appId)];
case 22:
// Firstly attempt to copy assets specified in project, if user has none specified use default from renative
_d.sent();
// shared assets
if (fs.existsSync(path.join(sharedPath, 'assets'))) {
copyFolderContentsRecursiveSync(path.join(sharedPath, 'assets'), path.join(appFolder, c.runtime.appId, 'Assets'), true, false,
// Must not override, project defined assets used, if new ones are added later on - rebuild will cover it
true);
}
appFolderFull = getAppFolder();
// react native vector icons fonts
// Only copy the files if the plugin is added to the project, aka plugin dir exists
if (RNIconsPluginPath && fs.existsSync(RNIconsPluginPath)) {
// Default React Native Windows Debug apps use this location
copyFolderContentsRecursive(RNIconsPluginPath, path.join(appFolderFull, c.runtime.appId, 'Assets'));
// Default React Native Windows Release apps use this location
copyFolderContentsRecursive(RNIconsPluginPath, path.join(appFolderFull, c.runtime.appId, 'Bundle', 'assets'));
glyphmapsDir = path.join(appFolderFull, c.runtime.appId, 'Assets', 'node_modules', 'react-native-vector-icons', 'glyphmaps');
if (!fs.existsSync(glyphmapsDir)) {
fs.mkdirSync(glyphmapsDir, { recursive: true });
}
RNIconsGlyphmapsPluginPath = path.join(path.dirname(require.resolve('react-native-vector-icons/package.json', {
paths: [c.paths.project.dir],
})), 'glyphmaps');
copyFolderContentsRecursive(RNIconsGlyphmapsPluginPath, glyphmapsDir);
}
if (!fs.existsSync(path.join(sharedPath, 'src'))) return [3 /*break*/, 24];
return [4 /*yield*/, generator_common_1.copyAndReplaceAll(path.join(sharedPath, 'src'), c.paths.project.dir, path.join(appFolder, c.runtime.appId), templateVars, options)];
case 23:
_d.sent();
_d.label = 24;
case 24:
if (!fs.existsSync(path.join(srcPath, 'src'))) return [3 /*break*/, 26];
return [4 /*yield*/, generator_common_1.copyAndReplaceAll(path.join(srcPath, 'src'), c.paths.project.dir, path.join(appFolder, c.runtime.appId), templateVars, options)];
case 25:
_d.sent();
_d.label = 26;
case 26: return [2 /*return*/];
}
});
});
}
exports.copyProjectTemplateAndReplace = copyProjectTemplateAndReplace;
function toCppNamespace(namespace) {
return namespace.replace(/\./g, '::');
}
//# sourceMappingURL=copyTemplate.js.map