Extract the buildSchema function in the parsers-commons.js (#35158)
Summary: This PR aims to extract the buildSchema function into parsers-commons that is shared between typescript and flow. It is a task of https://github.com/facebook/react-native/issues/34872: > Extract the buildSchema function ([Flow](https://github.com/facebook/react-native/blob/main/packages/react-native-codegen/src/parsers/flow/index.js#L66), [TypeScript](https://github.com/facebook/react-native/blob/main/packages/react-native-codegen/src/parsers/typescript/index.js#L72)) in the parsers-commons.js file to a top level buildSchema function which takes additional parameters to properly parse the content, get the config type and to build the schema, based on the language used. ## Changelog <!-- Help reviewers and the release process by writing your own changelog entry. For an example, see: https://reactnative.dev/contributing/changelogs-in-pull-requests --> [Internal] [Changed] - Extract the buildSchema function in the parsers-commons.js Pull Request resolved: https://github.com/facebook/react-native/pull/35158 Test Plan: yarn test: <img width="380" alt="image" src="https://user-images.githubusercontent.com/40902940/209584411-40f66047-e25d-43d4-975d-af10cd202f24.png"> yarn flow: <img width="150" alt="image" src="https://user-images.githubusercontent.com/40902940/209584423-4cf2cb5a-a300-40a6-962c-e57934f19ad2.png"> yarn lint: <img width="510" alt="image" src="https://user-images.githubusercontent.com/40902940/209584440-2d2b2658-73d8-47e2-bb8c-64d4633369a2.png"> Reviewed By: cortinico Differential Revision: D42386804 Pulled By: cipolleschi fbshipit-source-id: 2a238f7cec982d8ef3fd57a34dc9f58171e32b53
This commit is contained in:
Родитель
91397d4247
Коммит
dc4d73c954
|
@ -133,12 +133,11 @@ function rule(context) {
|
|||
|
||||
const {buildModuleSchema, createParserErrorCapturer, parser} =
|
||||
requireModuleParser();
|
||||
const flowParser = require('flow-parser');
|
||||
|
||||
const [parsingErrors, tryParse] = createParserErrorCapturer();
|
||||
|
||||
const sourceCode = context.getSourceCode().getText();
|
||||
const ast = flowParser.parse(sourceCode, {enums: true});
|
||||
const ast = parser.getAst(sourceCode);
|
||||
|
||||
tryParse(() => {
|
||||
buildModuleSchema(hasteModuleName, ast, tryParse, parser);
|
||||
|
|
|
@ -17,22 +17,32 @@ import {
|
|||
parseObjectProperty,
|
||||
wrapNullable,
|
||||
unwrapNullable,
|
||||
buildSchemaFromConfigType,
|
||||
buildSchema,
|
||||
} from '../parsers-commons';
|
||||
import type {ParserType} from '../errors';
|
||||
|
||||
const {Visitor} = require('../flow/Visitor');
|
||||
const {wrapComponentSchema} = require('../flow/components/schema');
|
||||
const {buildComponentSchema} = require('../flow/components');
|
||||
const {buildModuleSchema} = require('../flow/modules');
|
||||
const {isModuleRegistryCall} = require('../utils.js');
|
||||
const {
|
||||
ParserError,
|
||||
UnsupportedObjectPropertyTypeAnnotationParserError,
|
||||
} = require('../errors');
|
||||
|
||||
import {MockedParser} from '../parserMock';
|
||||
import {TypeScriptParser} from '../typescript/parser';
|
||||
|
||||
const parser = new MockedParser();
|
||||
const typeScriptParser = new TypeScriptParser();
|
||||
|
||||
const flowTranslateTypeAnnotation = require('../flow/modules/index');
|
||||
const typeScriptTranslateTypeAnnotation = require('../typescript/modules/index');
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('wrapNullable', () => {
|
||||
describe('when nullable is true', () => {
|
||||
it('returns nullable type annotation', () => {
|
||||
|
@ -338,3 +348,428 @@ describe('parseObjectProperty', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildSchemaFromConfigType', () => {
|
||||
const astMock = {
|
||||
type: 'Program',
|
||||
loc: {
|
||||
source: null,
|
||||
start: {line: 2, column: 10},
|
||||
end: {line: 16, column: 62},
|
||||
},
|
||||
range: [11, 373],
|
||||
body: [],
|
||||
comments: [],
|
||||
errors: [],
|
||||
};
|
||||
|
||||
const componentSchemaMock = {
|
||||
filename: 'filename',
|
||||
componentName: 'componentName',
|
||||
extendsProps: [],
|
||||
events: [],
|
||||
props: [],
|
||||
commands: [],
|
||||
};
|
||||
|
||||
const moduleSchemaMock = {
|
||||
type: 'NativeModule',
|
||||
aliases: {},
|
||||
spec: {properties: []},
|
||||
moduleName: '',
|
||||
};
|
||||
|
||||
const wrapComponentSchemaMock = jest.fn();
|
||||
const buildComponentSchemaMock = jest.fn(_ => componentSchemaMock);
|
||||
const buildModuleSchemaMock = jest.fn((_0, _1, _2, _3) => moduleSchemaMock);
|
||||
|
||||
const buildSchemaFromConfigTypeHelper = (
|
||||
configType: 'module' | 'component' | 'none',
|
||||
filename: ?string,
|
||||
) =>
|
||||
buildSchemaFromConfigType(
|
||||
configType,
|
||||
filename,
|
||||
astMock,
|
||||
wrapComponentSchemaMock,
|
||||
buildComponentSchemaMock,
|
||||
buildModuleSchemaMock,
|
||||
parser,
|
||||
);
|
||||
|
||||
describe('when configType is none', () => {
|
||||
it('returns an empty schema', () => {
|
||||
const schema = buildSchemaFromConfigTypeHelper('none');
|
||||
|
||||
expect(schema).toEqual({modules: {}});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when configType is component', () => {
|
||||
it('calls buildComponentSchema with ast and wrapComponentSchema with the result', () => {
|
||||
buildSchemaFromConfigTypeHelper('component');
|
||||
|
||||
expect(buildComponentSchemaMock).toHaveBeenCalledTimes(1);
|
||||
expect(buildComponentSchemaMock).toHaveBeenCalledWith(astMock);
|
||||
expect(wrapComponentSchemaMock).toHaveBeenCalledTimes(1);
|
||||
expect(wrapComponentSchemaMock).toHaveBeenCalledWith(componentSchemaMock);
|
||||
|
||||
expect(buildModuleSchemaMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when configType is module', () => {
|
||||
describe('when filename is undefined', () => {
|
||||
it('throws an error', () => {
|
||||
expect(() => buildSchemaFromConfigTypeHelper('module')).toThrow(
|
||||
'Filepath expected while parasing a module',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when filename is null', () => {
|
||||
it('throws an error', () => {
|
||||
expect(() => buildSchemaFromConfigTypeHelper('module', null)).toThrow(
|
||||
'Filepath expected while parasing a module',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when filename is defined and not null', () => {
|
||||
describe('when buildModuleSchema throws', () => {
|
||||
it('throws the error', () => {
|
||||
const parserError = new ParserError(
|
||||
'moduleName',
|
||||
astMock,
|
||||
'Something went wrong',
|
||||
);
|
||||
buildModuleSchemaMock.mockImplementationOnce(() => {
|
||||
throw parserError;
|
||||
});
|
||||
|
||||
expect(() =>
|
||||
buildSchemaFromConfigTypeHelper('module', 'filename'),
|
||||
).toThrow(parserError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when buildModuleSchema returns null', () => {
|
||||
it('throws an error', () => {
|
||||
// $FlowIgnore[incompatible-call] - This is to test an invariant
|
||||
buildModuleSchemaMock.mockReturnValueOnce(null);
|
||||
|
||||
expect(() =>
|
||||
buildSchemaFromConfigTypeHelper('module', 'filename'),
|
||||
).toThrow(
|
||||
'When there are no parsing errors, the schema should not be null',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when buildModuleSchema returns a schema', () => {
|
||||
it('calls buildModuleSchema with ast and wrapModuleSchema with the result', () => {
|
||||
buildSchemaFromConfigTypeHelper('module', 'filename');
|
||||
|
||||
expect(buildModuleSchemaMock).toHaveBeenCalledTimes(1);
|
||||
expect(buildModuleSchemaMock).toHaveBeenCalledWith(
|
||||
'filename',
|
||||
astMock,
|
||||
expect.any(Function),
|
||||
parser,
|
||||
);
|
||||
|
||||
expect(buildComponentSchemaMock).not.toHaveBeenCalled();
|
||||
expect(wrapComponentSchemaMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isModuleRegistryCall', () => {
|
||||
describe('when node is not of CallExpression type', () => {
|
||||
it('returns false', () => {
|
||||
const node = {
|
||||
type: 'NotCallExpression',
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when node is of CallExpressionType', () => {
|
||||
describe('when callee type is not of MemberExpression type', () => {
|
||||
it('returns false', () => {
|
||||
const node = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'NotMemberExpression',
|
||||
},
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when callee type is of MemberExpression type', () => {
|
||||
describe('when memberExpression has an object of type different than "Identifier"', () => {
|
||||
it('returns false', () => {
|
||||
const node = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'NotIdentifier',
|
||||
name: 'TurboModuleRegistry',
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when memberExpression has an object of name different than "TurboModuleRegistry"', () => {
|
||||
it('returns false', () => {
|
||||
const node = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'Identifier',
|
||||
name: 'NotTurboModuleRegistry',
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when memberExpression has an object of type "Identifier" and name "TurboModuleRegistry', () => {
|
||||
describe('when memberExpression has a property of type different than "Identifier"', () => {
|
||||
it('returns false', () => {
|
||||
const node = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'Identifier',
|
||||
name: 'TurboModuleRegistry',
|
||||
},
|
||||
property: {
|
||||
type: 'NotIdentifier',
|
||||
name: 'get',
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when memberExpression has a property of name different than "get" or "getEnforcing', () => {
|
||||
it('returns false', () => {
|
||||
const node = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'Identifier',
|
||||
name: 'TurboModuleRegistry',
|
||||
},
|
||||
property: {
|
||||
type: 'Identifier',
|
||||
name: 'NotGet',
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when memberExpression has a property of type "Identifier" and of name "get" or "getEnforcing', () => {
|
||||
describe('when memberExpression is computed', () => {
|
||||
it('returns false', () => {
|
||||
const node = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'Identifier',
|
||||
name: 'TurboModuleRegistry',
|
||||
},
|
||||
property: {
|
||||
type: 'Identifier',
|
||||
name: 'get',
|
||||
},
|
||||
computed: true,
|
||||
},
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when memberExpression is not computed', () => {
|
||||
it('returns true', () => {
|
||||
const node = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'Identifier',
|
||||
name: 'TurboModuleRegistry',
|
||||
},
|
||||
property: {
|
||||
type: 'Identifier',
|
||||
name: 'get',
|
||||
},
|
||||
computed: false,
|
||||
},
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildSchema', () => {
|
||||
const getConfigTypeSpy = jest.spyOn(require('../utils'), 'getConfigType');
|
||||
|
||||
describe('when there is no codegenNativeComponent and no TurboModule', () => {
|
||||
const contents = '';
|
||||
|
||||
it('returns an empty module', () => {
|
||||
const schema = buildSchema(
|
||||
contents,
|
||||
'fileName',
|
||||
wrapComponentSchema,
|
||||
buildComponentSchema,
|
||||
buildModuleSchema,
|
||||
Visitor,
|
||||
parser,
|
||||
);
|
||||
|
||||
expect(getConfigTypeSpy).not.toHaveBeenCalled();
|
||||
expect(schema).toEqual({modules: {}});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is a codegenNativeComponent', () => {
|
||||
const contents = `
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {HostComponent} from 'react-native';
|
||||
|
||||
const codegenNativeComponent = require('codegenNativeComponent');
|
||||
|
||||
export type ModuleProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|}>;
|
||||
|
||||
export default (codegenNativeComponent<ModuleProps>(
|
||||
'Module',
|
||||
): HostComponent<ModuleProps>);
|
||||
`;
|
||||
|
||||
it('returns a module with good properties', () => {
|
||||
const schema = buildSchema(
|
||||
contents,
|
||||
'fileName',
|
||||
wrapComponentSchema,
|
||||
buildComponentSchema,
|
||||
buildModuleSchema,
|
||||
Visitor,
|
||||
parser,
|
||||
);
|
||||
|
||||
expect(getConfigTypeSpy).toHaveBeenCalledTimes(1);
|
||||
expect(getConfigTypeSpy).toHaveBeenCalledWith(
|
||||
parser.getAst(contents),
|
||||
Visitor,
|
||||
);
|
||||
expect(schema).toEqual({
|
||||
modules: {
|
||||
Module: {
|
||||
type: 'Component',
|
||||
components: {
|
||||
Module: {
|
||||
extendsProps: [
|
||||
{
|
||||
type: 'ReactNativeBuiltInType',
|
||||
knownTypeName: 'ReactNativeCoreViewProps',
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
props: [],
|
||||
commands: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is a TurboModule', () => {
|
||||
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 {
|
||||
+getArray: (a: Array<any>) => Array<string>;
|
||||
}
|
||||
|
||||
export default (TurboModuleRegistry.getEnforcing<Spec>(
|
||||
'SampleTurboModule',
|
||||
): Spec);
|
||||
`;
|
||||
|
||||
it('returns a module with good properties', () => {
|
||||
const schema = buildSchema(
|
||||
contents,
|
||||
'fileName',
|
||||
wrapComponentSchema,
|
||||
buildComponentSchema,
|
||||
buildModuleSchema,
|
||||
Visitor,
|
||||
parser,
|
||||
);
|
||||
|
||||
expect(getConfigTypeSpy).toHaveBeenCalledTimes(1);
|
||||
expect(getConfigTypeSpy).toHaveBeenCalledWith(
|
||||
parser.getAst(contents),
|
||||
Visitor,
|
||||
);
|
||||
expect(schema).toEqual({
|
||||
modules: {
|
||||
fileName: {
|
||||
type: 'NativeModule',
|
||||
aliases: {},
|
||||
spec: {
|
||||
properties: [
|
||||
{
|
||||
name: 'getArray',
|
||||
optional: false,
|
||||
typeAnnotation: {
|
||||
type: 'FunctionTypeAnnotation',
|
||||
returnTypeAnnotation: {
|
||||
type: 'ArrayTypeAnnotation',
|
||||
elementType: {type: 'StringTypeAnnotation'},
|
||||
},
|
||||
params: [
|
||||
{
|
||||
name: 'a',
|
||||
optional: false,
|
||||
typeAnnotation: {type: 'ArrayTypeAnnotation'},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
moduleName: 'SampleTurboModule',
|
||||
excludedPlatforms: undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,15 +11,11 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const {MockedParser} = require('../parserMock');
|
||||
|
||||
const {
|
||||
extractNativeModuleName,
|
||||
createParserErrorCapturer,
|
||||
verifyPlatforms,
|
||||
visit,
|
||||
buildSchemaFromConfigType,
|
||||
isModuleRegistryCall,
|
||||
} = require('../utils.js');
|
||||
const {ParserError} = require('../errors');
|
||||
|
||||
|
@ -297,297 +293,3 @@ describe('visit', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildSchemaFromConfigType', () => {
|
||||
const parser = new MockedParser();
|
||||
|
||||
const astMock = {
|
||||
type: 'Program',
|
||||
loc: {
|
||||
source: null,
|
||||
start: {line: 2, column: 10},
|
||||
end: {line: 16, column: 62},
|
||||
},
|
||||
range: [11, 373],
|
||||
body: [],
|
||||
comments: [],
|
||||
errors: [],
|
||||
};
|
||||
|
||||
const componentSchemaMock = {
|
||||
filename: 'filename',
|
||||
componentName: 'componentName',
|
||||
extendsProps: [],
|
||||
events: [],
|
||||
props: [],
|
||||
commands: [],
|
||||
};
|
||||
|
||||
const moduleSchemaMock = {
|
||||
type: 'NativeModule',
|
||||
aliases: {},
|
||||
spec: {properties: []},
|
||||
moduleName: '',
|
||||
};
|
||||
|
||||
const wrapComponentSchemaMock = jest.fn();
|
||||
const buildComponentSchemaMock = jest.fn(_ => componentSchemaMock);
|
||||
const wrapModuleSchemaMock = jest.spyOn(
|
||||
require('../parsers-commons'),
|
||||
'wrapModuleSchema',
|
||||
);
|
||||
const buildModuleSchemaMock = jest.fn((_0, _1, _2, _3) => moduleSchemaMock);
|
||||
|
||||
const buildSchemaFromConfigTypeHelper = (
|
||||
configType: 'module' | 'component' | 'none',
|
||||
filename: ?string,
|
||||
) =>
|
||||
buildSchemaFromConfigType(
|
||||
configType,
|
||||
filename,
|
||||
astMock,
|
||||
wrapComponentSchemaMock,
|
||||
buildComponentSchemaMock,
|
||||
buildModuleSchemaMock,
|
||||
parser,
|
||||
);
|
||||
|
||||
describe('when configType is none', () => {
|
||||
it('returns an empty schema', () => {
|
||||
const schema = buildSchemaFromConfigTypeHelper('none');
|
||||
|
||||
expect(schema).toEqual({modules: {}});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when configType is component', () => {
|
||||
it('calls buildComponentSchema with ast and wrapComponentSchema with the result', () => {
|
||||
buildSchemaFromConfigTypeHelper('component');
|
||||
|
||||
expect(buildComponentSchemaMock).toHaveBeenCalledTimes(1);
|
||||
expect(buildComponentSchemaMock).toHaveBeenCalledWith(astMock);
|
||||
expect(wrapComponentSchemaMock).toHaveBeenCalledTimes(1);
|
||||
expect(wrapComponentSchemaMock).toHaveBeenCalledWith(componentSchemaMock);
|
||||
|
||||
expect(buildModuleSchemaMock).not.toHaveBeenCalled();
|
||||
expect(wrapModuleSchemaMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when configType is module', () => {
|
||||
describe('when filename is undefined', () => {
|
||||
it('throws an error', () => {
|
||||
expect(() => buildSchemaFromConfigTypeHelper('module')).toThrow(
|
||||
'Filepath expected while parasing a module',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when filename is null', () => {
|
||||
it('throws an error', () => {
|
||||
expect(() => buildSchemaFromConfigTypeHelper('module', null)).toThrow(
|
||||
'Filepath expected while parasing a module',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when filename is defined and not null', () => {
|
||||
describe('when buildModuleSchema throws', () => {
|
||||
it('throws the error', () => {
|
||||
const parserError = new ParserError(
|
||||
'moduleName',
|
||||
astMock,
|
||||
'Something went wrong',
|
||||
);
|
||||
buildModuleSchemaMock.mockImplementationOnce(() => {
|
||||
throw parserError;
|
||||
});
|
||||
|
||||
expect(() =>
|
||||
buildSchemaFromConfigTypeHelper('module', 'filename'),
|
||||
).toThrow(parserError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when buildModuleSchema returns null', () => {
|
||||
it('throws an error', () => {
|
||||
// $FlowIgnore[incompatible-call] - This is to test an invariant
|
||||
buildModuleSchemaMock.mockReturnValueOnce(null);
|
||||
|
||||
expect(() =>
|
||||
buildSchemaFromConfigTypeHelper('module', 'filename'),
|
||||
).toThrow(
|
||||
'When there are no parsing errors, the schema should not be null',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when buildModuleSchema returns a schema', () => {
|
||||
it('calls buildModuleSchema with ast and wrapModuleSchema with the result', () => {
|
||||
buildSchemaFromConfigTypeHelper('module', 'filename');
|
||||
|
||||
expect(buildModuleSchemaMock).toHaveBeenCalledTimes(1);
|
||||
expect(buildModuleSchemaMock).toHaveBeenCalledWith(
|
||||
'filename',
|
||||
astMock,
|
||||
expect.any(Function),
|
||||
parser,
|
||||
);
|
||||
expect(wrapModuleSchemaMock).toHaveBeenCalledTimes(1);
|
||||
expect(wrapModuleSchemaMock).toHaveBeenCalledWith(
|
||||
moduleSchemaMock,
|
||||
'filename',
|
||||
);
|
||||
|
||||
expect(buildComponentSchemaMock).not.toHaveBeenCalled();
|
||||
expect(wrapComponentSchemaMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isModuleRegistryCall', () => {
|
||||
describe('when node is not of CallExpression type', () => {
|
||||
it('returns false', () => {
|
||||
const node = {
|
||||
type: 'NotCallExpression',
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when node is of CallExpressionType', () => {
|
||||
describe('when callee type is not of MemberExpression type', () => {
|
||||
it('returns false', () => {
|
||||
const node = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'NotMemberExpression',
|
||||
},
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when callee type is of MemberExpression type', () => {
|
||||
describe('when memberExpression has an object of type different than "Identifier"', () => {
|
||||
it('returns false', () => {
|
||||
const node = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'NotIdentifier',
|
||||
name: 'TurboModuleRegistry',
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when memberExpression has an object of name different than "TurboModuleRegistry"', () => {
|
||||
it('returns false', () => {
|
||||
const node = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'Identifier',
|
||||
name: 'NotTurboModuleRegistry',
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when memberExpression has an object of type "Identifier" and name "TurboModuleRegistry', () => {
|
||||
describe('when memberExpression has a property of type different than "Identifier"', () => {
|
||||
it('returns false', () => {
|
||||
const node = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'Identifier',
|
||||
name: 'TurboModuleRegistry',
|
||||
},
|
||||
property: {
|
||||
type: 'NotIdentifier',
|
||||
name: 'get',
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when memberExpression has a property of name different than "get" or "getEnforcing', () => {
|
||||
it('returns false', () => {
|
||||
const node = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'Identifier',
|
||||
name: 'TurboModuleRegistry',
|
||||
},
|
||||
property: {
|
||||
type: 'Identifier',
|
||||
name: 'NotGet',
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when memberExpression has a property of type "Identifier" and of name "get" or "getEnforcing', () => {
|
||||
describe('when memberExpression is computed', () => {
|
||||
it('returns false', () => {
|
||||
const node = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'Identifier',
|
||||
name: 'TurboModuleRegistry',
|
||||
},
|
||||
property: {
|
||||
type: 'Identifier',
|
||||
name: 'get',
|
||||
},
|
||||
computed: true,
|
||||
},
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when memberExpression is not computed', () => {
|
||||
it('returns true', () => {
|
||||
const node = {
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'Identifier',
|
||||
name: 'TurboModuleRegistry',
|
||||
},
|
||||
property: {
|
||||
type: 'Identifier',
|
||||
name: 'get',
|
||||
},
|
||||
computed: false,
|
||||
},
|
||||
};
|
||||
expect(isModuleRegistryCall(node)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const {isModuleRegistryCall} = require('../utils');
|
||||
|
||||
function Visitor(infoMap: {isComponent: boolean, isModule: boolean}): {
|
||||
[type: string]: (node: $FlowFixMe) => void,
|
||||
} {
|
||||
return {
|
||||
CallExpression(node: $FlowFixMe) {
|
||||
if (
|
||||
node.callee.type === 'Identifier' &&
|
||||
node.callee.name === 'codegenNativeComponent'
|
||||
) {
|
||||
infoMap.isComponent = true;
|
||||
}
|
||||
|
||||
if (isModuleRegistryCall(node)) {
|
||||
infoMap.isModule = true;
|
||||
}
|
||||
},
|
||||
InterfaceExtends(node: $FlowFixMe) {
|
||||
if (node.id.name === 'TurboModule') {
|
||||
infoMap.isModule = true;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Visitor,
|
||||
};
|
|
@ -1,78 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {SchemaType} from '../../CodegenSchema';
|
||||
import type {Parser} from '../parser';
|
||||
|
||||
// $FlowFixMe[untyped-import] there's no flowtype flow-parser
|
||||
const flowParser = require('flow-parser');
|
||||
const {
|
||||
getConfigType,
|
||||
buildSchemaFromConfigType,
|
||||
isModuleRegistryCall,
|
||||
} = require('../utils');
|
||||
const {buildComponentSchema} = require('./components');
|
||||
const {wrapComponentSchema} = require('./components/schema');
|
||||
const {buildModuleSchema} = require('./modules');
|
||||
|
||||
function Visitor(infoMap: {isComponent: boolean, isModule: boolean}) {
|
||||
return {
|
||||
CallExpression(node: $FlowFixMe) {
|
||||
if (
|
||||
node.callee.type === 'Identifier' &&
|
||||
node.callee.name === 'codegenNativeComponent'
|
||||
) {
|
||||
infoMap.isComponent = true;
|
||||
}
|
||||
|
||||
if (isModuleRegistryCall(node)) {
|
||||
infoMap.isModule = true;
|
||||
}
|
||||
},
|
||||
InterfaceExtends(node: $FlowFixMe) {
|
||||
if (node.id.name === 'TurboModule') {
|
||||
infoMap.isModule = true;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildSchema(
|
||||
contents: string,
|
||||
filename: ?string,
|
||||
parser: Parser,
|
||||
): SchemaType {
|
||||
// Early return for non-Spec JavaScript files
|
||||
if (
|
||||
!contents.includes('codegenNativeComponent') &&
|
||||
!contents.includes('TurboModule')
|
||||
) {
|
||||
return {modules: {}};
|
||||
}
|
||||
|
||||
const ast = flowParser.parse(contents, {enums: true});
|
||||
const configType = getConfigType(ast, Visitor);
|
||||
|
||||
return buildSchemaFromConfigType(
|
||||
configType,
|
||||
filename,
|
||||
ast,
|
||||
wrapComponentSchema,
|
||||
buildComponentSchema,
|
||||
buildModuleSchema,
|
||||
parser,
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildSchema,
|
||||
};
|
|
@ -13,19 +13,32 @@
|
|||
import type {SchemaType} from '../../CodegenSchema.js';
|
||||
|
||||
const fs = require('fs');
|
||||
const {buildSchema} = require('./buildSchema');
|
||||
|
||||
const {buildSchema} = require('../parsers-commons');
|
||||
const {Visitor} = require('./Visitor');
|
||||
const {FlowParser} = require('./parser');
|
||||
const {buildComponentSchema} = require('./components');
|
||||
const {wrapComponentSchema} = require('./components/schema');
|
||||
const {buildModuleSchema} = require('./modules');
|
||||
|
||||
const parser = new FlowParser();
|
||||
|
||||
function parseModuleFixture(filename: string): SchemaType {
|
||||
const contents = fs.readFileSync(filename, 'utf8');
|
||||
|
||||
return buildSchema(contents, 'path/NativeSampleTurboModule.js', parser);
|
||||
return parseString(contents, 'path/NativeSampleTurboModule.js');
|
||||
}
|
||||
|
||||
function parseString(contents: string, filename: ?string): SchemaType {
|
||||
return buildSchema(contents, filename, parser);
|
||||
return buildSchema(
|
||||
contents,
|
||||
filename,
|
||||
wrapComponentSchema,
|
||||
buildComponentSchema,
|
||||
buildModuleSchema,
|
||||
Visitor,
|
||||
parser,
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -20,7 +20,14 @@ import type {
|
|||
import type {ParserType} from '../errors';
|
||||
import type {Parser} from '../parser';
|
||||
|
||||
const {buildSchema} = require('./buildSchema');
|
||||
// $FlowFixMe[untyped-import] there's no flowtype flow-parser
|
||||
const flowParser = require('flow-parser');
|
||||
|
||||
const {buildSchema} = require('../parsers-commons');
|
||||
const {Visitor} = require('./Visitor');
|
||||
const {buildComponentSchema} = require('./components');
|
||||
const {wrapComponentSchema} = require('./components/schema');
|
||||
const {buildModuleSchema} = require('./modules');
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
|
@ -89,7 +96,21 @@ class FlowParser implements Parser {
|
|||
parseFile(filename: string): SchemaType {
|
||||
const contents = fs.readFileSync(filename, 'utf8');
|
||||
|
||||
return buildSchema(contents, filename, this);
|
||||
return buildSchema(
|
||||
contents,
|
||||
filename,
|
||||
wrapComponentSchema,
|
||||
buildComponentSchema,
|
||||
buildModuleSchema,
|
||||
Visitor,
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
getAst(contents: string): $FlowFixMe {
|
||||
return flowParser.parse(contents, {
|
||||
enums: true,
|
||||
});
|
||||
}
|
||||
|
||||
getFunctionTypeAnnotationParameters(
|
||||
|
|
|
@ -84,6 +84,13 @@ export interface Parser {
|
|||
*/
|
||||
parseFile(filename: string): SchemaType;
|
||||
|
||||
/**
|
||||
* Given the content of a file, it returns an AST.
|
||||
* @parameter contents: the content of the file.
|
||||
* @returns: the AST of the file.
|
||||
*/
|
||||
getAst(contents: string): $FlowFixMe;
|
||||
|
||||
/**
|
||||
* Given a FunctionTypeAnnotation, it returns an array of its parameters.
|
||||
* @parameter functionTypeAnnotation: a FunctionTypeAnnotation
|
||||
|
|
|
@ -20,6 +20,8 @@ import type {
|
|||
NativeModuleParamTypeAnnotation,
|
||||
} from '../CodegenSchema';
|
||||
|
||||
// $FlowFixMe[untyped-import] there's no flowtype flow-parser
|
||||
const flowParser = require('flow-parser');
|
||||
const {
|
||||
UnsupportedObjectPropertyTypeAnnotationParserError,
|
||||
} = require('./errors');
|
||||
|
@ -89,6 +91,12 @@ export class MockedParser implements Parser {
|
|||
};
|
||||
}
|
||||
|
||||
getAst(contents: string): $FlowFixMe {
|
||||
return flowParser.parse(contents, {
|
||||
enums: true,
|
||||
});
|
||||
}
|
||||
|
||||
getFunctionTypeAnnotationParameters(
|
||||
functionTypeAnnotation: $FlowFixMe,
|
||||
): $ReadOnlyArray<$FlowFixMe> {
|
||||
|
|
|
@ -27,6 +27,13 @@ import type {
|
|||
import type {Parser} from './parser';
|
||||
import type {ParserType} from './errors';
|
||||
import type {ParserErrorCapturer, TypeDeclarationMap} from './utils';
|
||||
import type {ComponentSchemaBuilderConfig} from './flow/components/schema';
|
||||
|
||||
const {
|
||||
getConfigType,
|
||||
extractNativeModuleName,
|
||||
createParserErrorCapturer,
|
||||
} = require('./utils');
|
||||
|
||||
const {
|
||||
throwIfPropertyValueTypeIsUnsupported,
|
||||
|
@ -365,6 +372,98 @@ function buildPropertySchema(
|
|||
};
|
||||
}
|
||||
|
||||
function buildSchemaFromConfigType(
|
||||
configType: 'module' | 'component' | 'none',
|
||||
filename: ?string,
|
||||
ast: $FlowFixMe,
|
||||
wrapComponentSchema: (config: ComponentSchemaBuilderConfig) => SchemaType,
|
||||
buildComponentSchema: (ast: $FlowFixMe) => ComponentSchemaBuilderConfig,
|
||||
buildModuleSchema: (
|
||||
hasteModuleName: string,
|
||||
ast: $FlowFixMe,
|
||||
tryParse: ParserErrorCapturer,
|
||||
parser: Parser,
|
||||
) => NativeModuleSchema,
|
||||
parser: Parser,
|
||||
): SchemaType {
|
||||
switch (configType) {
|
||||
case 'component': {
|
||||
return wrapComponentSchema(buildComponentSchema(ast));
|
||||
}
|
||||
case 'module': {
|
||||
if (filename === undefined || filename === null) {
|
||||
throw new Error('Filepath expected while parasing a module');
|
||||
}
|
||||
const nativeModuleName = extractNativeModuleName(filename);
|
||||
|
||||
const [parsingErrors, tryParse] = createParserErrorCapturer();
|
||||
|
||||
const schema = tryParse(() =>
|
||||
buildModuleSchema(nativeModuleName, ast, tryParse, parser),
|
||||
);
|
||||
|
||||
if (parsingErrors.length > 0) {
|
||||
/**
|
||||
* TODO(T77968131): We have two options:
|
||||
* - Throw the first error, but indicate there are more then one errors.
|
||||
* - Display all errors, nicely formatted.
|
||||
*
|
||||
* For the time being, we're just throw the first error.
|
||||
**/
|
||||
|
||||
throw parsingErrors[0];
|
||||
}
|
||||
|
||||
invariant(
|
||||
schema != null,
|
||||
'When there are no parsing errors, the schema should not be null',
|
||||
);
|
||||
|
||||
return wrapModuleSchema(schema, nativeModuleName);
|
||||
}
|
||||
default:
|
||||
return {modules: {}};
|
||||
}
|
||||
}
|
||||
|
||||
function buildSchema(
|
||||
contents: string,
|
||||
filename: ?string,
|
||||
wrapComponentSchema: (config: ComponentSchemaBuilderConfig) => SchemaType,
|
||||
buildComponentSchema: (ast: $FlowFixMe) => ComponentSchemaBuilderConfig,
|
||||
buildModuleSchema: (
|
||||
hasteModuleName: string,
|
||||
ast: $FlowFixMe,
|
||||
tryParse: ParserErrorCapturer,
|
||||
parser: Parser,
|
||||
) => NativeModuleSchema,
|
||||
Visitor: ({isComponent: boolean, isModule: boolean}) => {
|
||||
[type: string]: (node: $FlowFixMe) => void,
|
||||
},
|
||||
parser: Parser,
|
||||
): SchemaType {
|
||||
// Early return for non-Spec JavaScript files
|
||||
if (
|
||||
!contents.includes('codegenNativeComponent') &&
|
||||
!contents.includes('TurboModule')
|
||||
) {
|
||||
return {modules: {}};
|
||||
}
|
||||
|
||||
const ast = parser.getAst(contents);
|
||||
const configType = getConfigType(ast, Visitor);
|
||||
|
||||
return buildSchemaFromConfigType(
|
||||
configType,
|
||||
filename,
|
||||
ast,
|
||||
wrapComponentSchema,
|
||||
buildComponentSchema,
|
||||
buildModuleSchema,
|
||||
parser,
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
wrapModuleSchema,
|
||||
unwrapNullable,
|
||||
|
@ -375,4 +474,6 @@ module.exports = {
|
|||
translateDefault,
|
||||
translateFunctionTypeAnnotation,
|
||||
buildPropertySchema,
|
||||
buildSchemaFromConfigType,
|
||||
buildSchema,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const {isModuleRegistryCall} = require('../utils');
|
||||
|
||||
function Visitor(infoMap: {isComponent: boolean, isModule: boolean}): {
|
||||
[type: string]: (node: $FlowFixMe) => void,
|
||||
} {
|
||||
return {
|
||||
CallExpression(node: $FlowFixMe) {
|
||||
if (
|
||||
node.callee.type === 'Identifier' &&
|
||||
node.callee.name === 'codegenNativeComponent'
|
||||
) {
|
||||
infoMap.isComponent = true;
|
||||
}
|
||||
|
||||
if (isModuleRegistryCall(node)) {
|
||||
infoMap.isModule = true;
|
||||
}
|
||||
},
|
||||
|
||||
TSInterfaceDeclaration(node: $FlowFixMe) {
|
||||
if (
|
||||
Array.isArray(node.extends) &&
|
||||
node.extends.some(
|
||||
extension => extension.expression.name === 'TurboModule',
|
||||
)
|
||||
) {
|
||||
infoMap.isModule = true;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Visitor,
|
||||
};
|
|
@ -1,88 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {SchemaType} from '../../CodegenSchema';
|
||||
import type {Parser} from '../parser';
|
||||
|
||||
// $FlowFixMe[untyped-import] Use flow-types for @babel/parser
|
||||
const babelParser = require('@babel/parser');
|
||||
const {
|
||||
buildSchemaFromConfigType,
|
||||
getConfigType,
|
||||
isModuleRegistryCall,
|
||||
} = require('../utils');
|
||||
const {buildComponentSchema} = require('./components');
|
||||
const {wrapComponentSchema} = require('./components/schema');
|
||||
const {buildModuleSchema} = require('./modules');
|
||||
|
||||
function Visitor(infoMap: {isComponent: boolean, isModule: boolean}) {
|
||||
return {
|
||||
CallExpression(node: $FlowFixMe) {
|
||||
if (
|
||||
node.callee.type === 'Identifier' &&
|
||||
node.callee.name === 'codegenNativeComponent'
|
||||
) {
|
||||
infoMap.isComponent = true;
|
||||
}
|
||||
|
||||
if (isModuleRegistryCall(node)) {
|
||||
infoMap.isModule = true;
|
||||
}
|
||||
},
|
||||
|
||||
TSInterfaceDeclaration(node: $FlowFixMe) {
|
||||
if (
|
||||
Array.isArray(node.extends) &&
|
||||
node.extends.some(
|
||||
extension => extension.expression.name === 'TurboModule',
|
||||
)
|
||||
) {
|
||||
infoMap.isModule = true;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildSchema(
|
||||
contents: string,
|
||||
filename: ?string,
|
||||
parser: Parser,
|
||||
): SchemaType {
|
||||
// Early return for non-Spec JavaScript files
|
||||
if (
|
||||
!contents.includes('codegenNativeComponent') &&
|
||||
!contents.includes('TurboModule')
|
||||
) {
|
||||
return {modules: {}};
|
||||
}
|
||||
|
||||
const ast = babelParser.parse(contents, {
|
||||
sourceType: 'module',
|
||||
plugins: ['typescript'],
|
||||
}).program;
|
||||
|
||||
const configType = getConfigType(ast, Visitor);
|
||||
|
||||
return buildSchemaFromConfigType(
|
||||
configType,
|
||||
filename,
|
||||
ast,
|
||||
wrapComponentSchema,
|
||||
buildComponentSchema,
|
||||
buildModuleSchema,
|
||||
parser,
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildSchema,
|
||||
};
|
|
@ -13,19 +13,32 @@
|
|||
import type {SchemaType} from '../../CodegenSchema.js';
|
||||
|
||||
const fs = require('fs');
|
||||
const {buildSchema} = require('./buildSchema');
|
||||
|
||||
const {buildSchema} = require('../parsers-commons');
|
||||
const {Visitor} = require('./Visitor');
|
||||
const {TypeScriptParser} = require('./parser');
|
||||
const {buildComponentSchema} = require('./components');
|
||||
const {wrapComponentSchema} = require('./components/schema');
|
||||
const {buildModuleSchema} = require('./modules');
|
||||
|
||||
const parser = new TypeScriptParser();
|
||||
|
||||
function parseModuleFixture(filename: string): SchemaType {
|
||||
const contents = fs.readFileSync(filename, 'utf8');
|
||||
|
||||
return buildSchema(contents, 'path/NativeSampleTurboModule.ts', parser);
|
||||
return parseString(contents, 'path/NativeSampleTurboModule.ts');
|
||||
}
|
||||
|
||||
function parseString(contents: string, filename: ?string): SchemaType {
|
||||
return buildSchema(contents, filename, parser);
|
||||
return buildSchema(
|
||||
contents,
|
||||
filename,
|
||||
wrapComponentSchema,
|
||||
buildComponentSchema,
|
||||
buildModuleSchema,
|
||||
Visitor,
|
||||
parser,
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -20,7 +20,14 @@ import type {
|
|||
import type {ParserType} from '../errors';
|
||||
import type {Parser} from '../parser';
|
||||
|
||||
const {buildSchema} = require('./buildSchema');
|
||||
// $FlowFixMe[untyped-import] Use flow-types for @babel/parser
|
||||
const babelParser = require('@babel/parser');
|
||||
|
||||
const {buildSchema} = require('../parsers-commons');
|
||||
const {Visitor} = require('./Visitor');
|
||||
const {buildComponentSchema} = require('./components');
|
||||
const {wrapComponentSchema} = require('./components/schema');
|
||||
const {buildModuleSchema} = require('./modules');
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
|
@ -95,7 +102,22 @@ class TypeScriptParser implements Parser {
|
|||
parseFile(filename: string): SchemaType {
|
||||
const contents = fs.readFileSync(filename, 'utf8');
|
||||
|
||||
return buildSchema(contents, filename, this);
|
||||
return buildSchema(
|
||||
contents,
|
||||
filename,
|
||||
wrapComponentSchema,
|
||||
buildComponentSchema,
|
||||
buildModuleSchema,
|
||||
Visitor,
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
getAst(contents: string): $FlowFixMe {
|
||||
return babelParser.parse(contents, {
|
||||
sourceType: 'module',
|
||||
plugins: ['typescript'],
|
||||
}).program;
|
||||
}
|
||||
|
||||
getFunctionTypeAnnotationParameters(
|
||||
|
|
|
@ -10,15 +10,9 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
import type {ComponentSchemaBuilderConfig} from './flow/components/schema';
|
||||
import type {NativeModuleSchema, SchemaType} from '../CodegenSchema';
|
||||
import type {Parser} from './parser';
|
||||
|
||||
const {ParserError} = require('./errors');
|
||||
const {wrapModuleSchema} = require('./parsers-commons');
|
||||
|
||||
const path = require('path');
|
||||
const invariant = require('invariant');
|
||||
|
||||
export type TypeDeclarationMap = {[declarationName: string]: $FlowFixMe};
|
||||
|
||||
|
@ -125,60 +119,6 @@ function visit(
|
|||
}
|
||||
}
|
||||
|
||||
function buildSchemaFromConfigType(
|
||||
configType: 'module' | 'component' | 'none',
|
||||
filename: ?string,
|
||||
ast: $FlowFixMe,
|
||||
wrapComponentSchema: (config: ComponentSchemaBuilderConfig) => SchemaType,
|
||||
buildComponentSchema: (ast: $FlowFixMe) => ComponentSchemaBuilderConfig,
|
||||
buildModuleSchema: (
|
||||
hasteModuleName: string,
|
||||
ast: $FlowFixMe,
|
||||
tryParse: ParserErrorCapturer,
|
||||
parser: Parser,
|
||||
) => NativeModuleSchema,
|
||||
parser: Parser,
|
||||
): SchemaType {
|
||||
switch (configType) {
|
||||
case 'component': {
|
||||
return wrapComponentSchema(buildComponentSchema(ast));
|
||||
}
|
||||
case 'module': {
|
||||
if (filename === undefined || filename === null) {
|
||||
throw new Error('Filepath expected while parasing a module');
|
||||
}
|
||||
const nativeModuleName = extractNativeModuleName(filename);
|
||||
|
||||
const [parsingErrors, tryParse] = createParserErrorCapturer();
|
||||
|
||||
const schema = tryParse(() =>
|
||||
buildModuleSchema(nativeModuleName, ast, tryParse, parser),
|
||||
);
|
||||
|
||||
if (parsingErrors.length > 0) {
|
||||
/**
|
||||
* TODO(T77968131): We have two options:
|
||||
* - Throw the first error, but indicate there are more then one errors.
|
||||
* - Display all errors, nicely formatted.
|
||||
*
|
||||
* For the time being, we're just throw the first error.
|
||||
**/
|
||||
|
||||
throw parsingErrors[0];
|
||||
}
|
||||
|
||||
invariant(
|
||||
schema != null,
|
||||
'When there are no parsing errors, the schema should not be null',
|
||||
);
|
||||
|
||||
return wrapModuleSchema(schema, nativeModuleName);
|
||||
}
|
||||
default:
|
||||
return {modules: {}};
|
||||
}
|
||||
}
|
||||
|
||||
function getConfigType(
|
||||
// TODO(T71778680): Flow-type this node.
|
||||
ast: $FlowFixMe,
|
||||
|
@ -254,6 +194,5 @@ module.exports = {
|
|||
createParserErrorCapturer,
|
||||
verifyPlatforms,
|
||||
visit,
|
||||
buildSchemaFromConfigType,
|
||||
isModuleRegistryCall,
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче