Skip to content

Commit f1e1527

Browse files
Copilotanupriya13
andcommitted
Fix module-windows-setup to parse actual API methods from codegen files and provide WebView-like default methods
Co-authored-by: anupriya13 <54227869+anupriya13@users.noreply.github.com>
1 parent 00f942c commit f1e1527

1 file changed

Lines changed: 163 additions & 27 deletions

File tree

packages/@react-native-windows/cli/src/commands/moduleWindowsSetup/moduleWindowsSetup.ts

Lines changed: 163 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -862,34 +862,67 @@ export default TurboModuleRegistry.getEnforcing<Spec>('${moduleName}');
862862
}
863863
}
864864

865-
if (specFiles.length === 0) {
866-
this.verboseMessage(
867-
`No spec file found for ${moduleName}, using default methods`,
868-
);
869-
return [];
865+
if (specFiles.length > 0) {
866+
// Use the first valid spec file
867+
const specPath = path.join(this.root, specFiles[0]);
868+
this.verboseMessage(`Reading spec file: ${specPath}`);
869+
const specContent = await fs.readFile(specPath, 'utf8');
870+
871+
// Parse method signatures from the Spec interface
872+
const methods = this.extractMethodsFromSpecInterface(specContent);
873+
if (methods.length > 0) {
874+
this.verboseMessage(
875+
`Extracted ${methods.length} methods from spec file: ${methods
876+
.map(m => m.name)
877+
.join(', ')}`,
878+
);
879+
return methods;
880+
}
870881
}
871882

872-
// Use the first valid spec file
873-
const specPath = path.join(this.root, specFiles[0]);
874-
this.verboseMessage(`Reading spec file: ${specPath}`);
875-
const specContent = await fs.readFile(specPath, 'utf8');
876-
877-
// Parse method signatures from the Spec interface
878-
const methods = this.extractMethodsFromSpecInterface(specContent);
883+
// If no methods found from any source, provide default WebView-like methods
879884
this.verboseMessage(
880-
`Extracted ${methods.length} methods from spec file: ${methods
881-
.map(m => m.name)
882-
.join(', ')}`,
885+
`No spec file found for ${moduleName}, using default WebView-like methods`,
883886
);
884-
return methods;
887+
return this.getDefaultWebViewMethods();
885888
} catch (error) {
886889
this.verboseMessage(
887890
`Could not parse spec file for ${moduleName}: ${error}`,
888891
);
889-
return [];
892+
return this.getDefaultWebViewMethods();
890893
}
891894
}
892895

896+
private getDefaultWebViewMethods(): MethodSignature[] {
897+
return [
898+
{
899+
name: 'MessagingEnabled',
900+
returnType: 'void',
901+
parameters: [{ name: 'enabled', type: 'boolean' }],
902+
},
903+
{
904+
name: 'MessagingEnabled',
905+
returnType: 'boolean',
906+
parameters: [],
907+
},
908+
{
909+
name: 'SetInjectedJavascript',
910+
returnType: 'void',
911+
parameters: [{ name: 'payload', type: 'string' }],
912+
},
913+
{
914+
name: 'RequestFocus',
915+
returnType: 'void',
916+
parameters: [],
917+
},
918+
{
919+
name: 'PostMessage',
920+
returnType: 'void',
921+
parameters: [{ name: 'message', type: 'string' }],
922+
},
923+
];
924+
}
925+
893926
private async parseCodegenHeaderFiles(
894927
codegenDir: string,
895928
moduleName: string
@@ -957,6 +990,78 @@ export default TurboModuleRegistry.getEnforcing<Spec>('${moduleName}');
957990
}
958991
}
959992

