diff --git a/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js b/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js index db591ef698d8bb..54217fc11bd989 100644 --- a/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js +++ b/packages/react-native-codegen/src/parsers/__tests__/parsers-commons-test.js @@ -21,6 +21,8 @@ import { buildSchema, parseModuleName, createComponentConfig, + getCommandOptions, + getOptions, } from '../parsers-commons'; import type {ParserType} from '../errors'; @@ -1277,3 +1279,133 @@ describe('createComponentConfig', () => { }); }); }); + +describe('getCommandOptions', () => { + it('returns null when commandOptionsExpression is null', () => { + const result = getCommandOptions(null); + expect(result).toBeNull(); + }); + + it('parses and returns command options correctly', () => { + const commandOptionsExpression = { + properties: [ + { + range: [], + loc: {}, + type: '', + key: { + name: 'hotspotUpdate', + loc: {}, + }, + value: { + elements: [ + { + value: 'value', + }, + ], + }, + }, + ], + }; + const result = getCommandOptions(commandOptionsExpression); + expect(result).toEqual({ + hotspotUpdate: ['value'], + }); + }); + + it('should throw an error if command options are not defined correctly', () => { + const commandOptionsExpression = { + properties: null, + }; + expect(() => getCommandOptions(commandOptionsExpression)).toThrowError( + 'Failed to parse command options, please check that they are defined correctly', + ); + }); +}); + +describe('getOptions', () => { + it('returns null if optionsExpression is falsy', () => { + expect(getOptions(null)).toBeNull(); + expect(getOptions(undefined)).toBeNull(); + expect(getOptions(false)).toBeNull(); + expect(getOptions(0)).toBeNull(); + expect(getOptions('')).toBeNull(); + }); + + it('parses and returns options correctly if codegen options are defined correctly', () => { + const optionsExpression = { + properties: [ + { + value: { + type: 'ArrayExpression', + value: 'value', + elements: [ + { + value: 'value1', + }, + ], + }, + key: { + name: 'keyName', + }, + }, + ], + }; + expect(getOptions(optionsExpression)).toEqual({ + keyName: ['value1'], + }); + }); + + it('throws an error if codegen options are not defined correctly', () => { + const optionsExpression = { + properties: null, + }; + expect(() => getOptions(optionsExpression)).toThrowError( + 'Failed to parse codegen options, please check that they are defined correctly', + ); + }); + + it('throws an error if both paperComponentName and paperComponentNameDeprecated are used', () => { + const optionsExpression = { + properties: [ + { + key: {name: 'paperComponentName'}, + value: {value: 'RCTRefreshControl'}, + }, + { + key: {name: 'paperComponentNameDeprecated'}, + value: {value: 'RCTSwitch'}, + }, + ], + }; + expect(() => getOptions(optionsExpression)).toThrowError( + 'Failed to parse codegen options, cannot use both paperComponentName and paperComponentNameDeprecated', + ); + }); + + it('returns options if only paperComponentName is used', () => { + const optionsExpression = { + properties: [ + { + key: {name: 'paperComponentName'}, + value: {value: 'RCTRefreshControl'}, + }, + ], + }; + const expectedOptions = {paperComponentName: 'RCTRefreshControl'}; + expect(getOptions(optionsExpression)).toEqual(expectedOptions); + }); + + it('returns options if only paperComponentNameDeprecated is used', () => { + const optionsExpression = { + properties: [ + { + key: {name: 'paperComponentNameDeprecated'}, + value: {value: 'RCTRefreshControl'}, + }, + ], + }; + const expectedOptions = {paperComponentNameDeprecated: 'RCTRefreshControl'}; + expect(getOptions(optionsExpression)).toEqual(expectedOptions); + }); +}); diff --git a/packages/react-native-codegen/src/parsers/flow/components/index.js b/packages/react-native-codegen/src/parsers/flow/components/index.js index 864915816c02b7..43aee56804580e 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/index.js +++ b/packages/react-native-codegen/src/parsers/flow/components/index.js @@ -11,19 +11,20 @@ 'use strict'; import type {Parser} from '../../parser'; import type {TypeDeclarationMap} from '../../utils'; -import type {CommandOptions} from './options'; +import type {CommandOptions} from '../../parsers-commons'; import type {ComponentSchemaBuilderConfig} from '../../schema.js'; const {getCommands} = require('./commands'); const {getEvents} = require('./events'); const {getExtendsProps, removeKnownExtends} = require('./extends'); -const {getCommandOptions, getOptions} = require('./options'); const {getProps} = require('./props'); const {getProperties} = require('./componentsUtils.js'); const {throwIfMoreThanOneCodegenNativecommands} = require('../../error-utils'); const { createComponentConfig, findNativeComponentType, + getCommandOptions, + getOptions, } = require('../../parsers-commons'); // $FlowFixMe[signature-verification-failure] there's no flowtype for AST diff --git a/packages/react-native-codegen/src/parsers/flow/components/options.js b/packages/react-native-codegen/src/parsers/flow/components/options.js deleted file mode 100644 index a83ba528b467d8..00000000000000 --- a/packages/react-native-codegen/src/parsers/flow/components/options.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - * 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. - * - * @flow strict - * @format - */ - -'use strict'; - -import type {OptionsShape} from '../../../CodegenSchema.js'; - -// $FlowFixMe[unclear-type] there's no flowtype for ASTs -type OptionsAST = Object; - -export type CommandOptions = $ReadOnly<{ - supportedCommands: $ReadOnlyArray, -}>; - -function getCommandOptions( - commandOptionsExpression: OptionsAST, -): ?CommandOptions { - 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: OptionsAST): ?OptionsShape { - 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; -} - -module.exports = { - getCommandOptions, - getOptions, -}; diff --git a/packages/react-native-codegen/src/parsers/parsers-commons.js b/packages/react-native-codegen/src/parsers/parsers-commons.js index e0348838eb6f10..1a8d3025503ea5 100644 --- a/packages/react-native-codegen/src/parsers/parsers-commons.js +++ b/packages/react-native-codegen/src/parsers/parsers-commons.js @@ -22,6 +22,7 @@ import type { NativeModulePropertyShape, SchemaType, NativeModuleEnumMap, + OptionsShape, } from '../CodegenSchema.js'; import type {Parser} from './parser'; @@ -61,6 +62,13 @@ const { const invariant = require('invariant'); +export type CommandOptions = $ReadOnly<{ + supportedCommands: $ReadOnlyArray, +}>; + +// $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser +type OptionsAST = Object; + function wrapModuleSchema( nativeModuleSchema: NativeModuleSchema, hasteModuleName: string, @@ -699,6 +707,68 @@ function findNativeComponentType( } } +function getCommandOptions( + commandOptionsExpression: OptionsAST, +): ?CommandOptions { + 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: OptionsAST): ?OptionsShape { + 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; +} + module.exports = { wrapModuleSchema, unwrapNullable, @@ -714,4 +784,6 @@ module.exports = { parseModuleName, buildModuleSchema, findNativeComponentType, + getCommandOptions, + getOptions, }; diff --git a/packages/react-native-codegen/src/parsers/typescript/components/index.js b/packages/react-native-codegen/src/parsers/typescript/components/index.js index f1ce04d0f338f8..4c41d7f1fdedbe 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/index.js @@ -12,19 +12,20 @@ import type {ExtendsPropsShape} from '../../../CodegenSchema.js'; import type {Parser} from '../../parser'; import type {TypeDeclarationMap} from '../../utils'; -import type {CommandOptions} from './options'; +import type {CommandOptions} from '../../parsers-commons'; import type {ComponentSchemaBuilderConfig} from '../../schema.js'; const {getCommands} = require('./commands'); const {getEvents} = require('./events'); const {categorizeProps} = require('./extends'); -const {getCommandOptions, getOptions} = require('./options'); const {getProps} = require('./props'); const {getProperties} = require('./componentsUtils.js'); const {throwIfMoreThanOneCodegenNativecommands} = require('../../error-utils'); const { createComponentConfig, findNativeComponentType, + getCommandOptions, + getOptions, } = require('../../parsers-commons'); // $FlowFixMe[signature-verification-failure] TODO(T108222691): Use flow-types for @babel/parser diff --git a/packages/react-native-codegen/src/parsers/typescript/components/options.js b/packages/react-native-codegen/src/parsers/typescript/components/options.js deleted file mode 100644 index 96ea8abc75b1d5..00000000000000 --- a/packages/react-native-codegen/src/parsers/typescript/components/options.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - * 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. - * - * @flow strict - * @format - */ - -'use strict'; - -import type {OptionsShape} from '../../../CodegenSchema.js'; - -// $FlowFixMe[unclear-type] TODO(T108222691): Use flow-types for @babel/parser -type OptionsAST = Object; - -export type CommandOptions = $ReadOnly<{ - supportedCommands: $ReadOnlyArray, -}>; - -function getCommandOptions( - commandOptionsExpression: OptionsAST, -): ?CommandOptions { - 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: OptionsAST): ?OptionsShape { - 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; -} - -module.exports = { - getCommandOptions, - getOptions, -};