Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor codegen: Dispatch props and events from a central place. #34975

Closed
wants to merge 18 commits into from
Closed
Original file line number Diff line number Diff line change
Expand Up @@ -426,26 +426,6 @@ function getTypeAnnotation<T>(
}
}

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,
Expand All @@ -456,17 +436,14 @@ type SchemaInfo = {
function getSchemaInfo(
property: PropAST,
types: TypeDeclarationMap,
): ?SchemaInfo {
): SchemaInfo {
// unpack WithDefault, (T) or T|U
const topLevelType = parseTopLevelType(
property.typeAnnotation.typeAnnotation,
types,
);

const name = property.key.name;
if (!isProp(name, topLevelType.type)) {
return null;
}

if (!property.optional && topLevelType.defaultValue !== undefined) {
throw new Error(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,42 +197,30 @@ 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;
const optional = property.optional || topLevelType.optional;
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,
Expand All @@ -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<EventTypeAST>,
types: TypeDeclarationMap,
): $ReadOnlyArray<EventTypeShape> {
return eventTypeAST
.filter(property => property.type === 'TSPropertySignature')
.map(property => buildEventSchema(types, property))
.filter(Boolean);
return eventTypeAST.map(property => buildEventSchema(types, property));
}

module.exports = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -36,31 +38,66 @@ function extendsForProp(prop: PropsAST, types: TypeDeclarationMap) {
}
}

function removeKnownExtends(
typeDefinition: $ReadOnlyArray<PropsAST>,
types: TypeDeclarationMap,
): $ReadOnlyArray<PropsAST> {
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<PropsAST>,
types: TypeDeclarationMap,
): $ReadOnlyArray<ExtendsPropsShape> {
return typeDefinition
.filter(prop => prop.type === 'TSExpressionWithTypeArguments')
.map(prop => extendsForProp(prop, types))
.filter(Boolean);
extendsProps: Array<ExtendsPropsShape>,
props: Array<PropsAST>,
events: Array<PropsAST>,
): void {
const remaining: Array<PropsAST> = [];
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,
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
*/

'use strict';
import type {ExtendsPropsShape} from '../../../CodegenSchema.js';
import type {TypeDeclarationMap} from '../../utils';
import type {CommandOptions} from './options';
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');
Expand Down Expand Up @@ -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 */
Expand All @@ -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<ExtendsPropsShape> = [];
const componentPropAsts: Array<PropsAST> = [];
const componentEventAsts: Array<PropsAST> = [];
categorizeProps(
propProperties,
types,
extendsProps,
componentPropAsts,
componentEventAsts,
);
const props = getProps(componentPropAsts, types);
const events = getEvents(componentEventAsts, types);
const commands = getCommands(commandProperties, types);

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -24,11 +20,8 @@ type PropAST = Object;
function buildPropSchema(
property: PropAST,
types: TypeDeclarationMap,
): ?NamedShape<PropTypeAnnotation> {
): NamedShape<PropTypeAnnotation> {
const info = getSchemaInfo(property, types);
if (info == null) {
return null;
}
const {name, optional, typeAnnotation, defaultValue} = info;
return {
name,
Expand All @@ -47,9 +40,7 @@ function getProps(
typeDefinition: $ReadOnlyArray<PropAST>,
types: TypeDeclarationMap,
): $ReadOnlyArray<NamedShape<PropTypeAnnotation>> {
return flattenProperties(typeDefinition, types)
.map(property => buildPropSchema(property, types))
.filter(Boolean);
return typeDefinition.map(property => buildPropSchema(property, types));
}

module.exports = {
Expand Down