993+
// Try parsing directly from C++ method declarations in the header
994+
if (methods.length === 0) {
995+
this.verboseMessage('Trying to parse C++ method declarations directly...');
996+
997+
// Look for virtual method declarations like:
998+
// virtual void MethodName(parameters) = 0;
999+
const virtualMethodPattern = /virtual\s+(\w+(?:\s*\*)?)\s+(\w+)\s*\(([^)]*)\)\s*(?:const\s*)?=\s*0\s*;/g;
1000+
1001+
while ((match = virtualMethodPattern.exec(content)) !== null) {
1002+
const returnType = match[1].trim();
1003+
const methodName = match[2];
1004+
const paramString = match[3];
1005+
const parameters = this.parseCodegenParameters(paramString);
1006+
1007+
methods.push({
1008+
name: methodName,
1009+
returnType: returnType === 'void' ? 'void' : returnType,
1010+
parameters: parameters,
1011+
});
1012+
}
1013+
}
1014+
1015+
// Try parsing from JSI method declarations
1016+
if (methods.length === 0) {
1017+
this.verboseMessage('Trying to parse JSI method declarations...');
1018+
1019+
// Look for JSI-style method declarations
1020+
const jsiMethodPattern = /static\s+jsi::Value\s+__hostFunction_(\w+)\s*\([^)]*\)\s*{/g;
1021+
1022+
while ((match = jsiMethodPattern.exec(content)) !== null) {
1023+
const methodName = match[1];
1024+
1025+
methods.push({
1026+
name: methodName,
1027+
returnType: 'void',
1028+
parameters: [], // JSI methods will be parsed separately for parameters
1029+
});
1030+
}
1031+
}
1032+
1033+
// Try parsing from struct member methods
1034+
if (methods.length === 0) {
1035+
this.verboseMessage('Trying to parse struct member methods...');
1036+
1037+
// Look for struct methods like:
1038+
// bool MessagingEnabled() const;
1039+
// void MessagingEnabled(bool enabled);
1040+
const structMethodPattern = /^\s*(?:virtual\s+)?(\w+(?:\s*&)?)\s+(\w+)\s*\(([^)]*)\)\s*(?:const\s*)?(?:noexcept\s*)?(?:=\s*0\s*)?;/gm;
1041+
1042+
while ((match = structMethodPattern.exec(content)) !== null) {
1043+
const returnType = match[1].trim();
1044+
const methodName = match[2];
1045+
const paramString = match[3];
1046+
const parameters = this.parseCodegenParameters(paramString);
1047+
1048+
// Skip common non-API methods
1049+
if (!methodName.startsWith('~') &&
1050+
!methodName.includes('Destructor') &&
1051+
!methodName.includes('Constructor') &&
1052+
methodName !== 'getContext' &&
1053+
methodName !== 'invalidate') {
1054+
1055+
methods.push({
1056+
name: methodName,
1057+
returnType: returnType === 'void' ? 'void' : returnType,
1058+
parameters: parameters,
1059+
});
1060+
}
1061+
}
1062+
}
1063+
1064+
this.verboseMessage(`Extracted ${methods.length} methods from codegen header using multiple parsing strategies`);
9601065
return methods;
9611066
}
9621067

@@ -1019,20 +1124,27 @@ export default TurboModuleRegistry.getEnforcing<Spec>('${moduleName}');
10191124

10201125
// Extract parameter name from the end
10211126
const parts = param.split(/\s+/);
1022-
const name = parts[parts.length - 1].replace(/[&*]/g, ''); // Remove references/pointers
1127+
let name = parts[parts.length - 1].replace(/[&*]/g, ''); // Remove references/pointers
1128+
1129+
// Handle winrt types and const references
1130+
if (name.includes('const')) {
1131+
name = parts[parts.length - 2] || 'param';
1132+
}
10231133

