flow-api-translator 0.36.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -48,6 +48,7 @@ import type {
48
48
  ObjectExpression,
49
49
  ObjectTypeAnnotation,
50
50
  ObjectTypeProperty,
51
+ ObjectTypeSpreadProperty,
51
52
  OpaqueType,
52
53
  QualifiedTypeIdentifier,
53
54
  QualifiedTypeofIdentifier,
@@ -88,12 +89,14 @@ import {
88
89
  isMemberExpressionWithNonComputedProperty,
89
90
  } from 'hermes-estree';
90
91
 
91
- const EMPTY_TRANSLATION_RESULT = [null, []];
92
+ const EMPTY_TRANSLATION_RESULT: TranslatedResultOrNull<empty> = [null, []];
92
93
 
93
- type TranslatedDeps = $ReadOnlyArray<Dep>;
94
- type TranslatedResultOrNull<T> = [DetachedNode<T> | null, TranslatedDeps];
94
+ type TranslatedDeps = ReadonlyArray<Dep>;
95
+ type TranslatedResultOrNull<out T> = Readonly<
96
+ [DetachedNode<T> | null, TranslatedDeps],
97
+ >;
95
98
  type TranslatedResultArray<T> = [
96
- $ReadOnlyArray<DetachedNode<T>>,
99
+ ReadonlyArray<DetachedNode<T>>,
97
100
  TranslatedDeps,
98
101
  ];
99
102
  type TranslatedResult<T> = [DetachedNode<T>, TranslatedDeps];
@@ -101,7 +104,7 @@ type TranslatedResult<T> = [DetachedNode<T>, TranslatedDeps];
101
104
  type ProgramStatement = Statement | ModuleDeclaration;
102
105
 
103
106
  function convertArray<TIn, TOut>(
104
- items: $ReadOnlyArray<TIn>,
107
+ items: ReadonlyArray<TIn>,
105
108
  convert: TIn => TranslatedResultOrNull<TOut>,
106
109
  ): TranslatedResultArray<TOut> {
107
110
  const resultItems: Array<DetachedNode<TOut>> = [];
@@ -406,6 +409,10 @@ function convertStatement(
406
409
  return [result, deps];
407
410
  }
408
411
  case 'VariableDeclaration': {
412
+ const requireImport = convertRequireToImport(stmt);
413
+ if (requireImport != null) {
414
+ return requireImport;
415
+ }
409
416
  const [result, deps] = convertVariableDeclaration(stmt, context);
410
417
  return [result, deps];
411
418
  }
@@ -483,12 +490,12 @@ function convertExpressionToTypeAnnotation(
483
490
  }
484
491
  }
485
492
 
486
- function inheritComments<T: DetachedNode<ESNode>>(
493
+ function inheritComments<T extends DetachedNode<ESNode>>(
487
494
  fromNode: ESNode,
488
495
  toNode: T,
489
496
  ): T {
490
497
  // $FlowFixMe[unclear-type]
491
- (toNode: any).comments = (fromNode: any).comments;
498
+ (toNode as any).comments = (fromNode as any).comments;
492
499
  return toNode;
493
500
  }
494
501
 
@@ -660,7 +667,7 @@ function convertLiteral(
660
667
  function convertExportDeclaration(
661
668
  decl:
662
669
  | ExportDefaultDeclaration['declaration']
663
- | $NonMaybeType<ExportNamedDeclaration['declaration']>,
670
+ | NonNullable<ExportNamedDeclaration['declaration']>,
664
671
  opts: {default: boolean},
665
672
  context: TranslationContext,
666
673
  ): TranslatedResult<ProgramStatement> {
@@ -828,8 +835,8 @@ function convertExportDefaultDeclaration(
828
835
  context: TranslationContext,
829
836
  ): TranslatedResult<ProgramStatement> {
830
837
  const expr = stmt.declaration;
831
- if (isExpression(expr) && (expr: $FlowFixMe).type === 'Identifier') {
832
- const name = ((expr: $FlowFixMe): Identifier).name;
838
+ if (isExpression(expr) && (expr as $FlowFixMe).type === 'Identifier') {
839
+ const name = (expr as $FlowFixMe as Identifier).name;
833
840
  const [declDecl, deps] = [
834
841
  t.TypeofTypeAnnotation({argument: t.Identifier({name})}),
835
842
  analyzeTypeDependencies(expr, context),
@@ -936,6 +943,78 @@ function convertVariableDeclaration(
936
943
  ];
937
944
  }
938
945
 
946
+ function convertRequireToImport(
947
+ stmt: VariableDeclaration,
948
+ ): ?TranslatedResult<ProgramStatement> {
949
+ if (stmt.declarations.length !== 1) {
950
+ return null;
951
+ }
952
+ const decl = stmt.declarations[0];
953
+ const init = decl.init;
954
+ if (
955
+ init == null ||
956
+ init.type !== 'CallExpression' ||
957
+ init.callee.type !== 'Identifier' ||
958
+ init.callee.name !== 'require' ||
959
+ init.arguments.length !== 1
960
+ ) {
961
+ return null;
962
+ }
963
+ const sourceArg = init.arguments[0];
964
+ if (!isStringLiteral(sourceArg)) {
965
+ return null;
966
+ }
967
+ const id = decl.id;
968
+
969
+ if (id.type === 'Identifier') {
970
+ return [
971
+ t.ImportDeclaration({
972
+ importKind: 'value',
973
+ source: asDetachedNode(sourceArg),
974
+ specifiers: [
975
+ t.ImportDefaultSpecifier({
976
+ local: t.Identifier({name: id.name}),
977
+ }),
978
+ ],
979
+ attributes: [],
980
+ }),
981
+ [],
982
+ ];
983
+ }
984
+
985
+ if (id.type === 'ObjectPattern') {
986
+ const specifiers = [];
987
+ for (const prop of id.properties) {
988
+ if (prop.type === 'RestElement') {
989
+ return null;
990
+ }
991
+ const key = prop.key;
992
+ const value = prop.value;
993
+ if (key.type !== 'Identifier' || value.type !== 'Identifier') {
994
+ return null;
995
+ }
996
+ specifiers.push(
997
+ t.ImportSpecifier({
998
+ imported: t.Identifier({name: key.name}),
999
+ local: t.Identifier({name: value.name}),
1000
+ importKind: null,
1001
+ }),
1002
+ );
1003
+ }
1004
+ return [
1005
+ t.ImportDeclaration({
1006
+ importKind: 'value',
1007
+ source: asDetachedNode(sourceArg),
1008
+ specifiers,
1009
+ attributes: [],
1010
+ }),
1011
+ [],
1012
+ ];
1013
+ }
1014
+
1015
+ return null;
1016
+ }
1017
+
939
1018
  function convertImportDeclaration(
940
1019
  stmt: ImportDeclaration,
941
1020
  context: TranslationContext,
@@ -1080,7 +1159,6 @@ function convertSuperClass(
1080
1159
  context: TranslationContext,
1081
1160
  ): TranslatedResultOrNull<InterfaceExtends> {
1082
1161
  if (superClass == null) {
1083
- // $FlowFixMe[incompatible-type]
1084
1162
  return EMPTY_TRANSLATION_RESULT;
1085
1163
  }
1086
1164
 
@@ -1175,7 +1253,15 @@ function convertClassMember(
1175
1253
  case 'PropertyDefinition': {
1176
1254
  // PrivateIdentifier's are not exposed so can be stripped.
1177
1255
  if (member.key.type === 'PrivateIdentifier') {
1178
- // $FlowFixMe[incompatible-type]
1256
+ return EMPTY_TRANSLATION_RESULT;
1257
+ }
1258
+ if (
1259
+ context.mungeUnderscores &&
1260
+ member.key.type === 'Identifier' &&
1261
+ member.key.name.length >= 2 &&
1262
+ member.key.name[0] === '_' &&
1263
+ member.key.name[1] !== '_'
1264
+ ) {
1179
1265
  return EMPTY_TRANSLATION_RESULT;
1180
1266
  }
1181
1267
  if (
@@ -1243,7 +1329,15 @@ function convertClassMember(
1243
1329
  case 'MethodDefinition': {
1244
1330
  // PrivateIdentifier's are not exposed so can be stripped.
1245
1331
  if (member.key.type === 'PrivateIdentifier') {
1246
- // $FlowFixMe[incompatible-type]
1332
+ return EMPTY_TRANSLATION_RESULT;
1333
+ }
1334
+ if (
1335
+ context.mungeUnderscores &&
1336
+ member.key.type === 'Identifier' &&
1337
+ member.key.name.length >= 2 &&
1338
+ member.key.name[0] === '_' &&
1339
+ member.key.name[1] !== '_'
1340
+ ) {
1247
1341
  return EMPTY_TRANSLATION_RESULT;
1248
1342
  }
1249
1343
  if (
@@ -1353,41 +1447,71 @@ function convertComponentDeclaration(
1353
1447
  }
1354
1448
 
1355
1449
  type TranslatedComponentParametersResults = [
1356
- $ReadOnlyArray<DetachedNode<ComponentTypeParameter>>,
1450
+ ReadonlyArray<DetachedNode<ComponentTypeParameter>>,
1357
1451
  ?DetachedNode<ComponentTypeParameter>,
1358
1452
  TranslatedDeps,
1359
1453
  ];
1360
1454
 
1455
+ function hasNonIdentifierStringLiteralParam(
1456
+ params: ReadonlyArray<ComponentParameter | RestElement>,
1457
+ ): boolean {
1458
+ return params.some(
1459
+ param =>
1460
+ param.type === 'ComponentParameter' &&
1461
+ isStringLiteral(param.name) &&
1462
+ !/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name.value),
1463
+ );
1464
+ }
1465
+
1466
+ function extractParamTypeInfo(
1467
+ param: ComponentParameter,
1468
+ context: TranslationContext,
1469
+ ): [boolean, BindingName, TranslatedResult<TypeAnnotationType>] {
1470
+ let optional = false;
1471
+ let local = param.local;
1472
+ if (local.type === 'AssignmentPattern') {
1473
+ local = local.left;
1474
+ optional = true;
1475
+ }
1476
+ if (!optional && local.type === 'Identifier') {
1477
+ optional = local.optional;
1478
+ }
1479
+ return [
1480
+ optional,
1481
+ local,
1482
+ convertTypeAnnotation(local.typeAnnotation, param, context),
1483
+ ];
1484
+ }
1485
+
1361
1486
  function convertComponentParameters(
1362
- params: $ReadOnlyArray<ComponentParameter | RestElement>,
1487
+ params: ReadonlyArray<ComponentParameter | RestElement>,
1363
1488
  context: TranslationContext,
1364
1489
  ): TranslatedComponentParametersResults {
1490
+ if (hasNonIdentifierStringLiteralParam(params)) {
1491
+ return convertComponentParametersToPropsObject(params, context);
1492
+ }
1493
+
1365
1494
  return params.reduce<TranslatedComponentParametersResults>(
1366
1495
  ([resultParams, restParam, paramsDeps], param) => {
1367
1496
  switch (param.type) {
1368
1497
  case 'ComponentParameter': {
1369
- let optional = false;
1370
- let local = param.local;
1371
- if (local.type === 'AssignmentPattern') {
1372
- local = local.left;
1373
- optional = true;
1374
- }
1375
- if (!optional && local.type === 'Identifier') {
1376
- optional = local.optional;
1377
- }
1378
-
1379
- const [typeAnnotationType, typeDeps] = convertTypeAnnotation(
1380
- local.typeAnnotation,
1498
+ const [optional, _local, [typeAnnotationType, typeDeps]] =
1499
+ extractParamTypeInfo(param, context);
1500
+
1501
+ const name =
1502
+ isStringLiteral(param.name) &&
1503
+ /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(param.name.value)
1504
+ ? t.Identifier({name: param.name.value})
1505
+ : asDetachedNode(param.name);
1506
+ const resultParam = inheritComments(
1381
1507
  param,
1382
- context,
1508
+ t.ComponentTypeParameter({
1509
+ name,
1510
+ typeAnnotation: typeAnnotationType,
1511
+ optional,
1512
+ }),
1383
1513
  );
1384
1514
 
1385
- const resultParam = t.ComponentTypeParameter({
1386
- name: asDetachedNode(param.name),
1387
- typeAnnotation: typeAnnotationType,
1388
- optional,
1389
- });
1390
-
1391
1515
  return [
1392
1516
  [...resultParams, resultParam],
1393
1517
  restParam,
@@ -1420,14 +1544,18 @@ function convertComponentParameters(
1420
1544
  context,
1421
1545
  );
1422
1546
 
1423
- const resultRestParam = t.ComponentTypeParameter({
1424
- name: t.Identifier({
1425
- name: argument.type === 'Identifier' ? argument.name : 'rest',
1547
+ const restName =
1548
+ argument.type === 'Identifier' ? argument.name : 'rest';
1549
+ const restOptional =
1550
+ argument.type === 'Identifier' ? argument.optional : false;
1551
+ const resultRestParam = inheritComments(
1552
+ param,
1553
+ t.ComponentTypeParameter({
1554
+ name: t.Identifier({name: restName}),
1555
+ typeAnnotation: typeAnnotationType,
1556
+ optional: restOptional,
1426
1557
  }),
1427
- typeAnnotation: typeAnnotationType,
1428
- optional:
1429
- argument.type === 'Identifier' ? argument.optional : false,
1430
- });
1558
+ );
1431
1559
 
1432
1560
  return [resultParams, resultRestParam, [...paramsDeps, ...typeDeps]];
1433
1561
  }
@@ -1437,6 +1565,72 @@ function convertComponentParameters(
1437
1565
  );
1438
1566
  }
1439
1567
 
1568
+ function convertComponentParametersToPropsObject(
1569
+ params: ReadonlyArray<ComponentParameter | RestElement>,
1570
+ context: TranslationContext,
1571
+ ): TranslatedComponentParametersResults {
1572
+ const properties: Array<
1573
+ DetachedNode<ObjectTypeProperty | ObjectTypeSpreadProperty>,
1574
+ > = [];
1575
+ const allDeps: Array<Dep> = [];
1576
+
1577
+ for (const param of params) {
1578
+ if (param.type === 'RestElement') {
1579
+ const argument = param.argument;
1580
+ if (
1581
+ argument.type !== 'AssignmentPattern' &&
1582
+ argument.type !== 'ArrayPattern' &&
1583
+ argument.type !== 'RestElement'
1584
+ ) {
1585
+ const [typeAnnotationType, typeDeps] = convertTypeAnnotation(
1586
+ argument.typeAnnotation,
1587
+ argument,
1588
+ context,
1589
+ );
1590
+ allDeps.push(...typeDeps);
1591
+ properties.push(
1592
+ t.ObjectTypeSpreadProperty({argument: typeAnnotationType}),
1593
+ );
1594
+ }
1595
+ continue;
1596
+ }
1597
+
1598
+ const [optional, _local, [typeAnnotationType, typeDeps]] =
1599
+ extractParamTypeInfo(param, context);
1600
+ allDeps.push(...typeDeps);
1601
+
1602
+ properties.push(
1603
+ inheritComments(
1604
+ param,
1605
+ t.ObjectTypePropertySignature({
1606
+ key: asDetachedNode(param.name),
1607
+ value: typeAnnotationType,
1608
+ optional,
1609
+ static: false,
1610
+ variance: null,
1611
+ }),
1612
+ ),
1613
+ );
1614
+ }
1615
+
1616
+ const propsType = t.ObjectTypeAnnotation({
1617
+ inexact: false,
1618
+ exact: false,
1619
+ properties,
1620
+ indexers: [],
1621
+ callProperties: [],
1622
+ internalSlots: [],
1623
+ });
1624
+
1625
+ const restParam = t.ComponentTypeParameter({
1626
+ name: t.Identifier({name: 'props'}),
1627
+ typeAnnotation: propsType,
1628
+ optional: false,
1629
+ });
1630
+
1631
+ return [[], restParam, allDeps];
1632
+ }
1633
+
1440
1634
  function convertHookDeclaration(
1441
1635
  hook: HookDeclaration,
1442
1636
  context: TranslationContext,
@@ -1461,7 +1655,7 @@ function convertHookDeclaration(
1461
1655
  const [resultTypeParams, typeParamsDeps] =
1462
1656
  convertTypeParameterDeclarationOrNull(hook.typeParameters, context);
1463
1657
 
1464
- const resultFunc = t.FunctionTypeAnnotation({
1658
+ const resultFunc = t.HookTypeAnnotation({
1465
1659
  params: resultParams,
1466
1660
  returnType: resultReturnType,
1467
1661
  rest: restParam,
@@ -1555,12 +1749,12 @@ function convertAFunction(
1555
1749
  }
1556
1750
 
1557
1751
  type TranslatedFunctionParametersResults = [
1558
- $ReadOnlyArray<DetachedNode<FunctionTypeParam>>,
1752
+ ReadonlyArray<DetachedNode<FunctionTypeParam>>,
1559
1753
  ?DetachedNode<FunctionTypeParam>,
1560
1754
  TranslatedDeps,
1561
1755
  ];
1562
1756
  function convertFunctionParameters(
1563
- params: $ReadOnlyArray<FunctionParameter>,
1757
+ params: ReadonlyArray<FunctionParameter>,
1564
1758
  context: TranslationContext,
1565
1759
  ): TranslatedFunctionParametersResults {
1566
1760
  return params.reduce<TranslatedFunctionParametersResults>(
@@ -1732,7 +1926,6 @@ function convertTypeAnnotationTypeOrNull(
1732
1926
  context: TranslationContext,
1733
1927
  ): TranslatedResultOrNull<TypeAnnotationType> {
1734
1928
  if (annot == null) {
1735
- // $FlowFixMe[incompatible-type]
1736
1929
  return EMPTY_TRANSLATION_RESULT;
1737
1930
  }
1738
1931
 
@@ -1743,7 +1936,6 @@ function convertTypeParameterDeclarationOrNull(
1743
1936
  context: TranslationContext,
1744
1937
  ): TranslatedResultOrNull<TypeParameterDeclaration> {
1745
1938
  if (decl == null) {
1746
- // $FlowFixMe[incompatible-type]
1747
1939
  return EMPTY_TRANSLATION_RESULT;
1748
1940
  }
1749
1941
  return [asDetachedNode(decl), analyzeTypeDependencies(decl, context)];
@@ -1753,7 +1945,6 @@ function convertTypeParameterInstantiationOrNull(
1753
1945
  context: TranslationContext,
1754
1946
  ): TranslatedResultOrNull<TypeParameterInstantiation> {
1755
1947
  if (inst == null) {
1756
- // $FlowFixMe[incompatible-type]
1757
1948
  return EMPTY_TRANSLATION_RESULT;
1758
1949
  }
1759
1950
  return [asDetachedNode(inst), analyzeTypeDependencies(inst, context)];
package/dist/flowToJS.js CHANGED
@@ -1,12 +1,3 @@
1
- /**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- *
8
- * @format
9
- */
10
1
  'use strict';
11
2
 
12
3
  Object.defineProperty(exports, "__esModule", {
package/dist/index.js CHANGED
@@ -1,12 +1,3 @@
1
- /**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- *
8
- * @format
9
- */
10
1
  'use strict';
11
2
 
12
3
  Object.defineProperty(exports, "__esModule", {
@@ -37,13 +28,14 @@ var _TSDefToFlowDef = require("./TSDefToFlowDef");
37
28
 
38
29
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
39
30
 
40
- async function translateFlowToFlowDef(code, prettierOptions = {}) {
31
+ async function translateFlowToFlowDef(code, prettierOptions = {}, opts) {
41
32
  const {
42
33
  ast,
43
34
  scopeManager
44
35
  } = await (0, _hermesTransform.parse)(code);
45
36
  const [flowDefAst, mutatedCode] = (0, _flowToFlowDef.default)(ast, code, scopeManager, {
46
- recoverFromErrors: true
37
+ recoverFromErrors: true,
38
+ mungeUnderscores: opts == null ? void 0 : opts.mungeUnderscores
47
39
  });
48
40
  return (0, _hermesTransform.print)(flowDefAst, mutatedCode, prettierOptions);
49
41
  }
@@ -61,8 +53,7 @@ async function translateFlowDefToTSDef(code, prettierOptions = {}) {
61
53
  const [tsAST, mutatedCode] = (0, _flowDefToTSDef.flowDefToTSDef)(code, ast, scopeManager, {
62
54
  recoverFromErrors: true
63
55
  });
64
- return (0, _hermesTransform.print)( // $FlowExpectedError[incompatible-type] - this is fine as we're providing the visitor keys
65
- tsAST, mutatedCode, { ...prettierOptions
56
+ return (0, _hermesTransform.print)(tsAST, mutatedCode, { ...prettierOptions
66
57
  }, _visitorKeys.visitorKeys);
67
58
  }
68
59
 
@@ -74,18 +65,6 @@ async function translateFlowToJS(code, prettierOptions = {}) {
74
65
  const jsAST = (0, _flowToJS.flowToJS)(ast, code, scopeManager);
75
66
  return (0, _hermesTransform.print)(jsAST, code, prettierOptions);
76
67
  }
77
- /**
78
- * This translator is very experimental and unstable.
79
- *
80
- * It is not written with productionizing it in mind, but instead used to evaluate how close Flow
81
- * is to TypeScript.
82
- *
83
- * If you are going to use it anyways, you agree that you are calling a potentially broken function
84
- * without any guarantee.
85
- *
86
- * @deprecated
87
- */
88
-
89
68
 
90
69
  async function unstable_translateTSDefToFlowDef(code, prettierOptions = {}) {
91
70
  const ast = (0, _parser.parse)(code, {
@@ -24,11 +24,13 @@ import {TSDefToFlowDef} from './TSDefToFlowDef';
24
24
  export async function translateFlowToFlowDef(
25
25
  code: string,
26
26
  prettierOptions: {...} = {},
27
+ opts?: {mungeUnderscores?: boolean},
27
28
  ): Promise<string> {
28
29
  const {ast, scopeManager} = await parse(code);
29
30
 
30
31
  const [flowDefAst, mutatedCode] = flowToFlowDef(ast, code, scopeManager, {
31
32
  recoverFromErrors: true,
33
+ mungeUnderscores: opts?.mungeUnderscores,
32
34
  });
33
35
 
34
36
  return print(flowDefAst, mutatedCode, prettierOptions);