add handleGenericTypeAnnotation in parser-commons (#37525)

Summary:
Part of https://github.com/facebook/react-native/issues/34872
Move the switch construct from [parsers/typescript/utils.js](e133100721/packages/react-native-codegen/src/parsers/typescript/utils.js (L59-L93)) and [parsers/flow/utils.js](e133100721/packages/react-native-codegen/src/parsers/flow/utils.js (L56-L81)) to the parsers-commons.js file, in a handleGenericTypeAnnotation function. Use that function in place of the switch.

## 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][Added]: Added handleGenericTypeAnnotation in parsers-commons

Pull Request resolved: https://github.com/facebook/react-native/pull/37525

Test Plan: `yarn test`

Reviewed By: cortinico

Differential Revision: D46264650

Pulled By: cipolleschi

fbshipit-source-id: a315ee8cad24d91c9e98e5533d4cdc8b43ebc9a0
This commit is contained in:
tarunrajput 2023-06-06 02:31:51 -07:00 коммит произвёл Facebook GitHub Bot
Родитель 0af806e96c
Коммит 8ffaede05a
6 изменённых файлов: 263 добавлений и 65 удалений

Просмотреть файл

@ -25,6 +25,8 @@ import {
getCommandOptions,
getOptions,
getCommandTypeNameAndOptionsExpression,
getTypeResolutionStatus,
handleGenericTypeAnnotation,
} from '../parsers-commons';
import type {ParserType} from '../errors';
@ -1544,3 +1546,120 @@ describe('getCommandTypeNameAndOptionsExpression', () => {
});
});
});
describe('getTypeResolutionStatus', () => {
it('returns type resolution status for a type declaration', () => {
const typeAnnotation = {
id: {
name: 'TypeAnnotationName',
},
};
expect(
getTypeResolutionStatus('alias', typeAnnotation, flowParser),
).toEqual({
successful: true,
type: 'alias',
name: 'TypeAnnotationName',
});
});
it('returns type resolution status for an enum declaration', () => {
const typeAnnotation = {
id: {
name: 'TypeAnnotationName',
},
};
expect(getTypeResolutionStatus('enum', typeAnnotation, flowParser)).toEqual(
{
successful: true,
type: 'enum',
name: 'TypeAnnotationName',
},
);
});
});
describe('handleGenericTypeAnnotation', () => {
it('returns when TypeAnnotation is a type declaration', () => {
const typeAnnotation = {
id: {
name: 'TypeAnnotationName',
},
};
const resolvedTypeAnnotation = {
type: 'TypeAlias',
right: {
type: 'TypeAnnotation',
},
};
expect(
handleGenericTypeAnnotation(
typeAnnotation,
resolvedTypeAnnotation,
flowParser,
),
).toEqual({
typeAnnotation: {
type: 'TypeAnnotation',
},
typeResolutionStatus: {
successful: true,
type: 'alias',
name: 'TypeAnnotationName',
},
});
});
it('returns when TypeAnnotation is an enum declaration', () => {
const typeAnnotation = {
id: {
name: 'TypeAnnotationName',
},
};
const resolvedTypeAnnotation = {
type: 'EnumDeclaration',
body: {
type: 'TypeAnnotation',
},
};
expect(
handleGenericTypeAnnotation(
typeAnnotation,
resolvedTypeAnnotation,
flowParser,
),
).toEqual({
typeAnnotation: {
type: 'TypeAnnotation',
},
typeResolutionStatus: {
successful: true,
type: 'enum',
name: 'TypeAnnotationName',
},
});
});
it('throws when the non GenericTypeAnnotation is unsupported', () => {
const typeAnnotation = {
type: 'UnsupportedTypeAnnotation',
id: {
name: 'UnsupportedType',
},
};
const resolvedTypeAnnotation = {
type: 'UnsupportedTypeAnnotation',
};
expect(() =>
handleGenericTypeAnnotation(
typeAnnotation,
resolvedTypeAnnotation,
flowParser,
),
).toThrow(
new Error(
parser.genericTypeAnnotationErrorMessage(resolvedTypeAnnotation),
),
);
});
});

Просмотреть файл

