Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "Fix init-windows C++ codegen include to use codegenConfig.name",
"packageName": "react-native-windows",
"email": "nisargakshetty@gmail.com",
"dependentChangeType": "none"
}
123 changes: 26 additions & 97 deletions vnext/templates/cpp-lib/template.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ const crypto = require('crypto');
const existsSync = require('fs').existsSync;
const path = require('path');
const username = require('username');
const util = require('util');

const glob = util.promisify(require('glob'));

const templateUtils = require('../templateUtils');

Expand Down Expand Up @@ -62,9 +59,6 @@ async function preInstall(config = {}, options = {}) {
const {exExists, exConfig, exOptions} = resolveArgs(config, options);

if (exExists) {
if (exOptions?.logging) {
console.log('Running cpp-app template preInstall() for example...');
}
await exampleTemplateConfig.preInstall(exConfig, exOptions);
}
}
Expand All @@ -91,44 +85,34 @@ async function getFileMappings(config = {}, options = {}) {
libConfig?.project?.windows?.projects[0]?.projectGuid
?.replace('{', '')
.replace('}', '') ?? crypto.randomUUID();
const currentUser = username.sync(); // Gets the current username depending on the platform.

// Check for existing codegen spec files
const codegenPath = path.join(projectRoot, 'windows', projectName, 'codegen');
let existingSpecFiles = [];
let firstSpecName = null;
if (existsSync(codegenPath)) {
try {
const specFiles = await glob('*Spec.g.h', {cwd: codegenPath});
existingSpecFiles = specFiles;
if (specFiles.length > 0) {
// Extract the spec name from filename (e.g., "NativeMyModuleSpec.g.h" -> "MyModuleSpec")
const firstFile = specFiles[0];
firstSpecName = firstFile.replace(/^Native/, '').replace(/\.g\.h$/, '');
}
} catch (e) {
// If we can't read the codegen directory, continue with empty array
}
const currentUser = username.sync();

const pkgJsonPath = path.join(projectRoot, 'package.json');
if (!existsSync(pkgJsonPath)) {
throw new Error('package.json not found in user library root');
}

const pkgJson = require(pkgJsonPath);

if (!pkgJson.codegenConfig?.name) {
throw new Error(
'codegenConfig.name is required in package.json for C++ libraries',
);
}

const cppNugetPackages = [];
const codegenName = pkgJson.codegenConfig.name;

const replacements = {
useMustache: true,
regExpPatternsToRemove: [],

name: projectName,
pascalName: templateUtils.pascalCase(projectName),
namespace: namespace,
namespaceCpp: namespaceCpp,

// Codegen spec files information
existingSpecFiles: existingSpecFiles,
hasExistingSpecFiles: existingSpecFiles.length > 0,
firstSpecFile: existingSpecFiles.length > 0 ? existingSpecFiles[0] : null,
firstSpecName: firstSpecName,
namespace,
namespaceCpp,
codegenName,

rnwVersion: rnwVersion,
rnwVersion,
rnwPathFromProjectRoot: path
.relative(projectRoot, rnwPath)
.replace(/\//g, '\\'),
Expand All @@ -140,27 +124,21 @@ async function getFileMappings(config = {}, options = {}) {
projectGuidUpper: `{${projectGuid.toUpperCase()}}`,

currentUser,

devMode,

useNuGets: !devMode, // default is to use published NuGets except in devMode, change to true here if you want to test devMode and nugets simultaneously
addReactNativePublicAdoFeed: true || isCanary, // Temporary true for all new projects until code-signing is restored, see issue #14030

cppNugetPackages,
cppNugetPackages: [],
};

let fileMappings = [];

const templateFiles = await glob('**/*', {
cwd: __dirname,
ignore: 'template.config.js',
nodir: true,
});
const templateFiles = templateUtils.getTemplateFiles(__dirname);

for (const file of templateFiles) {
const fileMapping = {
from: path.resolve(__dirname, path.normalize(file)),
to: path.normalize(file),
from: file.from,
to: file.to.replace(/MyLib/g, projectName),
replacements,
};

Expand All @@ -169,27 +147,9 @@ async function getFileMappings(config = {}, options = {}) {
continue;
}

// Perform simple file renames
const fileName = path.basename(fileMapping.to);
switch (fileName) {
case '_gitignore':
fileMapping.to = path.join(path.dirname(fileMapping.to), '.gitignore');
break;
case 'NuGet_Config':
fileMapping.to = path.join(
path.dirname(fileMapping.to),
'NuGet.config',
);
break;
}

// Rename files with MyLib in the name
fileMapping.to = fileMapping.to.replace(/MyLib/g, projectName);

fileMappings.push(fileMapping);
}

// Add the file mappings from the cpp-app template for the example app
if (exExists) {
const exampleFileMappings = await exampleTemplateConfig.getFileMappings(
exConfig,
Expand All @@ -198,27 +158,21 @@ async function getFileMappings(config = {}, options = {}) {

for (const exFileMap of exampleFileMappings) {
exFileMap.to = path.join('example', exFileMap.to);

// Only add the file map if there isn't a mapping from this cpp-lib template
if (fileMappings.filter(fm => fm.to === exFileMap.to).length === 0) {
fileMappings.push(exFileMap);
}
fileMappings.push(exFileMap);
}
}

return fileMappings;
}

async function postInstall(config = {}, options = {}) {
const {libConfig, libOptions, exExists, exConfig, exOptions} = resolveArgs(
config,
options,
);
const {libConfig, libOptions} = resolveArgs(config, options);

const projectName =
libConfig?.project?.windows?.projects[0]?.projectName ??
libOptions?.name ??
'MyLib';

const namespace = libOptions?.namespace ?? projectName;
const namespaceCpp = namespace.replace(/\./g, '::');

Expand All @@ -229,7 +183,7 @@ async function postInstall(config = {}, options = {}) {
{
codegenConfig: {
windows: {
namespace: namespaceCpp + 'Codegen',
namespace: `${namespaceCpp}Codegen`,
outputDirectory: `windows/${projectName}/codegen`,
separateDataTypes: true,
},
Expand All @@ -238,31 +192,6 @@ async function postInstall(config = {}, options = {}) {
true, // save options from command
true, // if a "files" property exists, make sure "windows" is included
);

if (exExists) {
const {rnwVersion} = templateUtils.getRnwInfo(exConfig, exOptions);

// Update example package.json with new scripts and dependencies
await templateUtils.updateProjectPackageJson(exConfig, exOptions, {
scripts: {
windows: 'npx @react-native-community/cli run-windows',
'test:windows': 'jest --config jest.config.windows.js',
},
dependencies: {
'react-native-windows': rnwVersion,
},
devDependencies: {
'@rnx-kit/jest-preset': '^0.1.17',
},
});

// Install recently added dependencies
await templateUtils.runNpmInstall(libConfig, libOptions);

console.log(
'\nRun the example app on Windows:\n\n > yarn example windows\n',
);
}
}

module.exports = {
Expand Down
31 changes: 9 additions & 22 deletions vnext/templates/cpp-lib/windows/MyLib/MyLib.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,14 @@
#include "pch.h"
#include "resource.h"

#if __has_include("codegen/Native{{ pascalName }}DataTypes.g.h")
#include "codegen/Native{{ pascalName }}DataTypes.g.h"
#if __has_include("codegen/Native{{ codegenName }}DataTypes.g.h")
#include "codegen/Native{{ codegenName }}DataTypes.g.h"
#endif

// Note: The following lines use Mustache template syntax which will be processed during
// project generation to produce standard C++ code. If existing codegen spec files are found,
// use the actual filename; otherwise use conditional includes.
{{#hasExistingSpecFiles}}
#include "codegen/{{ firstSpecFile }}"
{{/hasExistingSpecFiles}}
{{^hasExistingSpecFiles}}
#if __has_include("codegen/Native{{ pascalName }}Spec.g.h")
#include "codegen/Native{{ pascalName }}Spec.g.h"
#endif
{{/hasExistingSpecFiles}}
// project generation to produce standard C++ code. The spec filename is derived from
// `codegenConfig.name` in the user library's package.json, which is the single source of truth.
#include "codegen/Native{{ codegenName }}Spec.g.h"

#include "NativeModules.h"

Expand All @@ -29,15 +23,8 @@ REACT_MODULE({{ pascalName }})
struct {{ pascalName }}
{
// Note: Mustache template syntax below will be processed during project generation
// to produce standard C++ code based on detected codegen files.
{{#hasExistingSpecFiles}}
using ModuleSpec = {{ namespaceCpp }}Codegen::{{ firstSpecName }};
{{/hasExistingSpecFiles}}
{{^hasExistingSpecFiles}}
#if __has_include("codegen/Native{{ pascalName }}Spec.g.h")
using ModuleSpec = {{ namespaceCpp }}Codegen::{{ pascalName }}Spec;
#endif
{{/hasExistingSpecFiles}}
// to produce standard C++ code based on the codegenConfig.name value.
using ModuleSpec = {{ namespaceCpp }}Codegen::{{ codegenName }}Spec;

REACT_INIT(Initialize)
void Initialize(React::ReactContext const &reactContext) noexcept;
Expand All @@ -49,4 +36,4 @@ struct {{ pascalName }}
React::ReactContext m_context;
};

} // namespace winrt::{{ namespaceCpp }}
} // namespace winrt::{{ namespaceCpp }}
Loading