mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
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
This commit is contained in:
committed by
Facebook GitHub Bot
parent
3e6902244a
commit
165dfbcc87
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
class ParserError extends Error {
|
||||
nodes: $ReadOnlyArray<$FlowFixMe>;
|
||||
constructor(
|
||||
hasteModuleName: string,
|
||||
astNodeOrNodes: $FlowFixMe,
|
||||
message: string,
|
||||
) {
|
||||
super(`Module ${hasteModuleName}: ${message}`);
|
||||
|
||||
this.nodes = Array.isArray(astNodeOrNodes)
|
||||
? astNodeOrNodes
|
||||
: [astNodeOrNodes];
|
||||
|
||||
// assign the error class name in your custom error (as a shortcut)
|
||||
this.name = this.constructor.name;
|
||||
|
||||
// capturing the stack trace keeps the reference to your error class
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ParserError,
|
||||
};
|
||||
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const {ParserError} = require('./errors');
|
||||
|
||||
/**
|
||||
* This FlowFixMe is supposed to refer to an InterfaceDeclaration or TypeAlias
|
||||
* declaration type. Unfortunately, we don't have those types, because flow-parser
|
||||
* generates them, and flow-parser is not type-safe. In the future, we should find
|
||||
* a way to get these types from our flow parser library.
|
||||
*
|
||||
* TODO(T71778680): Flow type AST Nodes
|
||||
*/
|
||||
export type TypeDeclarationMap = {[declarationName: string]: $FlowFixMe};
|
||||
|
||||
function getTypes(ast: $FlowFixMe): TypeDeclarationMap {
|
||||
return ast.body.reduce((types, node) => {
|
||||
if (node.type === 'ExportNamedDeclaration' && node.exportKind === 'type') {
|
||||
if (
|
||||
node.declaration.type === 'TypeAlias' ||
|
||||
node.declaration.type === 'InterfaceDeclaration'
|
||||
) {
|
||||
types[node.declaration.id.name] = node.declaration;
|
||||
}
|
||||
} else if (
|
||||
node.type === 'TypeAlias' ||
|
||||
node.type === 'InterfaceDeclaration'
|
||||
) {
|
||||
types[node.id.name] = node;
|
||||
}
|
||||
return types;
|
||||
}, {});
|
||||
}
|
||||
|
||||
// $FlowFixMe[unclear-type] there's no flowtype for ASTs
|
||||
export type ASTNode = Object;
|
||||
|
||||
const invariant = require('invariant');
|
||||
|
||||
type TypeAliasResolutionStatus =
|
||||
| $ReadOnly<{
|
||||
successful: true,
|
||||
aliasName: string,
|
||||
}>
|
||||
| $ReadOnly<{
|
||||
successful: false,
|
||||
}>;
|
||||
|
||||
function resolveTypeAnnotation(
|
||||
// TODO(T71778680): This is an Flow TypeAnnotation. Flow-type this
|
||||
typeAnnotation: $FlowFixMe,
|
||||
types: TypeDeclarationMap,
|
||||
): {
|
||||
nullable: boolean,
|
||||
typeAnnotation: $FlowFixMe,
|
||||
typeAliasResolutionStatus: TypeAliasResolutionStatus,
|
||||
} {
|
||||
invariant(
|
||||
typeAnnotation != null,
|
||||
'resolveTypeAnnotation(): typeAnnotation cannot be null',
|
||||
);
|
||||
|
||||
let node = typeAnnotation;
|
||||
let nullable = false;
|
||||
let typeAliasResolutionStatus: TypeAliasResolutionStatus = {
|
||||
successful: false,
|
||||
};
|
||||
|
||||
for (;;) {
|
||||
if (node.type === 'NullableTypeAnnotation') {
|
||||
nullable = true;
|
||||
node = node.typeAnnotation;
|
||||
} else if (node.type === 'GenericTypeAnnotation') {
|
||||
typeAliasResolutionStatus = {
|
||||
successful: true,
|
||||
aliasName: node.id.name,
|
||||
};
|
||||
const resolvedTypeAnnotation = types[node.id.name];
|
||||
if (resolvedTypeAnnotation == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
invariant(
|
||||
resolvedTypeAnnotation.type === 'TypeAlias',
|
||||
`GenericTypeAnnotation '${node.id.name}' must resolve to a TypeAlias. Instead, it resolved to a '${resolvedTypeAnnotation.type}'`,
|
||||
);
|
||||
|
||||
node = resolvedTypeAnnotation.right;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
nullable: nullable,
|
||||
typeAnnotation: node,
|
||||
typeAliasResolutionStatus,
|
||||
};
|
||||
}
|
||||
|
||||
function getValueFromTypes(value: ASTNode, types: TypeDeclarationMap): ASTNode {
|
||||
if (value.type === 'GenericTypeAnnotation' && types[value.id.name]) {
|
||||
return getValueFromTypes(types[value.id.name].right, types);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export type ParserErrorCapturer = <T>(fn: () => T) => ?T;
|
||||
|
||||
function createParserErrorCapturer(): [
|
||||
Array<ParserError>,
|
||||
ParserErrorCapturer,
|
||||
] {
|
||||
const errors = [];
|
||||
function guard<T>(fn: () => T): ?T {
|
||||
try {
|
||||
return fn();
|
||||
} catch (error) {
|
||||
if (!(error instanceof ParserError)) {
|
||||
throw error;
|
||||
}
|
||||
errors.push(error);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return [errors, guard];
|
||||
}
|
||||
|
||||
// TODO(T71778680): Flow-type ASTNodes.
|
||||
function visit(
|
||||
astNode: $FlowFixMe,
|
||||
visitor: {
|
||||
[type: string]: (node: $FlowFixMe) => void,
|
||||
},
|
||||
) {
|
||||
const queue = [astNode];
|
||||
while (queue.length !== 0) {
|
||||
let item = queue.shift();
|
||||
|
||||
if (!(typeof item === 'object' && item != null)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof item.type === 'string' &&
|
||||
typeof visitor[item.type] === 'function'
|
||||
) {
|
||||
// Don't visit any children
|
||||
visitor[item.type](item);
|
||||
} else if (Array.isArray(item)) {
|
||||
queue.push(...item);
|
||||
} else {
|
||||
queue.push(...Object.values(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(T71778680): Flow-type ASTNodes.
|
||||
function isModuleRegistryCall(node: $FlowFixMe): boolean {
|
||||
if (node.type !== 'CallExpression') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const callExpression = node;
|
||||
|
||||
if (callExpression.callee.type !== 'MemberExpression') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const memberExpression = callExpression.callee;
|
||||
if (
|
||||
!(
|
||||
memberExpression.object.type === 'Identifier' &&
|
||||
memberExpression.object.name === 'TurboModuleRegistry'
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
!(
|
||||
memberExpression.property.type === 'Identifier' &&
|
||||
(memberExpression.property.name === 'get' ||
|
||||
memberExpression.property.name === 'getEnforcing')
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getValueFromTypes,
|
||||
resolveTypeAnnotation,
|
||||
createParserErrorCapturer,
|
||||
getTypes,
|
||||
visit,
|
||||
isModuleRegistryCall,
|
||||
};
|
||||
Reference in New Issue
Block a user