Skip to content

Commit

Permalink
Support component syntax with flowToFlowDef translator
Browse files Browse the repository at this point in the history
Summary: Support converting `ComponentDeclaration` to `DeclareComponent` nodes via the `flowToFlowDef` translator. This will enable TS translation next.

Reviewed By: SamChou19815

Differential Revision: D49037183

fbshipit-source-id: e0e6a9af465af753d2c5563b9b32ea4f566d5ece
  • Loading branch information
pieterv authored and facebook-github-bot committed Sep 8, 2023
1 parent 47a88c3 commit cf0bfda
Show file tree
Hide file tree
Showing 6 changed files with 320 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -551,4 +551,50 @@ describe('flowToFlowDef', () => {
});
});
});
describe('ComponentDeclaration', () => {
it('export', async () => {
await expectTranslate(
`export component Foo() {}`,
`declare export component Foo();`,
);
});
it('export default', async () => {
await expectTranslate(
`export default component Foo() {}`,
`declare export default component Foo();`,
);
});
it('params', async () => {
await expectTranslate(
`export component Foo(foo: string, 'bar' as BAR?: string) {}`,
`declare export component Foo(foo: string, 'bar'?: string);`,
);
});
it('default params', async () => {
await expectTranslate(
`export component Foo(foo: string = '') {}`,
`declare export component Foo(foo?: string);`,
);
});
it('rest param', async () => {
await expectTranslate(
`export component Foo(...foo: {...}) {}`,
`declare export component Foo(...foo: {...});`,
);
});
it('destructured rest param', async () => {
await expectTranslate(
`export component Foo(...{foo}: {...}) {}`,
`declare export component Foo(...rest: {...});`,
);
});
it('renders type', async () => {
await expectTranslate(
`type T = Bar;
export component Foo() renders T {}`,
`type T = Bar;
declare export component Foo() renders T;`,
);
});
});
});
13 changes: 13 additions & 0 deletions tools/hermes-parser/js/flow-api-translator/src/flowDefToTSDef.js
Original file line number Diff line number Diff line change
Expand Up @@ -992,6 +992,13 @@ const getTransforms = (
];
}

case 'DeclareComponent': {
throw translationError(
node.declaration,
`Unsupported node of type \`${node.declaration.type}\``,
);
}