@ -55,11 +55,15 @@ const {flowTranslateTypeAnnotation} = require('./modules');
// $FlowFixMe[untyped-import] there's no flowtype flow-parser
const flowParser = require('flow-parser');
const {buildSchema, buildPropSchema} = require('../parsers-commons');
const {
buildSchema,
buildPropSchema,
buildModuleSchema,
handleGenericTypeAnnotation,
} = require('../parsers-commons');
const {Visitor} = require('../parsers-primitives');
const {buildComponentSchema} = require('./components');
const {wrapComponentSchema} = require('../schema.js');
const {buildModuleSchema} = require('../parsers-commons.js');
const fs = require('fs');
@ -411,36 +415,16 @@ class FlowParser implements Parser {
break;
}
const resolvedTypeAnnotation = types[node.id.name];
const typeAnnotationName = this.nameForGenericTypeAnnotation(node);
const resolvedTypeAnnotation = types[typeAnnotationName];
if (resolvedTypeAnnotation == null) {
break;
}
switch (resolvedTypeAnnotation.type) {
case parser.typeAlias: {
typeResolutionStatus = {
successful: true,
type: 'alias',
name: node.id.name,
};
node = resolvedTypeAnnotation.right;
break;
}
case parser.enumDeclaration: {
typeResolutionStatus = {
successful: true,
type: 'enum',
name: node.id.name,
};
node = resolvedTypeAnnotation.body;
break;
}
default: {
throw new TypeError(
`A non GenericTypeAnnotation must be a type declaration ('${parser.typeAlias}') or enum ('${parser.enumDeclaration}'). Instead, got the unsupported ${resolvedTypeAnnotation.type}.`,
);
}
}
const {typeAnnotation: typeAnnotationNode, typeResolutionStatus: status} =
handleGenericTypeAnnotation(node, resolvedTypeAnnotation, this);
typeResolutionStatus = status;
node = typeAnnotationNode;
}
return {
@ -531,6 +515,18 @@ class FlowParser implements Parser {
);
}
}
nextNodeForTypeAlias(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.right;
}
nextNodeForEnum(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.body;
}
genericTypeAnnotationErrorMessage(typeAnnotation: $FlowFixMe): string {
return `A non GenericTypeAnnotation must be a type declaration ('${this.typeAlias}') or enum ('${this.enumDeclaration}'). Instead, got the unsupported ${typeAnnotation.type}.`;
}
}
module.exports = {

Просмотреть файл

@ -378,4 +378,19 @@ export interface Parser {
};
getProperties(typeName: string, types: TypeDeclarationMap): $FlowFixMe;
/**
* Given a typeAlias, it returns the next node.
*/
nextNodeForTypeAlias(typeAnnotation: $FlowFixMe): $FlowFixMe;
/**
* Given an enum Declaration, it returns the next node.
*/
nextNodeForEnum(typeAnnotation: $FlowFixMe): $FlowFixMe;
/**
* Given a unsupported typeAnnotation, returns an error message.
*/
genericTypeAnnotationErrorMessage(typeAnnotation: $FlowFixMe): string;
}

Просмотреть файл

@ -456,4 +456,16 @@ export class MockedParser implements Parser {
);
}
}
nextNodeForTypeAlias(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.right;
}
nextNodeForEnum(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.body;
}
genericTypeAnnotationErrorMessage(typeAnnotation: $FlowFixMe): string {
return `A non GenericTypeAnnotation must be a type declaration ('${this.typeAlias}') or enum ('${this.enumDeclaration}'). Instead, got the unsupported ${typeAnnotation.type}.`;
}
}

Просмотреть файл

