/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * * @format */ 'use strict'; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _defineProperty(e, r, t) { return ( (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0, }) : (e[r] = t), e ); } function _toPropertyKey(t) { var i = _toPrimitive(t, 'string'); return 'symbol' == typeof i ? i : i + ''; } function _toPrimitive(t, r) { if ('object' != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || 'default'); if ('object' != typeof i) return i; throw new TypeError('@@toPrimitive must return a primitive value.'); } return ('string' === r ? String : Number)(t); } function _slicedToArray(r, e) { return ( _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest() ); } function _nonIterableRest() { throw new TypeError( 'Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.', ); } function _unsupportedIterableToArray(r, a) { if (r) { if ('string' == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return ( 'Object' === t && r.constructor && (t = r.constructor.name), 'Map' === t || 'Set' === t ? Array.from(r) : 'Arguments' === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0 ); } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : ('undefined' != typeof Symbol && r[Symbol.iterator]) || r['@@iterator']; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (((i = (t = t.call(r)).next), 0 === l)) { if (Object(t) !== t) return; f = !1; } else for ( ; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0 ); } catch (r) { (o = !0), (n = r); } finally { try { if (!f && null != t.return && ((u = t.return()), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } const _require = require('./error-utils'), throwIfConfigNotfound = _require.throwIfConfigNotfound, throwIfEventEmitterEventTypeIsUnsupported = _require.throwIfEventEmitterEventTypeIsUnsupported, throwIfEventEmitterTypeIsUnsupported = _require.throwIfEventEmitterTypeIsUnsupported, throwIfIncorrectModuleRegistryCallArgument = _require.throwIfIncorrectModuleRegistryCallArgument, throwIfIncorrectModuleRegistryCallTypeParameterParserError = _require.throwIfIncorrectModuleRegistryCallTypeParameterParserError, throwIfModuleInterfaceIsMisnamed = _require.throwIfModuleInterfaceIsMisnamed, throwIfModuleInterfaceNotFound = _require.throwIfModuleInterfaceNotFound, throwIfModuleTypeIsUnsupported = _require.throwIfModuleTypeIsUnsupported, throwIfMoreThanOneCodegenNativecommands = _require.throwIfMoreThanOneCodegenNativecommands, throwIfMoreThanOneConfig = _require.throwIfMoreThanOneConfig, throwIfMoreThanOneModuleInterfaceParserError = _require.throwIfMoreThanOneModuleInterfaceParserError, throwIfMoreThanOneModuleRegistryCalls = _require.throwIfMoreThanOneModuleRegistryCalls, throwIfPropertyValueTypeIsUnsupported = _require.throwIfPropertyValueTypeIsUnsupported, throwIfTypeAliasIsNotInterface = _require.throwIfTypeAliasIsNotInterface, throwIfUnsupportedFunctionParamTypeAnnotationParserError = _require.throwIfUnsupportedFunctionParamTypeAnnotationParserError, throwIfUnsupportedFunctionReturnTypeAnnotationParserError = _require.throwIfUnsupportedFunctionReturnTypeAnnotationParserError, throwIfUntypedModule = _require.throwIfUntypedModule, throwIfUnusedModuleInterfaceParserError = _require.throwIfUnusedModuleInterfaceParserError, throwIfWrongNumberOfCallExpressionArgs = _require.throwIfWrongNumberOfCallExpressionArgs; const _require2 = require('./errors'), MissingTypeParameterGenericParserError = _require2.MissingTypeParameterGenericParserError, MoreThanOneTypeParameterGenericParserError = _require2.MoreThanOneTypeParameterGenericParserError, UnnamedFunctionParamParserError = _require2.UnnamedFunctionParamParserError, UnsupportedObjectDirectRecursivePropertyParserError = _require2.UnsupportedObjectDirectRecursivePropertyParserError; const _require3 = require('./utils'), createParserErrorCapturer = _require3.createParserErrorCapturer, extractNativeModuleName = _require3.extractNativeModuleName, getConfigType = _require3.getConfigType, getSortedObject = _require3.getSortedObject, isModuleRegistryCall = _require3.isModuleRegistryCall, verifyPlatforms = _require3.verifyPlatforms, visit = _require3.visit; const invariant = require('invariant'); // $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser function wrapModuleSchema(nativeModuleSchema, hasteModuleName) { return { modules: { [hasteModuleName]: nativeModuleSchema, }, }; } // $FlowFixMe[unsupported-variance-annotation] function unwrapNullable(x) { if (x.type === 'NullableTypeAnnotation') { return [x.typeAnnotation, true]; } return [x, false]; } // $FlowFixMe[unsupported-variance-annotation] function wrapNullable(nullable, typeAnnotation) { if (!nullable) { return typeAnnotation; } return { type: 'NullableTypeAnnotation', typeAnnotation, }; } function assertGenericTypeAnnotationHasExactlyOneTypeParameter( moduleName, /** * TODO(T108222691): Use flow-types for @babel/parser */ typeAnnotation, parser, ) { if (typeAnnotation.typeParameters == null) { throw new MissingTypeParameterGenericParserError( moduleName, typeAnnotation, parser, ); } const typeAnnotationType = parser.typeParameterInstantiation; invariant( typeAnnotation.typeParameters.type === typeAnnotationType, `assertGenericTypeAnnotationHasExactlyOneTypeParameter: Type parameters must be an AST node of type '${typeAnnotationType}'`, ); if (typeAnnotation.typeParameters.params.length !== 1) { throw new MoreThanOneTypeParameterGenericParserError( moduleName, typeAnnotation, parser, ); } } function isObjectProperty(property, language) { switch (language) { case 'Flow': return property.type === 'ObjectTypeProperty'; case 'TypeScript': return property.type === 'TSPropertySignature'; default: return false; } } function getObjectTypeAnnotations( hasteModuleName, types, tryParse, translateTypeAnnotation, parser, ) { const aliasMap = {}; Object.entries(types).forEach(([key, value]) => { const isTypeAlias = value.type === 'TypeAlias' || value.type === 'TSTypeAliasDeclaration'; if (!isTypeAlias) { return; } const parent = parser.nextNodeForTypeAlias(value); if ( parent.type !== 'ObjectTypeAnnotation' && parent.type !== 'TSTypeLiteral' ) { return; } const typeProperties = parser .getAnnotatedElementProperties(value) .map(prop => parseObjectProperty( parent, prop, hasteModuleName, types, aliasMap, {}, // enumMap tryParse, true, // cxxOnly (prop === null || prop === void 0 ? void 0 : prop.optional) || false, translateTypeAnnotation, parser, ), ); aliasMap[key] = { type: 'ObjectTypeAnnotation', properties: typeProperties, }; }); return aliasMap; } function parseObjectProperty( parentObject, property, hasteModuleName, types, aliasMap, enumMap, tryParse, cxxOnly, nullable, translateTypeAnnotation, parser, ) { const language = parser.language(); const name = parser.getKeyName(property, hasteModuleName); const _property$optional = property.optional, optional = _property$optional === void 0 ? false : _property$optional; const languageTypeAnnotation = language === 'TypeScript' ? property.typeAnnotation.typeAnnotation : property.value; // Handle recursive types if (parentObject) { var _languageTypeAnnotati, _languageTypeAnnotati2; const propertyType = parser.getResolveTypeAnnotationFN()( languageTypeAnnotation, types, parser, ); if ( propertyType.typeResolutionStatus.successful === true && propertyType.typeResolutionStatus.type === 'alias' && (language === 'TypeScript' ? parentObject.typeName && parentObject.typeName.name === ((_languageTypeAnnotati = languageTypeAnnotation.typeName) === null || _languageTypeAnnotati === void 0 ? void 0 : _languageTypeAnnotati.name) : parentObject.id && parentObject.id.name === ((_languageTypeAnnotati2 = languageTypeAnnotation.id) === null || _languageTypeAnnotati2 === void 0 ? void 0 : _languageTypeAnnotati2.name)) ) { if (!optional) { throw new UnsupportedObjectDirectRecursivePropertyParserError( name, languageTypeAnnotation, hasteModuleName, ); } return { name, optional, typeAnnotation: { type: 'TypeAliasTypeAnnotation', name: propertyType.typeResolutionStatus.name, }, }; } } // Handle non-recursive types const _unwrapNullable = unwrapNullable( translateTypeAnnotation( hasteModuleName, languageTypeAnnotation, types, aliasMap, enumMap, tryParse, cxxOnly, parser, ), ), _unwrapNullable2 = _slicedToArray(_unwrapNullable, 2), propertyTypeAnnotation = _unwrapNullable2[0], isPropertyNullable = _unwrapNullable2[1]; if ( (propertyTypeAnnotation.type === 'FunctionTypeAnnotation' && !cxxOnly) || propertyTypeAnnotation.type === 'PromiseTypeAnnotation' || propertyTypeAnnotation.type === 'VoidTypeAnnotation' ) { throwIfPropertyValueTypeIsUnsupported( hasteModuleName, languageTypeAnnotation, property.key, propertyTypeAnnotation.type, ); } return { name, optional, typeAnnotation: wrapNullable(isPropertyNullable, propertyTypeAnnotation), }; } function translateFunctionTypeAnnotation( hasteModuleName, // TODO(T108222691): Use flow-types for @babel/parser // TODO(T71778680): This is a FunctionTypeAnnotation. Type this. functionTypeAnnotation, types, aliasMap, enumMap, tryParse, cxxOnly, translateTypeAnnotation, parser, ) { const params = []; for (const param of parser.getFunctionTypeAnnotationParameters( functionTypeAnnotation, )) { const parsedParam = tryParse(() => { if (parser.getFunctionNameFromParameter(param) == null) { throw new UnnamedFunctionParamParserError(param, hasteModuleName); } const paramName = parser.getParameterName(param); const _unwrapNullable3 = unwrapNullable( translateTypeAnnotation( hasteModuleName, parser.getParameterTypeAnnotation(param), types, aliasMap, enumMap, tryParse, cxxOnly, parser, ), ), _unwrapNullable4 = _slicedToArray(_unwrapNullable3, 2), paramTypeAnnotation = _unwrapNullable4[0], isParamTypeAnnotationNullable = _unwrapNullable4[1]; if ( paramTypeAnnotation.type === 'VoidTypeAnnotation' || paramTypeAnnotation.type === 'PromiseTypeAnnotation' ) { return throwIfUnsupportedFunctionParamTypeAnnotationParserError( hasteModuleName, param.typeAnnotation, paramName, paramTypeAnnotation.type, ); } return { name: paramName, optional: Boolean(param.optional), typeAnnotation: wrapNullable( isParamTypeAnnotationNullable, paramTypeAnnotation, ), }; }); if (parsedParam != null) { params.push(parsedParam); } } const _unwrapNullable5 = unwrapNullable( translateTypeAnnotation( hasteModuleName, parser.getFunctionTypeAnnotationReturnType(functionTypeAnnotation), types, aliasMap, enumMap, tryParse, cxxOnly, parser, ), ), _unwrapNullable6 = _slicedToArray(_unwrapNullable5, 2), returnTypeAnnotation = _unwrapNullable6[0], isReturnTypeAnnotationNullable = _unwrapNullable6[1]; throwIfUnsupportedFunctionReturnTypeAnnotationParserError( hasteModuleName, functionTypeAnnotation, 'FunctionTypeAnnotation', cxxOnly, returnTypeAnnotation.type, ); return { type: 'FunctionTypeAnnotation', returnTypeAnnotation: wrapNullable( isReturnTypeAnnotationNullable, returnTypeAnnotation, ), params, }; } function buildPropertySchema( hasteModuleName, // TODO(T108222691): [TS] Use flow-types for @babel/parser // TODO(T71778680): [Flow] This is an ObjectTypeProperty containing either: // - a FunctionTypeAnnotation or GenericTypeAnnotation // - a NullableTypeAnnoation containing a FunctionTypeAnnotation or GenericTypeAnnotation // Flow type this node property, types, aliasMap, enumMap, tryParse, cxxOnly, translateTypeAnnotation, parser, ) { let nullable = false; let key = property.key, value = property.value; const methodName = key.name; if (parser.language() === 'TypeScript') { value = property.type === 'TSMethodSignature' ? property : property.typeAnnotation; } const resolveTypeAnnotationFN = parser.getResolveTypeAnnotationFN(); var _resolveTypeAnnotatio = resolveTypeAnnotationFN(value, types, parser); nullable = _resolveTypeAnnotatio.nullable; value = _resolveTypeAnnotatio.typeAnnotation; throwIfModuleTypeIsUnsupported( hasteModuleName, property.value, key.name, value.type, parser, ); return { name: methodName, optional: Boolean(property.optional), typeAnnotation: wrapNullable( nullable, translateFunctionTypeAnnotation( hasteModuleName, value, types, aliasMap, enumMap, tryParse, cxxOnly, translateTypeAnnotation, parser, ), ), }; } function buildEventEmitterSchema( hasteModuleName, // TODO(T108222691): [TS] Use flow-types for @babel/parser // TODO(T71778680): [Flow] This is an ObjectTypeProperty containing either: // - a FunctionTypeAnnotation or GenericTypeAnnotation // - a NullableTypeAnnoation containing a FunctionTypeAnnotation or GenericTypeAnnotation // Flow type this node property, types, aliasMap, enumMap, tryParse, cxxOnly, translateTypeAnnotation, parser, ) { const key = property.key; const value = parser.language() === 'TypeScript' ? property.typeAnnotation.typeAnnotation : property.value; const eventemitterName = key.name; const resolveTypeAnnotationFN = parser.getResolveTypeAnnotationFN(); const _unwrapNullable7 = unwrapNullable(value), _unwrapNullable8 = _slicedToArray(_unwrapNullable7, 2), typeAnnotation = _unwrapNullable8[0], typeAnnotationNullable = _unwrapNullable8[1]; const typeAnnotationUntyped = value.typeParameters.params.length === 1 && parser.language() === 'TypeScript' ? value.typeParameters.params[0].type === 'TSTypeLiteral' && value.typeParameters.params[0].members.length === 0 : value.typeParameters.params[0].type === 'ObjectTypeAnnotation' && value.typeParameters.params[0].properties.length === 0; throwIfEventEmitterTypeIsUnsupported( hasteModuleName, key.name, typeAnnotation.type, parser, typeAnnotationNullable, typeAnnotationUntyped, ); const eventTypeResolutionStatus = resolveTypeAnnotationFN( typeAnnotation.typeParameters.params[0], types, parser, ); throwIfEventEmitterEventTypeIsUnsupported( hasteModuleName, key.name, eventTypeResolutionStatus.typeAnnotation, parser, eventTypeResolutionStatus.nullable, ); const eventTypeAnnotation = translateTypeAnnotation( hasteModuleName, typeAnnotation.typeParameters.params[0], types, aliasMap, enumMap, tryParse, cxxOnly, parser, ); return { name: eventemitterName, optional: false, typeAnnotation: { type: 'EventEmitterTypeAnnotation', typeAnnotation: eventTypeAnnotation, }, }; } function buildSchemaFromConfigType( configType, filename, ast, wrapComponentSchema, buildComponentSchema, buildModuleSchema, parser, translateTypeAnnotation, ) { switch (configType) { case 'component': { return wrapComponentSchema(buildComponentSchema(ast, parser)); } case 'module': { if (filename === undefined || filename === null) { throw new Error('Filepath expected while parasing a module'); } const nativeModuleName = extractNativeModuleName(filename); const _createParserErrorCap = createParserErrorCapturer(), _createParserErrorCap2 = _slicedToArray(_createParserErrorCap, 2), parsingErrors = _createParserErrorCap2[0], tryParse = _createParserErrorCap2[1]; const schema = tryParse(() => buildModuleSchema( nativeModuleName, ast, tryParse, parser, translateTypeAnnotation, ), ); if (parsingErrors.length > 0) { /** * TODO(T77968131): We have two options: * - Throw the first error, but indicate there are more then one errors. * - Display all errors, nicely formatted. * * For the time being, we're just throw the first error. **/ throw parsingErrors[0]; } invariant( schema != null, 'When there are no parsing errors, the schema should not be null', ); return wrapModuleSchema(schema, nativeModuleName); } default: return { modules: {}, }; } } function buildSchema( contents, filename, wrapComponentSchema, buildComponentSchema, buildModuleSchema, Visitor, parser, translateTypeAnnotation, ) { // Early return for non-Spec JavaScript files if ( !contents.includes('codegenNativeComponent') && !contents.includes('TurboModule') ) { return { modules: {}, }; } const ast = parser.getAst(contents, filename); const configType = getConfigType(ast, Visitor); return buildSchemaFromConfigType( configType, filename, ast, wrapComponentSchema, buildComponentSchema, buildModuleSchema, parser, translateTypeAnnotation, ); } function createComponentConfig(foundConfig, commandsTypeNames) { return _objectSpread( _objectSpread({}, foundConfig), {}, { commandTypeName: commandsTypeNames[0] == null ? null : commandsTypeNames[0].commandTypeName, commandOptionsExpression: commandsTypeNames[0] == null ? null : commandsTypeNames[0].commandOptionsExpression, }, ); } const parseModuleName = (hasteModuleName, moduleSpec, ast, parser) => { const callExpressions = []; visit(ast, { CallExpression(node) { if (isModuleRegistryCall(node)) { callExpressions.push(node); } }, }); throwIfUnusedModuleInterfaceParserError( hasteModuleName, moduleSpec, callExpressions, ); throwIfMoreThanOneModuleRegistryCalls( hasteModuleName, callExpressions, callExpressions.length, ); const callExpression = callExpressions[0]; const typeParameters = parser.callExpressionTypeParameters(callExpression); const methodName = callExpression.callee.property.name; throwIfWrongNumberOfCallExpressionArgs( hasteModuleName, callExpression, methodName, callExpression.arguments.length, ); throwIfIncorrectModuleRegistryCallArgument( hasteModuleName, callExpression.arguments[0], methodName, ); const $moduleName = callExpression.arguments[0].value; throwIfUntypedModule( typeParameters, hasteModuleName, callExpression, methodName, $moduleName, ); throwIfIncorrectModuleRegistryCallTypeParameterParserError( hasteModuleName, typeParameters, methodName, $moduleName, parser, ); return $moduleName; }; const buildModuleSchema = ( hasteModuleName, ast, tryParse, parser, translateTypeAnnotation, ) => { const language = parser.language(); const types = parser.getTypes(ast); const moduleSpecs = Object.values(types).filter(t => parser.isModuleInterface(t), ); throwIfModuleInterfaceNotFound( moduleSpecs.length, hasteModuleName, ast, language, ); throwIfMoreThanOneModuleInterfaceParserError( hasteModuleName, moduleSpecs, language, ); const _moduleSpecs = _slicedToArray(moduleSpecs, 1), moduleSpec = _moduleSpecs[0]; throwIfModuleInterfaceIsMisnamed(hasteModuleName, moduleSpec.id, language); // Parse Module Name const moduleName = parseModuleName(hasteModuleName, moduleSpec, ast, parser); // Some module names use platform suffix to indicate platform-exclusive modules. // Eventually this should be made explicit in the Flow type itself. // Also check the hasteModuleName for platform suffix. // Note: this shape is consistent with ComponentSchema. const _verifyPlatforms = verifyPlatforms(hasteModuleName, moduleName), cxxOnly = _verifyPlatforms.cxxOnly, excludedPlatforms = _verifyPlatforms.excludedPlatforms; const aliasMap = cxxOnly ? getObjectTypeAnnotations( hasteModuleName, types, tryParse, translateTypeAnnotation, parser, ) : {}; const properties = language === 'Flow' ? moduleSpec.body.properties : moduleSpec.body.body; // $FlowFixMe[missing-type-arg] const nativeModuleSchema = properties .filter( property => property.type === 'ObjectTypeProperty' || property.type === 'TSPropertySignature' || property.type === 'TSMethodSignature', ) .map(property => { var _property$typeAnnotat, _property$value, _property$value2; const enumMap = {}; const isEventEmitter = language === 'TypeScript' ? (property === null || property === void 0 ? void 0 : property.type) === 'TSPropertySignature' && (property === null || property === void 0 || (_property$typeAnnotat = property.typeAnnotation) === null || _property$typeAnnotat === void 0 || (_property$typeAnnotat = _property$typeAnnotat.typeAnnotation) === null || _property$typeAnnotat === void 0 || (_property$typeAnnotat = _property$typeAnnotat.typeName) === null || _property$typeAnnotat === void 0 ? void 0 : _property$typeAnnotat.name) === 'EventEmitter' : (property === null || property === void 0 || (_property$value = property.value) === null || _property$value === void 0 ? void 0 : _property$value.type) === 'GenericTypeAnnotation' && (property === null || property === void 0 || (_property$value2 = property.value) === null || _property$value2 === void 0 || (_property$value2 = _property$value2.id) === null || _property$value2 === void 0 ? void 0 : _property$value2.name) === 'EventEmitter'; return tryParse(() => ({ aliasMap, enumMap, propertyShape: isEventEmitter ? { type: 'eventEmitter', value: buildEventEmitterSchema( hasteModuleName, property, types, aliasMap, enumMap, tryParse, cxxOnly, translateTypeAnnotation, parser, ), } : { type: 'method', value: buildPropertySchema( hasteModuleName, property, types, aliasMap, enumMap, tryParse, cxxOnly, translateTypeAnnotation, parser, ), }, })); }) .filter(Boolean) .reduce( (moduleSchema, {enumMap, propertyShape}) => ({ type: 'NativeModule', aliasMap: _objectSpread( _objectSpread({}, moduleSchema.aliasMap), aliasMap, ), enumMap: _objectSpread( _objectSpread({}, moduleSchema.enumMap), enumMap, ), spec: { eventEmitters: [...moduleSchema.spec.eventEmitters].concat( propertyShape.type === 'eventEmitter' ? [propertyShape.value] : [], ), methods: [...moduleSchema.spec.methods].concat( propertyShape.type === 'method' ? [propertyShape.value] : [], ), }, moduleName: moduleSchema.moduleName, excludedPlatforms: moduleSchema.excludedPlatforms, }), { type: 'NativeModule', aliasMap: {}, enumMap: {}, spec: { eventEmitters: [], methods: [], }, moduleName, excludedPlatforms: excludedPlatforms.length !== 0 ? [...excludedPlatforms] : undefined, }, ); return { type: 'NativeModule', aliasMap: getSortedObject(nativeModuleSchema.aliasMap), enumMap: getSortedObject(nativeModuleSchema.enumMap), spec: { eventEmitters: nativeModuleSchema.spec.eventEmitters.sort(), methods: nativeModuleSchema.spec.methods.sort(), }, moduleName, excludedPlatforms: nativeModuleSchema.excludedPlatforms, }; }; /** * This function is used to find the type of a native component * provided the default exports statement from generated AST. * @param statement The statement to be parsed. * @param foundConfigs The 'mutable' array of configs that have been found. * @param parser The language parser to be used. * @returns void */ function findNativeComponentType(statement, foundConfigs, parser) { let declaration = statement.declaration; // codegenNativeComponent can be nested inside a cast // expression so we need to go one level deeper if ( declaration.type === 'TSAsExpression' || declaration.type === 'AsExpression' || declaration.type === 'TypeCastExpression' ) { declaration = declaration.expression; } try { if (declaration.callee.name === 'codegenNativeComponent') { const typeArgumentParams = parser.getTypeArgumentParamsFromDeclaration(declaration); const funcArgumentParams = declaration.arguments; const nativeComponentType = parser.getNativeComponentType( typeArgumentParams, funcArgumentParams, ); if (funcArgumentParams.length > 1) { nativeComponentType.optionsExpression = funcArgumentParams[1]; } foundConfigs.push(nativeComponentType); } } catch (e) { // ignore } } function getCommandOptions(commandOptionsExpression) { if (commandOptionsExpression == null) { return null; } let foundOptions; try { foundOptions = commandOptionsExpression.properties.reduce( (options, prop) => { options[prop.key.name] = ( (prop && prop.value && prop.value.elements) || [] ).map(element => element && element.value); return options; }, {}, ); } catch (e) { throw new Error( 'Failed to parse command options, please check that they are defined correctly', ); } return foundOptions; } function getOptions(optionsExpression) { if (!optionsExpression) { return null; } let foundOptions; try { foundOptions = optionsExpression.properties.reduce((options, prop) => { if (prop.value.type === 'ArrayExpression') { options[prop.key.name] = prop.value.elements.map( element => element.value, ); } else { options[prop.key.name] = prop.value.value; } return options; }, {}); } catch (e) { throw new Error( 'Failed to parse codegen options, please check that they are defined correctly', ); } if ( foundOptions.paperComponentName && foundOptions.paperComponentNameDeprecated ) { throw new Error( 'Failed to parse codegen options, cannot use both paperComponentName and paperComponentNameDeprecated', ); } return foundOptions; } function getCommandTypeNameAndOptionsExpression(namedExport, parser) { let callExpression; let calleeName; try { callExpression = namedExport.declaration.declarations[0].init; calleeName = callExpression.callee.name; } catch (e) { return; } if (calleeName !== 'codegenNativeCommands') { return; } if (callExpression.arguments.length !== 1) { throw new Error( 'codegenNativeCommands must be passed options including the supported commands', ); } const typeArgumentParam = parser.getTypeArgumentParamsFromDeclaration(callExpression)[0]; if (!parser.isGenericTypeAnnotation(typeArgumentParam.type)) { throw new Error( "codegenNativeCommands doesn't support inline definitions. Specify a file local type alias", ); } return { commandTypeName: parser.getTypeAnnotationName(typeArgumentParam), commandOptionsExpression: callExpression.arguments[0], }; } function propertyNames(properties) { return properties .map(property => property && property.key && property.key.name) .filter(Boolean); } function extendsForProp(prop, types, parser) { const argument = parser.argumentForProp(prop); if (!argument) { console.log('null', prop); } const name = parser.nameForArgument(prop); if (types[name] != null) { // This type is locally defined in the file return null; } switch (name) { case 'ViewProps': return { type: 'ReactNativeBuiltInType', knownTypeName: 'ReactNativeCoreViewProps', }; default: { throw new Error(`Unable to handle prop spread: ${name}`); } } } function buildPropSchema(property, types, parser) { const getSchemaInfoFN = parser.getGetSchemaInfoFN(); const info = getSchemaInfoFN(property, types); if (info == null) { return null; } const name = info.name, optional = info.optional, typeAnnotation = info.typeAnnotation, defaultValue = info.defaultValue, withNullDefault = info.withNullDefault; const getTypeAnnotationFN = parser.getGetTypeAnnotationFN(); return { name, optional, typeAnnotation: getTypeAnnotationFN( name, typeAnnotation, defaultValue, withNullDefault, types, parser, buildPropSchema, ), }; } /* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's * LTI update could not be added via codemod */ function getEventArgument(argumentProps, parser, getPropertyType) { return { type: 'ObjectTypeAnnotation', properties: argumentProps.map(member => buildPropertiesForEvent(member, parser, getPropertyType), ), }; } /* $FlowFixMe[signature-verification-failure] there's no flowtype for AST. * TODO(T108222691): Use flow-types for @babel/parser */ function findComponentConfig(ast, parser) { const foundConfigs = []; const defaultExports = ast.body.filter( node => node.type === 'ExportDefaultDeclaration', ); defaultExports.forEach(statement => { findNativeComponentType(statement, foundConfigs, parser); }); throwIfConfigNotfound(foundConfigs); throwIfMoreThanOneConfig(foundConfigs); const foundConfig = foundConfigs[0]; const namedExports = ast.body.filter( node => node.type === 'ExportNamedDeclaration', ); const commandsTypeNames = namedExports .map(statement => getCommandTypeNameAndOptionsExpression(statement, parser)) .filter(Boolean); throwIfMoreThanOneCodegenNativecommands(commandsTypeNames); return createComponentConfig(foundConfig, commandsTypeNames); } // $FlowFixMe[signature-verification-failure] there's no flowtype for AST function getCommandProperties(ast, parser) { const _findComponentConfig = findComponentConfig(ast, parser), commandTypeName = _findComponentConfig.commandTypeName, commandOptionsExpression = _findComponentConfig.commandOptionsExpression; if (commandTypeName == null) { return []; } const types = parser.getTypes(ast); const typeAlias = types[commandTypeName]; throwIfTypeAliasIsNotInterface(typeAlias, parser); const properties = parser.bodyProperties(typeAlias); if (!properties) { throw new Error( `Failed to find type definition for "${commandTypeName}", please check that you have a valid codegen file`, ); } const commandPropertyNames = propertyNames(properties); const commandOptions = getCommandOptions(commandOptionsExpression); if (commandOptions == null || commandOptions.supportedCommands == null) { throw new Error( 'codegenNativeCommands must be given an options object with supportedCommands array', ); } if ( commandOptions.supportedCommands.length !== commandPropertyNames.length || !commandOptions.supportedCommands.every(supportedCommand => commandPropertyNames.includes(supportedCommand), ) ) { throw new Error( `codegenNativeCommands expected the same supportedCommands specified in the ${commandTypeName} interface: ${commandPropertyNames.join( ', ', )}`, ); } return properties; } function getTypeResolutionStatus(type, typeAnnotation, parser) { return { successful: true, type, name: parser.getTypeAnnotationName(typeAnnotation), }; } function handleGenericTypeAnnotation( typeAnnotation, resolvedTypeAnnotation, parser, ) { 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, }; } function buildPropertiesForEvent(property, parser, getPropertyType) { const name = property.key.name; const optional = parser.isOptionalProperty(property); const typeAnnotation = parser.getTypeAnnotationFromProperty(property); return getPropertyType(name, optional, typeAnnotation, parser); } function verifyPropNotAlreadyDefined(props, needleProp) { const propName = needleProp.key.name; const foundProp = props.some(prop => prop.key.name === propName); if (foundProp) { throw new Error(`A prop was already defined with the name ${propName}`); } } function handleEventHandler( name, typeAnnotation, parser, types, findEventArgumentsAndType, ) { const eventType = name === 'BubblingEventHandler' ? 'bubble' : 'direct'; const paperTopLevelNameDeprecated = parser.getPaperTopLevelNameDeprecated(typeAnnotation); switch (typeAnnotation.typeParameters.params[0].type) { case parser.nullLiteralTypeAnnotation: case parser.undefinedLiteralTypeAnnotation: return { argumentProps: [], bubblingType: eventType, paperTopLevelNameDeprecated, }; default: return findEventArgumentsAndType( parser, typeAnnotation.typeParameters.params[0], types, eventType, paperTopLevelNameDeprecated, ); } } function emitBuildEventSchema( paperTopLevelNameDeprecated, name, optional, nonNullableBubblingType, argument, ) { if (paperTopLevelNameDeprecated != null) { return { name, optional, bubblingType: nonNullableBubblingType, paperTopLevelNameDeprecated, typeAnnotation: { type: 'EventTypeAnnotation', argument: argument, }, }; } return { name, optional, bubblingType: nonNullableBubblingType, typeAnnotation: { type: 'EventTypeAnnotation', argument: argument, }, }; } module.exports = { wrapModuleSchema, unwrapNullable, wrapNullable, assertGenericTypeAnnotationHasExactlyOneTypeParameter, isObjectProperty, parseObjectProperty, translateFunctionTypeAnnotation, buildPropertySchema, buildSchemaFromConfigType, buildSchema, createComponentConfig, parseModuleName, buildModuleSchema, findNativeComponentType, propertyNames, getCommandOptions, getOptions, getCommandTypeNameAndOptionsExpression, extendsForProp, buildPropSchema, getEventArgument, findComponentConfig, getCommandProperties, handleGenericTypeAnnotation, getTypeResolutionStatus, buildPropertiesForEvent, verifyPropNotAlreadyDefined, handleEventHandler, emitBuildEventSchema, };