in packages/@react-native-windows/cli/src/generator-windows/index.ts [57:476]
export async function copyProjectTemplateAndReplace(
srcRootPath: string,
destPath: string,
newProjectName: string,
namespace: string,
options: GenerateOptions,
) {
if (!srcRootPath) {
throw new CodedError(
'CopyProjectTemplateNoSourcePath',
'Need a path to copy from',
);
}
if (!destPath) {
throw new CodedError(
'CopyProjectTemplateNoDestPath',
'Need a path to copy to',
);
}
if (!newProjectName) {
throw new CodedError(
'CopyProjectTemplateNoProjectName',
'Need a project name',
);
}
const projectType = options.projectType;
// React-native init only allows alphanumerics in project names, but other
// new project tools (like create-react-native-module) are less strict.
if (projectType === 'lib') {
newProjectName = pascalCase(newProjectName);
}
// Similar to the above, but we want to retain namespace separators
if (projectType === 'lib') {
namespace = namespace.split(/[.:]+/).map(pascalCase).join('.');
}
createDir(path.join(destPath, windowsDir));
createDir(path.join(destPath, windowsDir, newProjectName));
if (projectType === 'app') {
createDir(path.join(destPath, windowsDir, newProjectName, bundleDir));
createDir(path.join(destPath, windowsDir, newProjectName, 'BundleBuilder'));
}
const language = options.language;
const namespaceCpp = toCppNamespace(namespace);
if (options.experimentalNuGetDependency) {
console.log('Using experimental NuGet dependency.');
}
if (options.useWinUI3) {
console.log('Using experimental WinUI3 dependency.');
}
const projDir = 'proj';
const srcPath = path.join(srcRootPath, `${language}-${projectType}`);
const sharedPath = path.join(srcRootPath, `shared-${projectType}`);
const projectGuid = uuid.v4();
const rnwVersion = require(resolveRnwPath('package.json')).version;
const nugetVersion = options.nuGetTestVersion || rnwVersion;
const packageGuid = uuid.v4();
const currentUser = username.sync()!; // Gets the current username depending on the platform.
let mainComponentName = newProjectName;
const appJsonPath = await findUp('app.json', {cwd: destPath});
if (appJsonPath) {
const appJson = await fs.readJsonFile<{name: string}>(appJsonPath);
mainComponentName = appJson.name;
}
const xamlNamespace = options.useWinUI3
? 'Microsoft.UI.Xaml'
: 'Windows.UI.Xaml';
const xamlNamespaceCpp = toCppNamespace(xamlNamespace);
const winuiPropsPath = resolveRnwPath('PropertySheets/WinUI.props');
const winuiProps = readProjectFile(winuiPropsPath);
const winui3Version = findPropertyValue(
winuiProps,
'WinUI3Version',
winuiPropsPath,
);
const winui2xVersion = findPropertyValue(
winuiProps,
'WinUI2xVersion',
winuiPropsPath,
);
const jsEnginePropsPath = resolveRnwPath('PropertySheets/JSengine.props');
const hermesVersion = findPropertyValue(
readProjectFile(jsEnginePropsPath),
'HermesVersion',
jsEnginePropsPath,
);
const csNugetPackages: NugetPackage[] = [
{
id: 'Microsoft.NETCore.UniversalWindowsPlatform',
version: '6.2.9',
},
{
id: 'ReactNative.Hermes.Windows',
version: hermesVersion,
},
];
const cppNugetPackages: NugetPackage[] = [
{
id: 'Microsoft.Windows.CppWinRT',
version: '2.0.211028.7',
},
{
id: 'ReactNative.Hermes.Windows',
version: hermesVersion,
},
];
if (options.experimentalNuGetDependency) {
csNugetPackages.push({
id: 'Microsoft.ReactNative.Managed',
version: nugetVersion,
});
cppNugetPackages.push({
id: 'Microsoft.ReactNative',
version: nugetVersion,
});
cppNugetPackages.push({
id: 'Microsoft.ReactNative.Cxx',
version: nugetVersion,
});
}
const packagesConfigCppNugetPackages = [
...cppNugetPackages,
{
id: options.useWinUI3 ? 'Microsoft.WinUI' : 'Microsoft.UI.Xaml',
version: options.useWinUI3 ? winui3Version : winui2xVersion,
},
];
const templateVars: Record<string, any> = {
useMustache: true,
regExpPatternsToRemove: [],
name: newProjectName,
namespace: namespace,
namespaceCpp: namespaceCpp,
languageIsCpp: language === 'cpp',
rnwVersion: await getVersionOfNpmPackage('react-native-windows'),
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: `{${projectGuid.toLowerCase()}}`,
projectGuidUpper: `{${projectGuid.toUpperCase()}}`,
// packaging and signing variables:
packageGuid: packageGuid,
currentUser: currentUser,
useExperimentalNuget: options.experimentalNuGetDependency,
nuGetTestFeed: options.nuGetTestFeed,
nuGetADOFeed: nugetVersion.startsWith('0.0.0-'),
// cpp template variables
useWinUI3: options.useWinUI3,
useHermes: options.useHermes,
xamlNamespace: xamlNamespace,
xamlNamespaceCpp: xamlNamespaceCpp,
cppNugetPackages: cppNugetPackages,
packagesConfigCppNugetPackages: packagesConfigCppNugetPackages,
// cs template variables
csNugetPackages: csNugetPackages,
// autolinking template variables
autolinkPropertiesForProps: '',
autolinkProjectReferencesForTargets: '',
autolinkCsUsingNamespaces: '',
autolinkCsReactPackageProviders: '',
autolinkCppIncludes: '',
autolinkCppPackageProviders:
'\n UNREFERENCED_PARAMETER(packageProviders);', // CODESYNC: vnext\local-cli\runWindows\utils\autolink.js
};
const commonMappings =
projectType === 'app'
? [
// app common mappings
{
from: path.join(
srcRootPath,
options.useDevMode
? 'metro.devMode.config.js'
: 'metro.config.js',
),
to: 'metro.config.js',
},
{
from: path.join(srcRootPath, '_gitignore'),
to: path.join(windowsDir, '.gitignore'),
},
{
from: path.join(srcRootPath, 'b_gitignore'),
to: path.join(windowsDir, newProjectName, '.gitignore'),
},
{
from: path.join(srcRootPath, 'index.windows.bundle'),
to: path.join(
windowsDir,
newProjectName,
bundleDir,
'index.windows.bundle',
),
},
{
from: path.join(srcPath, projDir, 'MyApp.sln'),
to: path.join(windowsDir, newProjectName + '.sln'),
},
]
: [
// lib common mappings
{
from: path.join(srcRootPath, '_gitignore'),
to: path.join(windowsDir, '.gitignore'),
},
{
from: path.join(srcPath, projDir, 'MyLib.sln'),
to: path.join(windowsDir, newProjectName + '.sln'),
},
];
for (const mapping of commonMappings) {
await copyAndReplaceWithChangedCallback(
mapping.from,
destPath,
mapping.to,
templateVars,
options.overwrite,
);
}
if (language === 'cs') {
const csMappings =
projectType === 'app'
? [
// cs app mappings
{
from: path.join(srcPath, projDir, 'MyApp.csproj'),
to: path.join(
windowsDir,
newProjectName,
newProjectName + '.csproj',
),
},
]
: [
// cs lib mappings
{
from: path.join(srcPath, projDir, 'MyLib.csproj'),
to: path.join(
windowsDir,
newProjectName,
newProjectName + '.csproj',
),
},
];
csMappings.push({
from: path.join(srcPath, projDir, 'Directory.Build.props'),
to: path.join(windowsDir, 'Directory.Build.props'),
});
for (const mapping of csMappings) {
await copyAndReplaceWithChangedCallback(
mapping.from,
destPath,
mapping.to,
templateVars,
options.overwrite,
);
}
} else {
const cppMappings =
projectType === 'app'
? [
// cpp app mappings
{
from: path.join(srcPath, projDir, 'MyApp.vcxproj'),
to: path.join(
windowsDir,
newProjectName,
newProjectName + '.vcxproj',
),
},
{
from: path.join(srcPath, projDir, 'MyApp.vcxproj.filters'),
to: path.join(
windowsDir,
newProjectName,
newProjectName + '.vcxproj.filters',
),
},
]
: [
// cpp lib mappings
{
from: path.join(srcPath, projDir, 'MyLib.vcxproj'),
to: path.join(
windowsDir,
newProjectName,
newProjectName + '.vcxproj',
),
},
{
from: path.join(srcPath, projDir, 'MyLib.vcxproj.filters'),
to: path.join(
windowsDir,
newProjectName,
newProjectName + '.vcxproj.filters',
),
},
{
from: path.join(srcPath, projDir, 'MyLib.def'),
to: path.join(
windowsDir,
newProjectName,
newProjectName + '.def',
),
},
];
for (const mapping of cppMappings) {
await copyAndReplaceWithChangedCallback(
mapping.from,
destPath,
mapping.to,
templateVars,
options.overwrite,
);
}
}
// shared proj
if (fs.existsSync(path.join(sharedPath, projDir))) {
const sharedProjMappings = [];
sharedProjMappings.push({
from: path.join(sharedPath, projDir, 'NuGet.Config'),
to: path.join(windowsDir, 'NuGet.Config'),
});
if (
fs.existsSync(
path.join(sharedPath, projDir, 'ExperimentalFeatures.props'),
)
) {
sharedProjMappings.push({
from: path.join(sharedPath, projDir, 'ExperimentalFeatures.props'),
to: path.join(windowsDir, 'ExperimentalFeatures.props'),
});
}
for (const mapping of sharedProjMappings) {
await copyAndReplaceWithChangedCallback(
mapping.from,
destPath,
mapping.to,
templateVars,
options.overwrite,
);
}
}
// shared assets
if (fs.existsSync(path.join(sharedPath, 'assets'))) {
await copyAndReplaceAll(
path.join(sharedPath, 'assets'),
destPath,
path.join(windowsDir, newProjectName, 'Assets'),
templateVars,
options.overwrite,
);
}
// shared src
if (fs.existsSync(path.join(sharedPath, 'src'))) {
await copyAndReplaceAll(
path.join(sharedPath, 'src'),
destPath,
path.join(windowsDir, newProjectName),
templateVars,
options.overwrite,
);
}
// src
if (fs.existsSync(path.join(srcPath, 'src'))) {
await copyAndReplaceAll(
path.join(srcPath, 'src'),
destPath,
path.join(windowsDir, newProjectName),
templateVars,
options.overwrite,
);
}
if (projectType === 'app') {
console.log(chalk.white.bold('To run your app on UWP:'));
console.log(chalk.white(' npx react-native run-windows'));
}
}