@ -30,7 +30,12 @@ import type {
import type {Parser} from './parser';
import type {ParserType} from './errors';
import type {ParserErrorCapturer, TypeDeclarationMap, PropAST} from './utils';
import type {
ParserErrorCapturer,
TypeDeclarationMap,
PropAST,
TypeResolutionStatus,
} from './utils';
import type {ComponentSchemaBuilderConfig} from './schema.js';
const {
@ -983,6 +988,71 @@ function getCommandProperties(ast: $FlowFixMe, parser: Parser) {
return properties;
}
function getTypeResolutionStatus(
type: 'alias' | 'enum',
typeAnnotation: $FlowFixMe,
parser: Parser,
): TypeResolutionStatus {
return {
successful: true,
type,
name: parser.nameForGenericTypeAnnotation(typeAnnotation),
};
}
function handleGenericTypeAnnotation(
typeAnnotation: $FlowFixMe,
resolvedTypeAnnotation: TypeDeclarationMap,
parser: Parser,
): {
typeAnnotation: $FlowFixMe,
typeResolutionStatus: TypeResolutionStatus,
} {
let typeResolutionStatus;
let node;
switch (resolvedTypeAnnotation.type) {
case parser.typeAlias: {
typeResolutionStatus = getTypeResolutionStatus(
'alias',
typeAnnotation,
parser,
);
node = parser.nextNodeForTypeAlias(resolvedTypeAnnotation);
break;
}
case parser.enumDeclaration: {
typeResolutionStatus = getTypeResolutionStatus(
'enum',
typeAnnotation,
parser,
);
node = parser.nextNodeForEnum(resolvedTypeAnnotation);
break;
}
// parser.interfaceDeclaration is not used here because for flow it should fall through to default case and throw an error
case 'TSInterfaceDeclaration': {
typeResolutionStatus = getTypeResolutionStatus(
'alias',
typeAnnotation,
parser,
);
node = resolvedTypeAnnotation;
break;
}
default: {
throw new TypeError(
parser.genericTypeAnnotationErrorMessage(resolvedTypeAnnotation),
);
}
}
return {
typeAnnotation: node,
typeResolutionStatus,
};
}
module.exports = {
wrapModuleSchema,
unwrapNullable,
@ -1007,4 +1077,6 @@ module.exports = {
getEventArgument,
findComponentConfig,
getCommandProperties,
handleGenericTypeAnnotation,
getTypeResolutionStatus,
};

Просмотреть файл

@ -43,14 +43,15 @@ const {typeScriptTranslateTypeAnnotation} = require('./modules');
// $FlowFixMe[untyped-import] Use flow-types for @babel/parser
const babelParser = require('@babel/parser');
const {buildSchema} = require('../parsers-commons');
const {Visitor} = require('../parsers-primitives');
const {buildComponentSchema} = require('./components');
const {wrapComponentSchema} = require('../schema.js');
const {
buildSchema,
buildModuleSchema,
extendsForProp,
buildPropSchema,
handleGenericTypeAnnotation,
} = require('../parsers-commons.js');
const {parseTopLevelType} = require('./parseTopLevelType');
@ -408,45 +409,16 @@ class TypeScriptParser implements Parser {
break;
}
const resolvedTypeAnnotation = types[node.typeName.name];
const typeAnnotationName = this.nameForGenericTypeAnnotation(node);
const resolvedTypeAnnotation = types[typeAnnotationName];
if (resolvedTypeAnnotation == null) {
break;
}
switch (resolvedTypeAnnotation.type) {
case parser.typeAlias: {
typeResolutionStatus = {
successful: true,
type: 'alias',
name: node.typeName.name,
};
node = resolvedTypeAnnotation.typeAnnotation;
break;
}
case parser.interfaceDeclaration: {
typeResolutionStatus = {
successful: true,
type: 'alias',
name: node.typeName.name,
};
node = resolvedTypeAnnotation;
break;
}
case parser.enumDeclaration: {
typeResolutionStatus = {
successful: true,
type: 'enum',
name: node.typeName.name,
};
node = resolvedTypeAnnotation;
break;
}
default: {
throw new TypeError(
`A non GenericTypeAnnotation must be a type declaration ('${parser.typeAlias}'), an interface ('${parser.interfaceDeclaration}'), or enum ('${parser.enumDeclaration}'). Instead, got the unsupported ${resolvedTypeAnnotation.type}.`,
);
}
}
const {typeAnnotation: typeAnnotationNode, typeResolutionStatus: status} =
handleGenericTypeAnnotation(node, resolvedTypeAnnotation, this);
typeResolutionStatus = status;
node = typeAnnotationNode;
}
return {
@ -559,6 +531,18 @@ class TypeScriptParser implements Parser {
);
}
}
nextNodeForTypeAlias(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation.typeAnnotation;
}
nextNodeForEnum(typeAnnotation: $FlowFixMe): $FlowFixMe {
return typeAnnotation;
}
genericTypeAnnotationErrorMessage(typeAnnotation: $FlowFixMe): string {
return `A non GenericTypeAnnotation must be a type declaration ('${this.typeAlias}'), an interface ('${this.interfaceDeclaration}'), or enum ('${this.enumDeclaration}'). Instead, got the unsupported ${typeAnnotation.type}.`;
}
}
module.exports = {