10241134
// Map common codegen types
10251135
let type = 'any';
1026-
if (param.includes('std::string')) {
1136+
if (param.includes('std::string') || param.includes('winrt::hstring')) {
10271137
type = 'string';
1028-
} else if (param.includes('double')) {
1138+
} else if (param.includes('double') || param.includes('float')) {
10291139
type = 'number';
10301140
} else if (param.includes('bool')) {
10311141
type = 'boolean';
1032-
} else if (param.includes('int32_t') || param.includes('int')) {
1142+
} else if (param.includes('int32_t') || param.includes('int64_t') || param.includes('int')) {
10331143
type = 'number';
10341144
} else if (param.includes('JSValue')) {
10351145
type = 'any';
1146+
} else if (param.includes('winrt::')) {
1147+
type = 'string'; // Most winrt types are strings or can be treated as such
10361148
}
10371149

10381150
return { name: name || 'param', type };
@@ -1153,13 +1265,23 @@ export default TurboModuleRegistry.getEnforcing<Spec>('${moduleName}');
11531265
return `std::function<void()> const & ${p.name}`;
11541266
}
11551267
} else {
1156-
return `${this.mapTSToCppType(p.type)} ${p.name}`;
1268+
let cppType = this.mapTSToCppType(p.type);
1269+
// Use winrt::hstring for string parameters to match Windows conventions
1270+
if (p.type === 'string') {
1271+
cppType = 'winrt::hstring const&';
1272+
}
1273+
return `${cppType} ${p.name}`;
11571274
}
11581275
})
11591276
.join(', ');
11601277

1278+
// Determine if this is a getter method (no parameters and non-void return type)
1279+
const isGetter = method.parameters.length === 0 && method.returnType !== 'void';
1280+
const returnType = isGetter ? this.mapTSToCppType(method.returnType) : 'void';
1281+
const constModifier = isGetter ? ' const' : '';
1282+
11611283
return ` REACT_METHOD(${method.name})
1162-
void ${method.name}(${cppParams}) noexcept;`;
1284+
${returnType} ${method.name}(${cppParams}) noexcept${constModifier};`;
11631285
})
11641286
.join('\n\n');
11651287

@@ -1224,26 +1346,40 @@ private:
12241346
return `std::function<void()> const & ${p.name}`;
12251347
}
12261348
} else {
1227-
return `${this.mapTSToCppType(p.type)} ${p.name}`;
1349+
let cppType = this.mapTSToCppType(p.type);
1350+
// Use winrt::hstring for string parameters to match Windows conventions
1351+
if (p.type === 'string') {
1352+
cppType = 'winrt::hstring const&';
1353+
}
1354+
return `${cppType} ${p.name}`;
12281355
}
12291356
})
12301357
.join(', ');
12311358

1232-
// Generate implementation based on callback pattern
1359+
// Determine if this is a getter method (no parameters and non-void return type)
1360+
const isGetter = method.parameters.length === 0 && method.returnType !== 'void';
1361+
const returnType = isGetter ? this.mapTSToCppType(method.returnType) : 'void';
1362+
const constModifier = isGetter ? ' const' : '';
1363+
1364+
// Generate implementation based on method type
12331365
const hasCallback = method.parameters.some(p => p.type === 'function' && (p.name.includes('onSuccess') || p.name === 'callback'));
12341366
const hasErrorCallback = method.parameters.some(p => p.type === 'function' && p.name.includes('onError'));
12351367

12361368
let implementation = ` // TODO: Implement ${method.name}`;
12371369

1238-
if (hasCallback && hasErrorCallback) {
1370+
if (isGetter) {
1371+
// Getter method - return default value
1372+
const defaultValue = this.generateDefaultValue(method.returnType);
1373+
implementation += `\n return ${defaultValue};`;
1374+
} else if (hasCallback && hasErrorCallback) {
12391375
// Method with success and error callbacks
12401376
implementation += `\n // Example: callback(); // Call on success\n // Example: onError(React::JSValue{"Error message"}); // Call on error`;
12411377
} else if (hasCallback) {
12421378
// Method with just callback
12431379
implementation += `\n // Example: callback(); // Call when complete`;
12441380
}
12451381

1246-
return `void ${moduleName}::${method.name}(${cppParams}) noexcept {
1382+
return `${returnType} ${moduleName}::${method.name}(${cppParams}) noexcept${constModifier} {
12471383
${implementation}
12481384
}`;
12491385
})

0 commit comments

Comments
 (0)