mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
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:
1 parent
e68f513879
commit
3cd97e4994
7 files changed
+402
-250
No files matched your search
+32
-6
@@ -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 => {
|
||||
|
||||
+228
-4
@@ -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,
|
||||
};
|
||||
@@ -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
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user