export async function copyProjectTemplateAndReplace()

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'));
  }
}