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 https://github.com/facebook/react-native/issues/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: https://github.com/facebook/react-native/pull/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
This commit is contained in:
Tarun Chauhan authored and Facebook GitHub Bot committed 2023-03-10 07:58:32 -08:00
1 parent e68f513879
commit 3cd97e4994
7 files changed
+402 -250

No files matched your search

+32 -6
View File
@@ -24,9 +24,17 @@ const ERRORS = {
let RNModuleParser;
let RNParserUtils;
let RNFlowParser;
let RNParserCommons;
let RNFlowParserUtils;
function requireModuleParser() {
if (RNModuleParser == null || RNParserUtils == null || RNFlowParser == null) {
if (
RNModuleParser == null ||
RNParserUtils == null ||
RNFlowParser == null ||
RNParserCommons == null ||
RNFlowParserUtils == null
) {
// If using this externally, we leverage @react-native/codegen as published form
if (!PACKAGE_USAGE) {
const config = {
@@ -38,6 +46,8 @@ function requireModuleParser() {
RNModuleParser = require('@react-native/codegen/src/parsers/flow/modules');
RNParserUtils = require('@react-native/codegen/src/parsers/utils');
RNFlowParser = require('@react-native/codegen/src/parsers/flow/parser');
RNParserCommons = require('@react-native/codegen/src/parsers/parsers-commons');
RNFlowParserUtils = require('@react-native/codegen/src/parsers/flow/utils');
});
} else {
const config = {
@@ -47,16 +57,20 @@ function requireModuleParser() {
withBabelRegister(config, () => {
RNModuleParser = require('@react-native/codegen/lib/parsers/flow/modules');
RNParserUtils = require('@react-native/codegen/lib/parsers/flow/utils');
RNParserUtils = require('@react-native/codegen/lib/parsers/utils');
RNFlowParser = require('@react-native/codegen/lib/parsers/flow/parser');
RNParserCommons = require('@react-native/codegen/lib/parsers/parsers-commons');
RNFlowParserUtils = require('@react-native/codegen/lib/parsers/flow/utils');
});
}
}
return {
buildModuleSchema: RNModuleParser.buildModuleSchema,
buildModuleSchema: RNParserCommons.buildModuleSchema,
createParserErrorCapturer: RNParserUtils.createParserErrorCapturer,
parser: new RNFlowParser.FlowParser(),
resolveTypeAnnotation: RNFlowParserUtils.resolveTypeAnnotation,
translateTypeAnnotation: RNModuleParser.flowTranslateTypeAnnotation,
};
}
@@ -131,8 +145,13 @@ function rule(context) {
});
}
const {buildModuleSchema, createParserErrorCapturer, parser} =
requireModuleParser();
const {
buildModuleSchema,
createParserErrorCapturer,
parser,
resolveTypeAnnotation,
translateTypeAnnotation,
} = requireModuleParser();
const [parsingErrors, tryParse] = createParserErrorCapturer();
@@ -140,7 +159,14 @@ function rule(context) {
const ast = parser.getAst(sourceCode);
tryParse(() => {
buildModuleSchema(hasteModuleName, ast, tryParse, parser);
buildModuleSchema(
hasteModuleName,
ast,
tryParse,
parser,
resolveTypeAnnotation,
translateTypeAnnotation,
);
});
parsingErrors.forEach(error => {
@@ -26,8 +26,11 @@ import type {ParserType} from '../errors';
const {Visitor} = require('../flow/Visitor');
const {wrapComponentSchema} = require('../schema.js');
const {buildComponentSchema} = require('../flow/components');
const {buildModuleSchema} = require('../flow/modules');
const {isModuleRegistryCall} = require('../utils.js');
const {buildModuleSchema} = require('../parsers-commons.js');
const {
isModuleRegistryCall,
createParserErrorCapturer,
} = require('../utils.js');
const {
ParserError,
UnsupportedObjectPropertyTypeAnnotationParserError,
@@ -36,6 +39,9 @@ const {
IncorrectModuleRegistryCallArityParserError,
IncorrectModuleRegistryCallArgumentTypeParserError,
UntypedModuleRegistryCallParserError,
ModuleInterfaceNotFoundParserError,
MoreThanOneModuleInterfaceParserError,
MisnamedModuleInterfaceParserError,
} = require('../errors');
import {MockedParser} from '../parserMock';
@@ -45,8 +51,9 @@ const parser = new MockedParser();
const flowParser = new FlowParser();
const flowTranslateTypeAnnotation = require('../flow/modules/index');
const {flowTranslateTypeAnnotation} = require('../flow/modules/index');
const typeScriptTranslateTypeAnnotation = require('../typescript/modules/index');
const {resolveTypeAnnotation} = require('../flow/utils');
beforeEach(() => {
jest.clearAllMocks();
@@ -396,7 +403,9 @@ describe('buildSchemaFromConfigType', () => {
const buildComponentSchemaMock = jest.fn(
(_ast, _parser) => componentSchemaMock,
);
const buildModuleSchemaMock = jest.fn((_0, _1, _2, _3) => moduleSchemaMock);
const buildModuleSchemaMock = jest.fn(
(_0, _1, _2, _3, _4, _5) => moduleSchemaMock,
);
const buildSchemaFromConfigTypeHelper = (
configType: 'module' | 'component' | 'none',
@@ -410,6 +419,8 @@ describe('buildSchemaFromConfigType', () => {
buildComponentSchemaMock,
buildModuleSchemaMock,
parser,
resolveTypeAnnotation,
flowTranslateTypeAnnotation,
);
describe('when configType is none', () => {
@@ -491,6 +502,8 @@ describe('buildSchemaFromConfigType', () => {
astMock,
expect.any(Function),
parser,
resolveTypeAnnotation,
flowTranslateTypeAnnotation,
);
expect(buildComponentSchemaMock).not.toHaveBeenCalled();
@@ -661,6 +674,8 @@ describe('buildSchema', () => {
buildModuleSchema,
Visitor,
parser,
resolveTypeAnnotation,
flowTranslateTypeAnnotation,
);
expect(getConfigTypeSpy).not.toHaveBeenCalled();
@@ -693,6 +708,8 @@ describe('buildSchema', () => {
buildModuleSchema,
Visitor,
flowParser,
resolveTypeAnnotation,
flowTranslateTypeAnnotation,
);
expect(getConfigTypeSpy).toHaveBeenCalledTimes(1);
@@ -746,6 +763,8 @@ describe('buildSchema', () => {
buildModuleSchema,
Visitor,
flowParser,
resolveTypeAnnotation,
flowTranslateTypeAnnotation,
);
expect(getConfigTypeSpy).toHaveBeenCalledTimes(1);
@@ -1007,3 +1026,208 @@ describe('parseModuleName', () => {
});
});
});
describe('buildModuleSchema', () => {
const hasteModuleName = 'TestModuleName';
const [, tryParse] = createParserErrorCapturer();
const language = flowParser.language();
const NATIVE_MODULE = `
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+getArray: (a: Array<any>) => Array<string>;
}
export default (TurboModuleRegistry.getEnforcing<Spec>(
'SampleTurboModule',
): Spec);
`;
describe('throwIfModuleInterfaceNotFound', () => {
it('should throw ModuleInterfaceNotFoundParserError if no module interface is found', () => {
const ast = flowParser.getAst('');
const expected = new ModuleInterfaceNotFoundParserError(
hasteModuleName,
ast,
language,
);
expect(() =>
buildModuleSchema(
hasteModuleName,
ast,
tryParse,
flowParser,
resolveTypeAnnotation,
flowTranslateTypeAnnotation,
),
).toThrow(expected);
});
it('should not throw ModuleInterfaceNotFoundParserError if module interface is found', () => {
const ast = flowParser.getAst(NATIVE_MODULE);
expect(() =>
buildModuleSchema(
hasteModuleName,
ast,
tryParse,
flowParser,
resolveTypeAnnotation,
flowTranslateTypeAnnotation,
),
).not.toThrow();
});
});
describe('throwIfMoreThanOneModuleInterfaceParser', () => {
it('should throw an error if mulitple module interfaces are found', () => {
const contents = `
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+getBool: (arg: boolean) => boolean; }
export interface SpecOther extends TurboModule {
+getArray: (a: Array<any>) => Array<string>;
}
`;
const ast = flowParser.getAst(contents);
const types = flowParser.getTypes(ast);
const moduleSpecs = Object.values(types).filter(t =>
flowParser.isModuleInterface(t),
);
const expected = new MoreThanOneModuleInterfaceParserError(
hasteModuleName,
moduleSpecs,
moduleSpecs.map(node => node.id.name),
language,
);
expect(() =>
buildModuleSchema(
hasteModuleName,
ast,
tryParse,
flowParser,
resolveTypeAnnotation,
flowTranslateTypeAnnotation,
),
).toThrow(expected);
});
it('should not throw an error if exactly one module interface is found', () => {
const ast = flowParser.getAst(NATIVE_MODULE);
expect(() =>
buildModuleSchema(
hasteModuleName,
ast,
tryParse,
flowParser,
resolveTypeAnnotation,
flowTranslateTypeAnnotation,
),
).not.toThrow();
});
});
describe('throwIfModuleInterfaceIsMisnamed', () => {
it('should throw an error if module interface is misnamed', () => {
const contents = `
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
export interface MisnamedSpec extends TurboModule {
+getArray: (a: Array<any>) => Array<string>;
}
export default (TurboModuleRegistry.getEnforcing<Spec>(
'SampleTurboModule',
): Spec);
`;
const ast = flowParser.getAst(contents);
const types = flowParser.getTypes(ast);
const moduleSpecs = Object.values(types).filter(t =>
flowParser.isModuleInterface(t),
);
const [moduleSpec] = moduleSpecs;
const expected = new MisnamedModuleInterfaceParserError(
hasteModuleName,
moduleSpec.id,
language,
);
expect(() =>
buildModuleSchema(
hasteModuleName,
ast,
tryParse,
flowParser,
resolveTypeAnnotation,
flowTranslateTypeAnnotation,
),
).toThrow(expected);
});
it('should not throw an error if module interface is correctly named', () => {
const ast = flowParser.getAst(NATIVE_MODULE);
expect(() =>
buildModuleSchema(
hasteModuleName,
ast,
tryParse,
flowParser,
resolveTypeAnnotation,
flowTranslateTypeAnnotation,
),
).not.toThrow();
});
});
it('should return valid module schema', () => {
const ast = flowParser.getAst(NATIVE_MODULE);
const schmeaMock = {
aliasMap: {},
enumMap: {},
excludedPlatforms: undefined,
moduleName: 'SampleTurboModule',
spec: {
properties: [
{
name: 'getArray',
optional: false,
typeAnnotation: {
params: [
{
name: 'a',
optional: false,
typeAnnotation: {type: 'ArrayTypeAnnotation'},
},
],
returnTypeAnnotation: {
elementType: {type: 'StringTypeAnnotation'},
type: 'ArrayTypeAnnotation',
},
type: 'FunctionTypeAnnotation',
},
},
],
},
type: 'NativeModule',
};
const schema = buildModuleSchema(
hasteModuleName,
ast,
tryParse,
flowParser,
resolveTypeAnnotation,
flowTranslateTypeAnnotation,
);
expect(schema).toEqual(schmeaMock);
});
});
@@ -16,23 +16,18 @@ import type {
NativeModuleEnumMap,
NativeModuleBaseTypeAnnotation,
NativeModuleTypeAnnotation,
NativeModulePropertyShape,
NativeModuleSchema,
Nullable,
} from '../../../CodegenSchema';
import type {Parser} from '../../parser';
import type {ParserErrorCapturer, TypeDeclarationMap} from '../../utils';
const {verifyPlatforms} = require('../../utils');
const {resolveTypeAnnotation} = require('../utils');
const {
unwrapNullable,
wrapNullable,
assertGenericTypeAnnotationHasExactlyOneTypeParameter,
parseObjectProperty,
buildPropertySchema,
parseModuleName,
} = require('../../parsers-commons');
const {
emitArrayType,
@@ -61,9 +56,6 @@ const {
} = require('../../errors');
const {
throwIfModuleInterfaceNotFound,
throwIfModuleInterfaceIsMisnamed,
throwIfMoreThanOneModuleInterfaceParserError,
throwIfPartialNotAnnotatingTypeParameter,
throwIfPartialWithMoreParameter,
} = require('../../error-utils');
@@ -318,111 +310,6 @@ function translateTypeAnnotation(
}
}
function buildModuleSchema(
hasteModuleName: string,
/**
* TODO(T71778680): Flow-type this node.
*/
ast: $FlowFixMe,
tryParse: ParserErrorCapturer,
parser: Parser,
): NativeModuleSchema {
const types = parser.getTypes(ast);
const moduleSpecs = (Object.values(types): $ReadOnlyArray<$FlowFixMe>).filter(
t => parser.isModuleInterface(t),
);
throwIfModuleInterfaceNotFound(
moduleSpecs.length,
hasteModuleName,
ast,
language,
);
throwIfMoreThanOneModuleInterfaceParserError(
hasteModuleName,
moduleSpecs,
language,
);
const [moduleSpec] = moduleSpecs;
throwIfModuleInterfaceIsMisnamed(hasteModuleName, moduleSpec.id, language);
// Parse Module Name
// Also checks and throws error if:
// - Module Interface is Unused
// - More than 1 Module Registry Calls
// - Wrong number of Call Expression Args
// - Module Registry Call Args are Incorrect
// - Module is Untyped
// - Module Registry Call Type Parameter is Icorrect
const moduleName = parseModuleName(hasteModuleName, moduleSpec, ast, parser);
// Some module names use platform suffix to indicate platform-exclusive modules.
// Eventually this should be made explicit in the Flow type itself.
// Also check the hasteModuleName for platform suffix.
// Note: this shape is consistent with ComponentSchema.
const {cxxOnly, excludedPlatforms} = verifyPlatforms(
hasteModuleName,
moduleName,
);
// $FlowFixMe[missing-type-arg]
return (moduleSpec.body.properties: $ReadOnlyArray<$FlowFixMe>)
.filter(property => property.type === 'ObjectTypeProperty')
.map<?{
aliasMap: NativeModuleAliasMap,
enumMap: NativeModuleEnumMap,
propertyShape: NativeModulePropertyShape,
}>(property => {
const aliasMap: {...NativeModuleAliasMap} = {};
const enumMap: {...NativeModuleEnumMap} = {};
return tryParse(() => ({
aliasMap: aliasMap,
enumMap: enumMap,
propertyShape: buildPropertySchema(
hasteModuleName,
property,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
resolveTypeAnnotation,
translateTypeAnnotation,
parser,
),
}));
})
.filter(Boolean)
.reduce(
(
moduleSchema: NativeModuleSchema,
{aliasMap, enumMap, propertyShape},
) => ({
type: 'NativeModule',
aliasMap: {...moduleSchema.aliasMap, ...aliasMap},
enumMap: {...moduleSchema.enumMap, ...enumMap},
spec: {
properties: [...moduleSchema.spec.properties, propertyShape],
},
moduleName: moduleSchema.moduleName,
excludedPlatforms: moduleSchema.excludedPlatforms,
}),
{
type: 'NativeModule',
aliasMap: {},
enumMap: {},
spec: {properties: []},
moduleName,
excludedPlatforms:
excludedPlatforms.length !== 0 ? [...excludedPlatforms] : undefined,
},
);
}
module.exports = {
buildModuleSchema,
flowTranslateTypeAnnotation: translateTypeAnnotation,
};
+4 -1
View File
@@ -34,7 +34,8 @@ const {buildSchema} = require('../parsers-commons');
const {Visitor} = require('./Visitor');
const {buildComponentSchema} = require('./components');
const {wrapComponentSchema} = require('../schema.js');
const {buildModuleSchema} = require('./modules');
const {buildModuleSchema} = require('../parsers-commons.js');
const {resolveTypeAnnotation} = require('./utils');
const fs = require('fs');
@@ -105,6 +106,8 @@ class FlowParser implements Parser {
buildModuleSchema,
Visitor,
this,
resolveTypeAnnotation,
flowTranslateTypeAnnotation,
);
}
+133 -2
View File
@@ -21,6 +21,7 @@ import type {
NativeModuleParamTypeAnnotation,
NativeModulePropertyShape,
SchemaType,
NativeModuleEnumMap,
} from '../CodegenSchema.js';
import type {Parser} from './parser';
@@ -34,6 +35,7 @@ const {
createParserErrorCapturer,
visit,
isModuleRegistryCall,
verifyPlatforms,
} = require('./utils');
const {
throwIfPropertyValueTypeIsUnsupported,
@@ -46,6 +48,9 @@ const {
throwIfUntypedModule,
throwIfIncorrectModuleRegistryCallTypeParameterParserError,
throwIfIncorrectModuleRegistryCallArgument,
throwIfModuleInterfaceNotFound,
throwIfMoreThanOneModuleInterfaceParserError,
throwIfModuleInterfaceIsMisnamed,
} = require('./error-utils');
const {
@@ -55,7 +60,6 @@ const {
} = require('./errors');
const invariant = require('invariant');
import type {NativeModuleEnumMap} from '../CodegenSchema';
function wrapModuleSchema(
nativeModuleSchema: NativeModuleSchema,
@@ -361,8 +365,12 @@ function buildSchemaFromConfigType(
ast: $FlowFixMe,
tryParse: ParserErrorCapturer,
parser: Parser,
resolveTypeAnnotation: $FlowFixMe,
translateTypeAnnotation: $FlowFixMe,
) => NativeModuleSchema,
parser: Parser,
resolveTypeAnnotation: $FlowFixMe,
translateTypeAnnotation: $FlowFixMe,
): SchemaType {
switch (configType) {
case 'component': {
@@ -377,7 +385,14 @@ function buildSchemaFromConfigType(
const [parsingErrors, tryParse] = createParserErrorCapturer();
const schema = tryParse(() =>
buildModuleSchema(nativeModuleName, ast, tryParse, parser),
buildModuleSchema(
nativeModuleName,
ast,
tryParse,
parser,
resolveTypeAnnotation,
translateTypeAnnotation,
),
);
if (parsingErrors.length > 0) {
@@ -417,11 +432,15 @@ function buildSchema(
ast: $FlowFixMe,
tryParse: ParserErrorCapturer,
parser: Parser,
resolveTypeAnnotation: $FlowFixMe,
translateTypeAnnotation: $FlowFixMe,
) => NativeModuleSchema,
Visitor: ({isComponent: boolean, isModule: boolean}) => {
[type: string]: (node: $FlowFixMe) => void,
},
parser: Parser,
resolveTypeAnnotation: $FlowFixMe,
translateTypeAnnotation: $FlowFixMe,
): SchemaType {
// Early return for non-Spec JavaScript files
if (
@@ -442,6 +461,8 @@ function buildSchema(
buildComponentSchema,
buildModuleSchema,
parser,
resolveTypeAnnotation,
translateTypeAnnotation,
);
}
@@ -510,6 +531,115 @@ const parseModuleName = (
return $moduleName;
};
const buildModuleSchema = (
hasteModuleName: string,
/**
* TODO(T71778680): Flow-type this node.
*/
ast: $FlowFixMe,
tryParse: ParserErrorCapturer,
parser: Parser,
resolveTypeAnnotation: $FlowFixMe,
translateTypeAnnotation: $FlowFixMe,
): NativeModuleSchema => {
const language = parser.language();
const types = parser.getTypes(ast);
const moduleSpecs = (Object.values(types): $ReadOnlyArray<$FlowFixMe>).filter(
t => parser.isModuleInterface(t),
);
throwIfModuleInterfaceNotFound(
moduleSpecs.length,
hasteModuleName,
ast,
language,
);
throwIfMoreThanOneModuleInterfaceParserError(
hasteModuleName,
moduleSpecs,
language,
);
const [moduleSpec] = moduleSpecs;
throwIfModuleInterfaceIsMisnamed(hasteModuleName, moduleSpec.id, language);
// Parse Module Name
const moduleName = parseModuleName(hasteModuleName, moduleSpec, ast, parser);
// Some module names use platform suffix to indicate platform-exclusive modules.
// Eventually this should be made explicit in the Flow type itself.
// Also check the hasteModuleName for platform suffix.
// Note: this shape is consistent with ComponentSchema.
const {cxxOnly, excludedPlatforms} = verifyPlatforms(
hasteModuleName,
moduleName,
);
const properties: $ReadOnlyArray<$FlowFixMe> =
language === 'Flow' ? moduleSpec.body.properties : moduleSpec.body.body;
// $FlowFixMe[missing-type-arg]
return properties
.filter(
property =>
property.type === 'ObjectTypeProperty' ||
property.type === 'TSPropertySignature' ||
property.type === 'TSMethodSignature',
)
.map<?{
aliasMap: NativeModuleAliasMap,
enumMap: NativeModuleEnumMap,
propertyShape: NativeModulePropertyShape,
}>(property => {
const aliasMap: {...NativeModuleAliasMap} = {};
const enumMap: {...NativeModuleEnumMap} = {};
return tryParse(() => ({
aliasMap,
enumMap,
propertyShape: buildPropertySchema(
hasteModuleName,
property,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
resolveTypeAnnotation,
translateTypeAnnotation,
parser,
),
}));
})
.filter(Boolean)
.reduce(
(
moduleSchema: NativeModuleSchema,
{aliasMap, enumMap, propertyShape},
) => ({
type: 'NativeModule',
aliasMap: {...moduleSchema.aliasMap, ...aliasMap},
enumMap: {...moduleSchema.enumMap, ...enumMap},
spec: {
properties: [...moduleSchema.spec.properties, propertyShape],
},
moduleName: moduleSchema.moduleName,
excludedPlatforms: moduleSchema.excludedPlatforms,
}),
{
type: 'NativeModule',
aliasMap: {},
enumMap: {},
spec: {properties: []},
moduleName,
excludedPlatforms:
excludedPlatforms.length !== 0 ? [...excludedPlatforms] : undefined,
},
);
};
module.exports = {
wrapModuleSchema,
unwrapNullable,
@@ -522,4 +652,5 @@ module.exports = {
buildSchemaFromConfigType,
buildSchema,
parseModuleName,
buildModuleSchema,
};
@@ -15,9 +15,7 @@ import type {
NativeModuleAliasMap,
NativeModuleEnumMap,
NativeModuleBaseTypeAnnotation,
NativeModulePropertyShape,
NativeModuleTypeAnnotation,
NativeModuleSchema,
Nullable,
} from '../../../CodegenSchema';
@@ -30,14 +28,9 @@ import type {
const {flattenIntersectionType} = require('../parseTopLevelType');
const {flattenProperties} = require('../components/componentsUtils');
const {verifyPlatforms} = require('../../utils');
const {resolveTypeAnnotation} = require('../utils');
const {
parseObjectProperty,
buildPropertySchema,
parseModuleName,
} = require('../../parsers-commons');
const {parseObjectProperty} = require('../../parsers-commons');
const {typeEnumResolution} = require('../../parsers-primitives');
const {
@@ -67,9 +60,6 @@ const {
} = require('../../errors');
const {
throwIfModuleInterfaceNotFound,
throwIfModuleInterfaceIsMisnamed,
throwIfMoreThanOneModuleInterfaceParserError,
throwIfPartialNotAnnotatingTypeParameter,
throwIfPartialWithMoreParameter,
} = require('../../error-utils');
@@ -422,118 +412,6 @@ function translateTypeAnnotation(
}
}
function buildModuleSchema(
hasteModuleName: string,
/**
* TODO(T108222691): Use flow-types for @babel/parser
*/
ast: $FlowFixMe,
tryParse: ParserErrorCapturer,
parser: Parser,
): NativeModuleSchema {
const types = parser.getTypes(ast);
const moduleSpecs = (Object.values(types): $ReadOnlyArray<$FlowFixMe>).filter(
t => parser.isModuleInterface(t),
);
throwIfModuleInterfaceNotFound(
moduleSpecs.length,
hasteModuleName,
ast,
language,
);
throwIfMoreThanOneModuleInterfaceParserError(
hasteModuleName,
moduleSpecs,
language,
);
const [moduleSpec] = moduleSpecs;
throwIfModuleInterfaceIsMisnamed(hasteModuleName, moduleSpec.id, language);
// Parse Module Name
// Also checks and throws error if:
// - Module Interface is Unused
// - More than 1 Module Registry Calls
// - Wrong number of Call Expression Args
// - Module Registry Call Args are Incorrect
// - Module is Untyped
// - Module Registry Call Type Parameter is Icorrect
const moduleName = parseModuleName(hasteModuleName, moduleSpec, ast, parser);
// Some module names use platform suffix to indicate platform-exclusive modules.
// Eventually this should be made explicit in the Flow type itself.
// Also check the hasteModuleName for platform suffix.
// Note: this shape is consistent with ComponentSchema.
const {cxxOnly, excludedPlatforms} = verifyPlatforms(
hasteModuleName,
moduleName,
);
// $FlowFixMe[missing-type-arg]
return (moduleSpec.body.body: $ReadOnlyArray<$FlowFixMe>)
.filter(
property =>
property.type === 'TSMethodSignature' ||
property.type === 'TSPropertySignature',
)
.map<?{
aliasMap: NativeModuleAliasMap,
enumMap: NativeModuleEnumMap,
propertyShape: NativeModulePropertyShape,
}>(property => {
const aliasMap: {...NativeModuleAliasMap} = {};
const enumMap: {...NativeModuleEnumMap} = {};
return tryParse(() => ({
aliasMap: aliasMap,
enumMap: enumMap,
propertyShape: buildPropertySchema(
hasteModuleName,
property,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
resolveTypeAnnotation,
translateTypeAnnotation,
parser,
),
}));
})
.filter(Boolean)
.reduce(
(
moduleSchema: NativeModuleSchema,
{aliasMap, enumMap, propertyShape},
) => {
return {
type: 'NativeModule',
aliasMap: {...moduleSchema.aliasMap, ...aliasMap},
enumMap: {...moduleSchema.enumMap, ...enumMap},
spec: {
properties: [...moduleSchema.spec.properties, propertyShape],
},
moduleName: moduleSchema.moduleName,
excludedPlatforms: moduleSchema.excludedPlatforms,
};
},
{
type: 'NativeModule',
aliasMap: {},
enumMap: {},
spec: {properties: []},
moduleName: moduleName,
excludedPlatforms:
excludedPlatforms.length !== 0 ? [...excludedPlatforms] : undefined,
},
);
}
module.exports = {
buildModuleSchema,
typeScriptTranslateTypeAnnotation: translateTypeAnnotation,
};
@@ -34,7 +34,8 @@ const {buildSchema} = require('../parsers-commons');
const {Visitor} = require('./Visitor');
const {buildComponentSchema} = require('./components');
const {wrapComponentSchema} = require('../schema.js');
const {buildModuleSchema} = require('./modules');
const {buildModuleSchema} = require('../parsers-commons.js');
const {resolveTypeAnnotation} = require('./utils');
const fs = require('fs');
@@ -107,6 +108,8 @@ class TypeScriptParser implements Parser {
buildModuleSchema,
Visitor,
this,
resolveTypeAnnotation,
typeScriptTranslateTypeAnnotation,
);
}