diff --git a/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js b/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js index ff8661b28a69b1..0d84786df8f2b6 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js @@ -426,26 +426,6 @@ function getTypeAnnotation( } } -function isProp(name: string, typeAnnotation: $FlowFixMe) { - if (typeAnnotation.type === 'TSTypeReference') { - // Remove unwanted types - if ( - typeAnnotation.typeName.name === 'DirectEventHandler' || - typeAnnotation.typeName.name === 'BubblingEventHandler' - ) { - return false; - } - if ( - name === 'style' && - typeAnnotation.type === 'GenericTypeAnnotation' && - typeAnnotation.typeName.name === 'ViewStyleProp' - ) { - return false; - } - } - return true; -} - type SchemaInfo = { name: string, optional: boolean, @@ -456,7 +436,7 @@ type SchemaInfo = { function getSchemaInfo( property: PropAST, types: TypeDeclarationMap, -): ?SchemaInfo { +): SchemaInfo { // unpack WithDefault, (T) or T|U const topLevelType = parseTopLevelType( property.typeAnnotation.typeAnnotation, @@ -464,9 +444,6 @@ function getSchemaInfo( ); const name = property.key.name; - if (!isProp(name, topLevelType.type)) { - return null; - } if (!property.optional && topLevelType.defaultValue !== undefined) { throw new Error( diff --git a/packages/react-native-codegen/src/parsers/typescript/components/events.js b/packages/react-native-codegen/src/parsers/typescript/components/events.js index 94114362255e38..0fbfb4b71f442b 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/events.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/events.js @@ -197,34 +197,18 @@ function getEventArgument(argumentProps, name: $FlowFixMe) { }; } -function isEvent(typeAnnotation: $FlowFixMe) { - switch (typeAnnotation.type) { - case 'TSTypeReference': - if ( - typeAnnotation.typeName.name !== 'BubblingEventHandler' && - typeAnnotation.typeName.name !== 'DirectEventHandler' - ) { - return false; - } else { - return true; - } - default: - return false; - } -} +// $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser +type EventTypeAST = Object; function buildEventSchema( types: TypeDeclarationMap, property: EventTypeAST, -): ?EventTypeShape { +): EventTypeShape { // unpack WithDefault, (T) or T|U const topLevelType = parseTopLevelType( property.typeAnnotation.typeAnnotation, types, ); - if (!isEvent(topLevelType.type)) { - return null; - } const name = property.key.name; const typeAnnotation = topLevelType.type; @@ -232,7 +216,11 @@ function buildEventSchema( const {argumentProps, bubblingType, paperTopLevelNameDeprecated} = findEventArgumentsAndType(typeAnnotation, types); - if (bubblingType && argumentProps) { + if (!argumentProps) { + throw new Error(`Unable to determine event arguments for "${name}"`); + } else if (!bubblingType) { + throw new Error(`Unable to determine event bubbling type for "${name}"`); + } else { if (paperTopLevelNameDeprecated != null) { return { name, @@ -256,27 +244,13 @@ function buildEventSchema( }, }; } - - if (argumentProps === null) { - throw new Error(`Unable to determine event arguments for "${name}"`); - } - - if (bubblingType === null) { - throw new Error(`Unable to determine event arguments for "${name}"`); - } } -// $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser -type EventTypeAST = Object; - function getEvents( eventTypeAST: $ReadOnlyArray, types: TypeDeclarationMap, ): $ReadOnlyArray { - return eventTypeAST - .filter(property => property.type === 'TSPropertySignature') - .map(property => buildEventSchema(types, property)) - .filter(Boolean); + return eventTypeAST.map(property => buildEventSchema(types, property)); } module.exports = { diff --git a/packages/react-native-codegen/src/parsers/typescript/components/extends.js b/packages/react-native-codegen/src/parsers/typescript/components/extends.js index 84ad505dd1fcb9..e9fda8c46ee752 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/extends.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/extends.js @@ -12,6 +12,8 @@ import type {ExtendsPropsShape} from '../../../CodegenSchema.js'; import type {TypeDeclarationMap} from '../../utils'; +const {parseTopLevelType} = require('../parseTopLevelType'); +const {flattenProperties} = require('./componentsUtils.js'); function extendsForProp(prop: PropsAST, types: TypeDeclarationMap) { if (!prop.expression) { @@ -36,31 +38,66 @@ function extendsForProp(prop: PropsAST, types: TypeDeclarationMap) { } } -function removeKnownExtends( - typeDefinition: $ReadOnlyArray, - types: TypeDeclarationMap, -): $ReadOnlyArray { - return typeDefinition.filter( - prop => - prop.type !== 'TSExpressionWithTypeArguments' || - extendsForProp(prop, types) === null, - ); +function isEvent(typeAnnotation: $FlowFixMe): boolean { + if (typeAnnotation.type !== 'TSTypeReference') { + return false; + } + const eventNames = new Set(['BubblingEventHandler', 'DirectEventHandler']); + return eventNames.has(typeAnnotation.typeName.name); +} + +function isProp(name: string, typeAnnotation: $FlowFixMe): boolean { + if (typeAnnotation.type !== 'TSTypeReference') { + return true; + } + const isStyle = + name === 'style' && + typeAnnotation.type === 'GenericTypeAnnotation' && + typeAnnotation.typeName.name === 'ViewStyleProp'; + return !isStyle; } // $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser type PropsAST = Object; -function getExtendsProps( +function categorizeProps( typeDefinition: $ReadOnlyArray, types: TypeDeclarationMap, -): $ReadOnlyArray { - return typeDefinition - .filter(prop => prop.type === 'TSExpressionWithTypeArguments') - .map(prop => extendsForProp(prop, types)) - .filter(Boolean); + extendsProps: Array, + props: Array, + events: Array, +): void { + const remaining: Array = []; + for (const prop of typeDefinition) { + // find extends + if (prop.type === 'TSExpressionWithTypeArguments') { + const extend = extendsForProp(prop, types); + if (extend) { + extendsProps.push(extend); + continue; + } + } + + remaining.push(prop); + } + + // find events and props + for (const prop of flattenProperties(remaining, types)) { + if (prop.type === 'TSPropertySignature') { + const topLevelType = parseTopLevelType( + prop.typeAnnotation.typeAnnotation, + types, + ); + + if (isEvent(topLevelType.type)) { + events.push(prop); + } else if (isProp(prop.key.name, prop)) { + props.push(prop); + } + } + } } module.exports = { - getExtendsProps, - removeKnownExtends, + categorizeProps, }; diff --git a/packages/react-native-codegen/src/parsers/typescript/components/index.js b/packages/react-native-codegen/src/parsers/typescript/components/index.js index 0c4cc4d7c29382..46cbba5dd505e7 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/index.js @@ -9,6 +9,7 @@ */ 'use strict'; +import type {ExtendsPropsShape} from '../../../CodegenSchema.js'; import type {TypeDeclarationMap} from '../../utils'; import type {CommandOptions} from './options'; import type {ComponentSchemaBuilderConfig} from './schema.js'; @@ -16,7 +17,7 @@ import type {ComponentSchemaBuilderConfig} from './schema.js'; const {getTypes} = require('../utils'); const {getCommands} = require('./commands'); const {getEvents} = require('./events'); -const {getExtendsProps, removeKnownExtends} = require('./extends'); +const {categorizeProps} = require('./extends'); const {getCommandOptions, getOptions} = require('./options'); const {getProps} = require('./props'); const {getProperties} = require('./componentsUtils.js'); @@ -180,6 +181,9 @@ function getCommandProperties( return properties; } +// $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser +type PropsAST = Object; + // $FlowFixMe[signature-verification-failure] TODO(T108222691): Use flow-types for @babel/parser /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's * LTI update could not be added via codemod */ @@ -203,12 +207,20 @@ function buildComponentSchema(ast): ComponentSchemaBuilderConfig { commandOptions, ); - const extendsProps = getExtendsProps(propProperties, types); const options = getOptions(optionsExpression); - const nonExtendsProps = removeKnownExtends(propProperties, types); - const props = getProps(nonExtendsProps, types); - const events = getEvents(propProperties, types); + const extendsProps: Array = []; + const componentPropAsts: Array = []; + const componentEventAsts: Array = []; + categorizeProps( + propProperties, + types, + extendsProps, + componentPropAsts, + componentEventAsts, + ); + const props = getProps(componentPropAsts, types); + const events = getEvents(componentEventAsts, types); const commands = getCommands(commandProperties, types); return { diff --git a/packages/react-native-codegen/src/parsers/typescript/components/props.js b/packages/react-native-codegen/src/parsers/typescript/components/props.js index b4a201816325d1..a2b1bcfad482f3 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/props.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/props.js @@ -9,11 +9,7 @@ */ 'use strict'; -const { - flattenProperties, - getSchemaInfo, - getTypeAnnotation, -} = require('./componentsUtils.js'); +const {getSchemaInfo, getTypeAnnotation} = require('./componentsUtils.js'); import type {NamedShape, PropTypeAnnotation} from '../../../CodegenSchema.js'; import type {TypeDeclarationMap} from '../../utils'; @@ -24,11 +20,8 @@ type PropAST = Object; function buildPropSchema( property: PropAST, types: TypeDeclarationMap, -): ?NamedShape { +): NamedShape { const info = getSchemaInfo(property, types); - if (info == null) { - return null; - } const {name, optional, typeAnnotation, defaultValue} = info; return { name, @@ -47,9 +40,7 @@ function getProps( typeDefinition: $ReadOnlyArray, types: TypeDeclarationMap, ): $ReadOnlyArray> { - return flattenProperties(typeDefinition, types) - .map(property => buildPropSchema(property, types)) - .filter(Boolean); + return typeDefinition.map(property => buildPropSchema(property, types)); } module.exports = {