// Flow's declare export default Identifier is ambiguous.
// the Identifier might reference a type, or it might reference a value
// - If it's a value, then that's all good, TS supports that.
Expand Down Expand Up @@ -1128,6 +1135,12 @@ const getTransforms = (
exportKind: 'value',
},
];
case 'DeclareComponent': {
throw translationError(
node.declaration,
`Unsupported node of type \`${node.declaration.type}\``,
);
}
case 'DeclareFunction':
return [
{
Expand Down
146 changes: 143 additions & 3 deletions tools/hermes-parser/js/flow-api-translator/src/flowToFlowDef.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ import type {
ClassMember,
ClassPropertyNameComputed,
ClassPropertyNameNonComputed,
ComponentDeclaration,
ComponentParameter,
ComponentTypeParameter,
DeclareClass,
DeclareComponent,
DeclareFunction,
DeclareOpaqueType,
DeclareVariable,
Expand All @@ -43,6 +47,7 @@ import type {
ObjectTypeProperty,
OpaqueType,
Program,
RestElement,
Statement,
StringLiteral,
TypeAlias,
Expand Down Expand Up @@ -356,8 +361,12 @@ function convertStatement(
context: TranslationContext,
): TranslatedResult<ProgramStatement> {
switch (stmt.type) {
case 'ComponentDeclaration': {
const [result, deps] = convertComponentDeclaration(stmt, context);
return [result, deps];
}
case 'FunctionDeclaration': {
const [result, deps] = convertFunctionDeclation(stmt, context);
const [result, deps] = convertFunctionDeclaration(stmt, context);
return [result, deps];
}
case 'ClassDeclaration': {
Expand Down Expand Up @@ -608,8 +617,21 @@ function convertExportDeclaration(
context: TranslationContext,
): TranslatedResult<ProgramStatement> {
switch (decl.type) {
case 'ComponentDeclaration': {
const [declDecl, deps] = convertComponentDeclaration(decl, context);
return [
opts.default
? t.DeclareExportDefaultDeclaration({
declaration: declDecl,
})
: t.DeclareExportDeclarationNamedWithDeclaration({
declaration: declDecl,
}),
deps,
];
}
case 'FunctionDeclaration': {
const [declDecl, deps] = convertFunctionDeclation(decl, context);
const [declDecl, deps] = convertFunctionDeclaration(decl, context);
return [
opts.default
? t.DeclareExportDefaultDeclaration({
Expand Down Expand Up @@ -1069,8 +1091,126 @@ function convertClassMember(
}
}
}
function convertComponentDeclaration(
comp: ComponentDeclaration,
context: TranslationContext,
): TranslatedResult<DeclareComponent> {
const [resultTypeParams, typeParamsDeps] =
convertTypeParameterDeclarationOrNull(comp.typeParameters, context);
const [resultParams, resultRestParam, paramsAndRestDeps] =
convertComponentParameters(comp.params, context);
const [resultRendersType, rendersTypeDeps] = (() => {
const rendersType = comp.rendersType;
if (rendersType == null) {
return EMPTY_TRANSLATION_RESULT;
}
return [
asDetachedNode<TypeAnnotation>(rendersType),
analyzeTypeDependencies(rendersType, context),
];
})();
return [
t.DeclareComponent({
id: comp.id,
params: resultParams,
rest: resultRestParam,
typeParameters: resultTypeParams,
rendersType: resultRendersType,
}),
[...typeParamsDeps, ...paramsAndRestDeps, ...rendersTypeDeps],
];
}
type TranslatedComponentParametersResults = [
$ReadOnlyArray<DetachedNode<ComponentTypeParameter>>,
?DetachedNode<ComponentTypeParameter>,
TranslatedDeps,
];
function convertComponentParameters(
params: $ReadOnlyArray<ComponentParameter | RestElement>,
context: TranslationContext,
): TranslatedComponentParametersResults {
return params.reduce<TranslatedComponentParametersResults>(
([resultParams, restParam, paramsDeps], param) => {
switch (param.type) {
case 'ComponentParameter': {
let optional = false;
let local = param.local;
if (local.type === 'AssignmentPattern') {
local = local.left;
optional = true;
}
if (!optional && local.type === 'Identifier') {
optional = local.optional;
}
const [typeAnnotationType, typeDeps] = convertTypeAnnotation(
local.typeAnnotation,
param,
context,
);
const resultParam = t.ComponentTypeParameter({
name: asDetachedNode(param.name),
typeAnnotation: typeAnnotationType,
optional,
});
return [
[...resultParams, resultParam],
restParam,
[...paramsDeps, ...typeDeps],
];
}
case 'RestElement': {
if (restParam != null) {
throw translationError(
param,
`ComponentParameter: Multiple rest elements found`,
context,
);
}
const argument = param.argument;
if (
argument.type === 'AssignmentPattern' ||
argument.type === 'ArrayPattern' ||
argument.type === 'RestElement'
) {
throw translationError(
param,
`ComponentParameter: Invalid RestElement usage`,
context,
);
}
const [typeAnnotationType, typeDeps] = convertTypeAnnotation(
argument.typeAnnotation,
argument,
context,
);
const resultRestParam = t.ComponentTypeParameter({
name: t.Identifier({
name: argument.type === 'Identifier' ? argument.name : 'rest',
}),
typeAnnotation: typeAnnotationType,
optional:
argument.type === 'Identifier' ? argument.optional : false,
});
return [resultParams, resultRestParam, [...paramsDeps, ...typeDeps]];
}
}
},
[[], null, []],
);
}
function convertFunctionDeclation(
function convertFunctionDeclaration(
func: FunctionDeclaration,
context: TranslationContext,
): TranslatedResult<DeclareFunction> {
Expand Down
7 changes: 6 additions & 1 deletion tools/hermes-parser/js/hermes-estree/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -1715,7 +1715,11 @@ interface DeclareExportDeclarationBase extends BaseNode {
export interface DeclareExportDefaultDeclaration
extends DeclareExportDeclarationBase {
+type: 'DeclareExportDeclaration';
+declaration: DeclareClass | DeclareFunction | TypeAnnotationType;
+declaration:
| DeclareClass
| DeclareFunction
| DeclareComponent
| TypeAnnotationType;
+default: true;
// default cannot have a source
+source: null;
Expand All @@ -1728,6 +1732,7 @@ export interface DeclareExportDeclarationNamedWithDeclaration
+declaration:
| DeclareClass
| DeclareFunction
| DeclareComponent
| DeclareInterface
| DeclareOpaqueType
| DeclareVariable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,24 @@ describe('DeclareComponent', () => {
);
});
});

describe('Export', () => {
const code = `
declare export component Foo();
declare export default component Bar();
`;

test('ESTree', async () => {
expect(await parseForSnapshotESTree(code)).toMatchSnapshot();
expect(await printForSnapshotESTree(code)).toBe(code.trim());
});

test('Babel', async () => {
expect(await parseForSnapshotBabel(code)).toMatchSnapshot();
expect(await printForSnapshotBabel(code)).toMatchInlineSnapshot(`
"declare export var Foo: any;
declare export default var Bar: any;"
`);
});
});
});
Loading

0 comments on commit cf0bfda

Please sign in to comment.