Skip to content

Commit 165dfbc

Browse files
charlesbdudleyfacebook-github-bot
authored andcommitted
Copy Flow parser foundation for TypeScript parser
Summary: This is a direct copy of the following files from the flow parser: ``` react-native-codegen/src/parsers/flow/errors.js react-native-codegen/src/parsers/flow/utils.js ``` Changelog: [Internal][Added] - Copy flow parser foundation files to aid in diff review Reviewed By: RSNara Differential Revision: D33137685 fbshipit-source-id: 1345c8bb0785c90b2bd64d4e6e2447f3fdb0ae6b
1 parent 3e69022 commit 165dfbc

File tree

2 files changed

+246
-0
lines changed

2 files changed

+246
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its 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+
* @flow strict-local
8+
* @format
9+
*/
10+
11+
'use strict';
12+
13+
class ParserError extends Error {
14+
nodes: $ReadOnlyArray<$FlowFixMe>;
15+
constructor(
16+
hasteModuleName: string,
17+
astNodeOrNodes: $FlowFixMe,
18+
message: string,
19+
) {
20+
super(`Module ${hasteModuleName}: ${message}`);
21+
22+
this.nodes = Array.isArray(astNodeOrNodes)
23+
? astNodeOrNodes
24+
: [astNodeOrNodes];
25+
26+
// assign the error class name in your custom error (as a shortcut)
27+
this.name = this.constructor.name;
28+
29+
// capturing the stack trace keeps the reference to your error class
30+
Error.captureStackTrace(this, this.constructor);
31+
}
32+
}
33+
34+
module.exports = {
35+
ParserError,
36+
};
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its 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+
* @flow strict-local
8+
* @format
9+
*/
10+
11+
'use strict';
12+
13+
const {ParserError} = require('./errors');
14+
15+
/**
16+
* This FlowFixMe is supposed to refer to an InterfaceDeclaration or TypeAlias
17+
* declaration type. Unfortunately, we don't have those types, because flow-parser
18+
* generates them, and flow-parser is not type-safe. In the future, we should find
19+
* a way to get these types from our flow parser library.
20+
*
21+
* TODO(T71778680): Flow type AST Nodes
22+
*/
23+
export type TypeDeclarationMap = {[declarationName: string]: $FlowFixMe};
24+
25+
function getTypes(ast: $FlowFixMe): TypeDeclarationMap {
26+
return ast.body.reduce((types, node) => {
27+
if (node.type === 'ExportNamedDeclaration' && node.exportKind === 'type') {
28+
if (
29+
node.declaration.type === 'TypeAlias' ||
30+
node.declaration.type === 'InterfaceDeclaration'
31+
) {
32+
types[node.declaration.id.name] = node.declaration;
33+
}
34+
} else if (
35+
node.type === 'TypeAlias' ||
36+
node.type === 'InterfaceDeclaration'
37+
) {
38+
types[node.id.name] = node;
39+
}
40+
return types;
41+
}, {});
42+
}
43+
44+
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
45+
export type ASTNode = Object;
46+
47+
const invariant = require('invariant');
48+
49+
type TypeAliasResolutionStatus =
50+
| $ReadOnly<{
51+
successful: true,
52+
aliasName: string,
53+
}>
54+
| $ReadOnly<{
55+
successful: false,
56+
}>;
57+
58+
function resolveTypeAnnotation(
59+
// TODO(T71778680): This is an Flow TypeAnnotation. Flow-type this
60+
typeAnnotation: $FlowFixMe,
61+
types: TypeDeclarationMap,
62+
): {
63+
nullable: boolean,
64+
typeAnnotation: $FlowFixMe,
65+
typeAliasResolutionStatus: TypeAliasResolutionStatus,
66+
} {
67+
invariant(
68+
typeAnnotation != null,
69+
'resolveTypeAnnotation(): typeAnnotation cannot be null',
70+
);
71+
72+
let node = typeAnnotation;
73+
let nullable = false;
74+
let typeAliasResolutionStatus: TypeAliasResolutionStatus = {
75+
successful: false,
76+
};
77+
78+
for (;;) {
79+
if (node.type === 'NullableTypeAnnotation') {
80+
nullable = true;
81+
node = node.typeAnnotation;
82+
} else if (node.type === 'GenericTypeAnnotation') {
83+
typeAliasResolutionStatus = {
84+
successful: true,
85+
aliasName: node.id.name,
86+
};
87+
const resolvedTypeAnnotation = types[node.id.name];
88+
if (resolvedTypeAnnotation == null) {
89+
break;
90+
}
91+
92+
invariant(
93+
resolvedTypeAnnotation.type === 'TypeAlias',
94+
`GenericTypeAnnotation '${node.id.name}' must resolve to a TypeAlias. Instead, it resolved to a '${resolvedTypeAnnotation.type}'`,
95+
);
96+
97+
node = resolvedTypeAnnotation.right;
98+
} else {
99+
break;
100+
}
101+
}
102+
103+
return {
104+
nullable: nullable,
105+
typeAnnotation: node,
106+
typeAliasResolutionStatus,
107+
};
108+
}
109+
110+
function getValueFromTypes(value: ASTNode, types: TypeDeclarationMap): ASTNode {
111+
if (value.type === 'GenericTypeAnnotation' && types[value.id.name]) {
112+
return getValueFromTypes(types[value.id.name].right, types);
113+
}
114+
return value;
115+
}
116+
117+
export type ParserErrorCapturer = <T>(fn: () => T) => ?T;
118+
119+
function createParserErrorCapturer(): [
120+
Array<ParserError>,
121+
ParserErrorCapturer,
122+
] {
123+
const errors = [];
124+
function guard<T>(fn: () => T): ?T {
125+
try {
126+
return fn();
127+
} catch (error) {
128+
if (!(error instanceof ParserError)) {
129+
throw error;
130+
}
131+
errors.push(error);
132+
133+
return null;
134+
}
135+
}
136+
137+
return [errors, guard];
138+
}
139+
140+
// TODO(T71778680): Flow-type ASTNodes.
141+
function visit(
142+
astNode: $FlowFixMe,
143+
visitor: {
144+
[type: string]: (node: $FlowFixMe) => void,
145+
},
146+
) {
147+
const queue = [astNode];
148+
while (queue.length !== 0) {
149+
let item = queue.shift();
150+
151+
if (!(typeof item === 'object' && item != null)) {
152+
continue;
153+
}
154+
155+
if (
156+
typeof item.type === 'string' &&
157+
typeof visitor[item.type] === 'function'
158+
) {
159+
// Don't visit any children
160+
visitor[item.type](item);
161+
} else if (Array.isArray(item)) {
162+
queue.push(...item);
163+
} else {
164+
queue.push(...Object.values(item));
165+
}
166+
}
167+
}
168+
169+
// TODO(T71778680): Flow-type ASTNodes.
170+
function isModuleRegistryCall(node: $FlowFixMe): boolean {
171+
if (node.type !== 'CallExpression') {
172+
return false;
173+
}
174+
175+
const callExpression = node;
176+
177+
if (callExpression.callee.type !== 'MemberExpression') {
178+
return false;
179+
}
180+
181+
const memberExpression = callExpression.callee;
182+
if (
183+
!(
184+
memberExpression.object.type === 'Identifier' &&
185+
memberExpression.object.name === 'TurboModuleRegistry'
186+
)
187+
) {
188+
return false;
189+
}
190+
191+
if (
192+
!(
193+
memberExpression.property.type === 'Identifier' &&
194+
(memberExpression.property.name === 'get' ||
195+
memberExpression.property.name === 'getEnforcing')
196+
)
197+
) {
198+
return false;
199+
}
200+
return true;
201+
}
202+
203+
module.exports = {
204+
getValueFromTypes,
205+
resolveTypeAnnotation,
206+
createParserErrorCapturer,
207+
getTypes,
208+
visit,
209+
isModuleRegistryCall,
210+
};

0 commit comments

Comments
 (0)