Skip to content

Commit 3cd97e4

Browse files
tarunrajputfacebook-github-bot
authored andcommitted
extract buildModuleSchema to parsers-commons (#36330)
Summary: > Extract the buildModuleSchema function ([Flow](https://github.com/facebook/react-native/blob/main/packages/react-native-codegen/src/parsers/flow/modules/index.js#L571), [TypeScript](https://github.com/facebook/react-native/blob/main/packages/react-native-codegen/src/parsers/typescript/modules/index.js#L584))in the parsers-commons.js function. The two functions are almost identical except for the filter(property =>) at the end of the function, which is different based on the language. Part of Codegen Issue #34872 Depends on Codegen 74, Codegen 84. ## Changelog <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests --> [INTERNAL] [CHANGED] - Extract buildModuleSchema from Flow and TypeScript parsers modules to parsers-commons Pull Request resolved: #36330 Test Plan: `yarn lint && yarn run flow && yarn test react-native-codegen ` Reviewed By: christophpurrer Differential Revision: D43833653 Pulled By: cipolleschi fbshipit-source-id: 4d67cb5ef746ecd7ace4b91eb30d36e96252e779
1 parent e68f513 commit 3cd97e4

File tree

7 files changed

+402
-250
lines changed

7 files changed

+402
-250
lines changed

packages/eslint-plugin-specs/react-native-modules.js

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,17 @@ const ERRORS = {
2424
let RNModuleParser;
2525
let RNParserUtils;
2626
let RNFlowParser;
27+
let RNParserCommons;
28+
let RNFlowParserUtils;
2729

2830
function requireModuleParser() {
29-
if (RNModuleParser == null || RNParserUtils == null || RNFlowParser == null) {
31+
if (
32+
RNModuleParser == null ||
33+
RNParserUtils == null ||
34+
RNFlowParser == null ||
35+
RNParserCommons == null ||
36+
RNFlowParserUtils == null
37+
) {
3038
// If using this externally, we leverage @react-native/codegen as published form
3139
if (!PACKAGE_USAGE) {
3240
const config = {
@@ -38,6 +46,8 @@ function requireModuleParser() {
3846
RNModuleParser = require('@react-native/codegen/src/parsers/flow/modules');
3947
RNParserUtils = require('@react-native/codegen/src/parsers/utils');
4048
RNFlowParser = require('@react-native/codegen/src/parsers/flow/parser');
49+
RNParserCommons = require('@react-native/codegen/src/parsers/parsers-commons');
50+
RNFlowParserUtils = require('@react-native/codegen/src/parsers/flow/utils');
4151
});
4252
} else {
4353
const config = {
@@ -47,16 +57,20 @@ function requireModuleParser() {
4757

4858
withBabelRegister(config, () => {
4959
RNModuleParser = require('@react-native/codegen/lib/parsers/flow/modules');
50-
RNParserUtils = require('@react-native/codegen/lib/parsers/flow/utils');
60+
RNParserUtils = require('@react-native/codegen/lib/parsers/utils');
5161
RNFlowParser = require('@react-native/codegen/lib/parsers/flow/parser');
62+
RNParserCommons = require('@react-native/codegen/lib/parsers/parsers-commons');
63+
RNFlowParserUtils = require('@react-native/codegen/lib/parsers/flow/utils');
5264
});
5365
}
5466
}
5567

5668
return {
57-
buildModuleSchema: RNModuleParser.buildModuleSchema,
69+
buildModuleSchema: RNParserCommons.buildModuleSchema,
5870
createParserErrorCapturer: RNParserUtils.createParserErrorCapturer,
5971
parser: new RNFlowParser.FlowParser(),
72+
resolveTypeAnnotation: RNFlowParserUtils.resolveTypeAnnotation,
73+
translateTypeAnnotation: RNModuleParser.flowTranslateTypeAnnotation,
6074
};
6175
}
6276

@@ -131,16 +145,28 @@ function rule(context) {
131145
});
132146
}
133147

134-
const {buildModuleSchema, createParserErrorCapturer, parser} =
135-
requireModuleParser();
148+
const {
149+
buildModuleSchema,
150+
createParserErrorCapturer,
151+
parser,
152+
resolveTypeAnnotation,
153+
translateTypeAnnotation,
154+
} = requireModuleParser();
136155

137156
const [parsingErrors, tryParse] = createParserErrorCapturer();
138157

139158
const sourceCode = context.getSourceCode().getText();
140159
const ast = parser.getAst(sourceCode);
141160

142161
tryParse(() => {
143-
buildModuleSchema(hasteModuleName, ast, tryParse, parser);
162+
buildModuleSchema(
163+
hasteModuleName,
164+
ast,
165+
tryParse,
166+
parser,
167+
resolveTypeAnnotation,
168+
translateTypeAnnotation,
169+
);
144170
});
145171

146172
parsingErrors.forEach(error => {

packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js

Lines changed: 228 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@ import type {ParserType} from '../errors';
2626
const {Visitor} = require('../flow/Visitor');
2727
const {wrapComponentSchema} = require('../schema.js');
2828
const {buildComponentSchema} = require('../flow/components');
29-
const {buildModuleSchema} = require('../flow/modules');
30-
const {isModuleRegistryCall} = require('../utils.js');
29+
const {buildModuleSchema} = require('../parsers-commons.js');
30+
const {
31+
isModuleRegistryCall,
32+
createParserErrorCapturer,
33+
} = require('../utils.js');
3134
const {
3235
ParserError,
3336
UnsupportedObjectPropertyTypeAnnotationParserError,
@@ -36,6 +39,9 @@ const {
3639
IncorrectModuleRegistryCallArityParserError,
3740
IncorrectModuleRegistryCallArgumentTypeParserError,
3841
UntypedModuleRegistryCallParserError,
42+
ModuleInterfaceNotFoundParserError,
43+
MoreThanOneModuleInterfaceParserError,
44+
MisnamedModuleInterfaceParserError,
3945
} = require('../errors');
4046

4147
import {MockedParser} from '../parserMock';
@@ -45,8 +51,9 @@ const parser = new MockedParser();
4551

4652
const flowParser = new FlowParser();
4753

48-
const flowTranslateTypeAnnotation = require('../flow/modules/index');
54+
const {flowTranslateTypeAnnotation} = require('../flow/modules/index');
4955
const typeScriptTranslateTypeAnnotation = require('../typescript/modules/index');
56+
const {resolveTypeAnnotation} = require('../flow/utils');
5057

5158
beforeEach(() => {
5259
jest.clearAllMocks();
@@ -396,7 +403,9 @@ describe('buildSchemaFromConfigType', () => {
396403
const buildComponentSchemaMock = jest.fn(
397404
(_ast, _parser) => componentSchemaMock,
398405
);
399-
const buildModuleSchemaMock = jest.fn((_0, _1, _2, _3) => moduleSchemaMock);
406+
const buildModuleSchemaMock = jest.fn(
407+
(_0, _1, _2, _3, _4, _5) => moduleSchemaMock,
408+
);
400409

401410
const buildSchemaFromConfigTypeHelper = (
402411
configType: 'module' | 'component' | 'none',
@@ -410,6 +419,8 @@ describe('buildSchemaFromConfigType', () => {
410419
buildComponentSchemaMock,
411420
buildModuleSchemaMock,
412421
parser,
422+
resolveTypeAnnotation,
423+
flowTranslateTypeAnnotation,
413424
);
414425

415426
describe('when configType is none', () => {
@@ -491,6 +502,8 @@ describe('buildSchemaFromConfigType', () => {
491502
astMock,
492503
expect.any(Function),
493504
parser,
505+
resolveTypeAnnotation,
506+
flowTranslateTypeAnnotation,
494507
);
495508

496509
expect(buildComponentSchemaMock).not.toHaveBeenCalled();
@@ -661,6 +674,8 @@ describe('buildSchema', () => {
661674
buildModuleSchema,
662675
Visitor,
663676
parser,
677+
resolveTypeAnnotation,
678+
flowTranslateTypeAnnotation,
664679
);
665680

666681
expect(getConfigTypeSpy).not.toHaveBeenCalled();
@@ -693,6 +708,8 @@ describe('buildSchema', () => {
693708
buildModuleSchema,
694709
Visitor,
695710
flowParser,
711+
resolveTypeAnnotation,
712+
flowTranslateTypeAnnotation,
696713
);
697714

698715
expect(getConfigTypeSpy).toHaveBeenCalledTimes(1);
@@ -746,6 +763,8 @@ describe('buildSchema', () => {
746763
buildModuleSchema,
747764
Visitor,
748765
flowParser,
766+
resolveTypeAnnotation,
767+
flowTranslateTypeAnnotation,
749768
);
750769

751770
expect(getConfigTypeSpy).toHaveBeenCalledTimes(1);
@@ -1007,3 +1026,208 @@ describe('parseModuleName', () => {
10071026
});
10081027
});
10091028
});
1029+
1030+
describe('buildModuleSchema', () => {
1031+
const hasteModuleName = 'TestModuleName';
1032+
const [, tryParse] = createParserErrorCapturer();
1033+
const language = flowParser.language();
1034+
const NATIVE_MODULE = `
1035+
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
1036+
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
1037+
1038+
export interface Spec extends TurboModule {
1039+
+getArray: (a: Array<any>) => Array<string>;
1040+
}
1041+
1042+
export default (TurboModuleRegistry.getEnforcing<Spec>(
1043+
'SampleTurboModule',
1044+
): Spec);
1045+
`;
1046+
1047+
describe('throwIfModuleInterfaceNotFound', () => {
1048+
it('should throw ModuleInterfaceNotFoundParserError if no module interface is found', () => {
1049+
const ast = flowParser.getAst('');
1050+
const expected = new ModuleInterfaceNotFoundParserError(
1051+
hasteModuleName,
1052+
ast,
1053+
language,
1054+
);
1055+
1056+
expect(() =>
1057+
buildModuleSchema(
1058+
hasteModuleName,
1059+
ast,
1060+
tryParse,
1061+
flowParser,
1062+
resolveTypeAnnotation,
1063+
flowTranslateTypeAnnotation,
1064+
),
1065+
).toThrow(expected);
1066+
});
1067+
1068+
it('should not throw ModuleInterfaceNotFoundParserError if module interface is found', () => {
1069+
const ast = flowParser.getAst(NATIVE_MODULE);
1070+
1071+
expect(() =>
1072+
buildModuleSchema(
1073+
hasteModuleName,
1074+
ast,
1075+
tryParse,
1076+
flowParser,
1077+
resolveTypeAnnotation,
1078+
flowTranslateTypeAnnotation,
1079+
),
1080+
).not.toThrow();
1081+
});
1082+
});
1083+
1084+
describe('throwIfMoreThanOneModuleInterfaceParser', () => {
1085+
it('should throw an error if mulitple module interfaces are found', () => {
1086+
const contents = `
1087+
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
1088+
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
1089+
1090+
export interface Spec extends TurboModule {
1091+
+getBool: (arg: boolean) => boolean; }
1092+
export interface SpecOther extends TurboModule {
1093+
+getArray: (a: Array<any>) => Array<string>;
1094+
}
1095+
`;
1096+
const ast = flowParser.getAst(contents);
1097+
const types = flowParser.getTypes(ast);
1098+
const moduleSpecs = Object.values(types).filter(t =>
1099+
flowParser.isModuleInterface(t),
1100+
);
1101+
const expected = new MoreThanOneModuleInterfaceParserError(
1102+
hasteModuleName,
1103+
moduleSpecs,
1104+
moduleSpecs.map(node => node.id.name),
1105+
language,
1106+
);
1107+
1108+
expect(() =>
1109+
buildModuleSchema(
1110+
hasteModuleName,
1111+
ast,
1112+
tryParse,
1113+
flowParser,
1114+
resolveTypeAnnotation,
1115+
flowTranslateTypeAnnotation,
1116+
),
1117+
).toThrow(expected);
1118+
});
1119+
1120+
it('should not throw an error if exactly one module interface is found', () => {
1121+
const ast = flowParser.getAst(NATIVE_MODULE);
1122+
1123+
expect(() =>
1124+
buildModuleSchema(
1125+
hasteModuleName,
1126+
ast,
1127+
tryParse,
1128+
flowParser,
1129+
resolveTypeAnnotation,
1130+
flowTranslateTypeAnnotation,
1131+
),
1132+
).not.toThrow();
1133+
});
1134+
});
1135+
1136+
describe('throwIfModuleInterfaceIsMisnamed', () => {
1137+
it('should throw an error if module interface is misnamed', () => {
1138+
const contents = `
1139+
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
1140+
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
1141+
1142+
export interface MisnamedSpec extends TurboModule {
1143+
+getArray: (a: Array<any>) => Array<string>;
1144+
}
1145+
1146+
export default (TurboModuleRegistry.getEnforcing<Spec>(
1147+
'SampleTurboModule',
1148+
): Spec);
1149+
`;
1150+
const ast = flowParser.getAst(contents);
1151+
const types = flowParser.getTypes(ast);
1152+
const moduleSpecs = Object.values(types).filter(t =>
1153+
flowParser.isModuleInterface(t),
1154+
);
1155+
const [moduleSpec] = moduleSpecs;
1156+
1157+
const expected = new MisnamedModuleInterfaceParserError(
1158+
hasteModuleName,
1159+
moduleSpec.id,
1160+
language,
1161+
);
1162+
1163+
expect(() =>
1164+
buildModuleSchema(
1165+
hasteModuleName,
1166+
ast,
1167+
tryParse,
1168+
flowParser,
1169+
resolveTypeAnnotation,
1170+
flowTranslateTypeAnnotation,
1171+
),
1172+
).toThrow(expected);
1173+
});
1174+
1175+
it('should not throw an error if module interface is correctly named', () => {
1176+
const ast = flowParser.getAst(NATIVE_MODULE);
1177+
1178+
expect(() =>
1179+
buildModuleSchema(
1180+
hasteModuleName,
1181+
ast,
1182+
tryParse,
1183+
flowParser,
1184+
resolveTypeAnnotation,
1185+
flowTranslateTypeAnnotation,
1186+
),
1187+
).not.toThrow();
1188+
});
1189+
});
1190+
1191+
it('should return valid module schema', () => {
1192+
const ast = flowParser.getAst(NATIVE_MODULE);
1193+
const schmeaMock = {
1194+
aliasMap: {},
1195+
enumMap: {},
1196+
excludedPlatforms: undefined,
1197+
moduleName: 'SampleTurboModule',
1198+
spec: {
1199+
properties: [
1200+
{
1201+
name: 'getArray',
1202+
optional: false,
1203+
typeAnnotation: {
1204+
params: [
1205+
{
1206+
name: 'a',
1207+
optional: false,
1208+
typeAnnotation: {type: 'ArrayTypeAnnotation'},
1209+
},
1210+
],
1211+
returnTypeAnnotation: {
1212+
elementType: {type: 'StringTypeAnnotation'},
1213+
type: 'ArrayTypeAnnotation',
1214+
},
1215+
type: 'FunctionTypeAnnotation',
1216+
},
1217+
},
1218+
],
1219+
},
1220+
type: 'NativeModule',
1221+
};
1222+
const schema = buildModuleSchema(
1223+
hasteModuleName,
1224+
ast,
1225+
tryParse,
1226+
flowParser,
1227+
resolveTypeAnnotation,
1228+
flowTranslateTypeAnnotation,
1229+
);
1230+
1231+
expect(schema).toEqual(schmeaMock);
1232+
});
1233+
});

0 commit comments

Comments
 (0)